diff --git a/BUILD.gn b/BUILD.gn
index 1cad2ba..b4d3ae9d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -95,19 +95,15 @@
       "//breakpad:minidump_stackwalk($host_toolchain)",
       "//chrome",
       "//chrome/test/chromedriver",
-
-      # TODO(crbug.com/607362)
-      # "//content/test:video_decode_accelerator_unittest",
-      # "//content/test:video_encode_accelerator_unittest",
-
+      "//content/test:video_decode_accelerator_unittest",
+      "//content/test:video_encode_accelerator_unittest",
       "//media:media_unittests",
       "//ppapi/examples/video_decode",
       "//sandbox/linux:chrome_sandbox",
       "//sandbox/linux:sandbox_linux_unittests",
 
-      # TODO(crbug.com/607362)
+      # Blocked on https://github.com/catapult-project/catapult/issues/2297
       #"//third_party/catapult/telemetry:bitmaptools",
-
       "//tools/perf/clear_system_cache",
     ]
   }
@@ -633,39 +629,6 @@
   }
 }
 
-# This group contains all the targets needed to run mojo's apptest_runner.
-group("mojo_apptests") {
-  testonly = true
-
-  deps = []
-  data_deps = []
-  data = []
-
-  if (is_win || is_linux) {
-    deps += [ "//services/shell/standalone" ]
-    data_deps = [
-      "//media/mojo/services:media_apptests",
-    ]
-    if (is_linux) {
-      data_deps += [ "//tools/xdisplaycheck" ]
-    }
-    data += [
-      "//mojo/tools/apptest_runner.py",
-      "//mojo/tools/data/apptests",
-      "//mojo/tools/mopy/",
-      "//testing/xvfb.py",
-      "//tools/swarming_client/utils/subprocess42.py",
-    ]
-  }
-}
-
-group("mojo_apptests_run") {
-  testonly = true
-  deps = [
-    ":mojo_apptests",
-  ]
-}
-
 group("gn_only") {
   testonly = true
 
@@ -685,11 +648,13 @@
 
   if (is_win || is_linux) {
     deps += [
-      ":mojo_apptests",
       "//components/mus/demo",
       "//components/mus/ws:tests",
+      "//components/resource_provider:resource_provider_unittests",
       "//mash:all",
+      "//media/mojo/services:media_mojo_shell_unittests",
       "//mojo",
+      "//ui/views/mus:views_mus_unittests",
     ]
   }
 
diff --git a/DEPS b/DEPS
index 360705e9..55d0b4e 100644
--- a/DEPS
+++ b/DEPS
@@ -39,11 +39,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '219ac2bc091d8f7b0fbf586ada24af9a55e9ca45',
+  'skia_revision': 'cf942c4ef750712b624867cbb2217c14857db3c6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'f79101b0f22458cb42f2188db987004143b2fd0e',
+  'v8_revision': '23856ea4b7d08657788c72a46483d1ccbd7a6306',
   # 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.
@@ -83,7 +83,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': 'eec7648b9878417ea4f9f9628cf3340bd584bb5d',
+  'nacl_revision': '38d45c78d6e76f557c841627f9edc521a8b3a2ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling dEQP
   # and whatever else without interference from each other.
@@ -96,11 +96,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ec3f320c2852476e0acacafb5311c8ae0b068a53',
+  'catapult_revision': '623cc4a59608f3370e0eaab6070265574cf01848',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
-  'libfuzzer_revision': '84041ac212d33703dc5c48d918dc7a9212b14bd3', # from svn revision 266838
+  'libfuzzer_revision': 'badcec68b8d90f0e6f1296a04c023cf484b98488', # from svn revision 268088
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -186,7 +186,7 @@
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
 
   'src/third_party/libvpx/source/libvpx':
-   Var('chromium_git') + '/webm/libvpx.git' + '@' +  '83f17eeede83f138f34177121019798719d5be1c',
+   Var('chromium_git') + '/webm/libvpx.git' + '@' +  'b2ccb9c189069d45d201c988184e9e0796b96270',
 
   'src/third_party/ffmpeg':
    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '40ac6f0443064be7cd085baf538e70fe2a9afc19',
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 a524adaf..68f77eba 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -83,6 +83,7 @@
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.WeakHashMap;
 import java.util.concurrent.Callable;
 
 /**
@@ -963,16 +964,26 @@
             return mWindowAndroid;
         }
     }
+    private static WeakHashMap<Context, WindowAndroidWrapper> sContextWindowMap;
 
-    private static WindowAndroidWrapper createWindowAndroid(Context context) {
-        // TODO(boliu): WebView does not currently initialize ApplicationStatus, crbug.com/470582.
+    // getWindowAndroid is only called on UI thread, so there are no threading issues with lazy
+    // initialization.
+    @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
+    private static WindowAndroidWrapper getWindowAndroid(Context context) {
+        if (sContextWindowMap == null) sContextWindowMap = new WeakHashMap<>();
+        WindowAndroidWrapper wrapper = sContextWindowMap.get(context);
+        if (wrapper != null) return wrapper;
+
         boolean contextWrapsActivity = activityFromContext(context) != null;
-        if (!contextWrapsActivity) {
-            return new WindowAndroidWrapper(new WindowAndroid(context));
+        if (contextWrapsActivity) {
+            wrapper = new WindowAndroidWrapper(new WindowAndroid(context));
+        } else {
+            final boolean listenToActivityState = false;
+            wrapper = new WindowAndroidWrapper(
+                    new ActivityWindowAndroid(context, listenToActivityState));
         }
-
-        final boolean listenToActivityState = false;
-        return new WindowAndroidWrapper(new ActivityWindowAndroid(context, listenToActivityState));
+        sContextWindowMap.put(context, wrapper);
+        return wrapper;
     }
 
     @VisibleForTesting
@@ -1006,7 +1017,7 @@
 
         WebContents webContents = nativeGetWebContents(mNativeAwContents);
 
-        mWindowAndroid = createWindowAndroid(mContext);
+        mWindowAndroid = getWindowAndroid(mContext);
         mContentViewCore = createAndInitializeContentViewCore(mContainerView, mContext,
                 mInternalAccessAdapter, webContents, new AwGestureStateListener(),
                 mContentViewClient, mZoomControls, mWindowAndroid.getWindowAndroid());
diff --git a/ash/utility/screenshot_controller.cc b/ash/utility/screenshot_controller.cc
index cd1192f..6cfad385 100644
--- a/ash/utility/screenshot_controller.cc
+++ b/ash/utility/screenshot_controller.cc
@@ -16,10 +16,10 @@
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/compositor/paint_recorder.h"
+#include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_handler.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/screen.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/cursor_manager.h"
 
@@ -53,8 +53,8 @@
     gfx::Point location = event->location();
     position_client->ConvertPointToScreen(target, &location);
 
-    gfx::Display display =
-        gfx::Screen::GetScreen()->GetDisplayNearestPoint(location);
+    display::Display display =
+        display::Screen::GetScreen()->GetDisplayNearestPoint(location);
 
     aura::Window* root_window = Shell::GetInstance()
                                     ->window_tree_host_manager()
@@ -199,7 +199,7 @@
   screenshot_delegate_ = screenshot_delegate;
   mode_ = WINDOW;
 
-  gfx::Screen::GetScreen()->AddObserver(this);
+  display::Screen::GetScreen()->AddObserver(this);
   for (aura::Window* root : Shell::GetAllRootWindows()) {
     layers_[root] = new ScreenshotLayer(
         Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer());
@@ -222,7 +222,7 @@
 
   screenshot_delegate_ = screenshot_delegate;
   mode_ = PARTIAL;
-  gfx::Screen::GetScreen()->AddObserver(this);
+  display::Screen::GetScreen()->AddObserver(this);
   for (aura::Window* root : Shell::GetAllRootWindows()) {
     layers_[root] = new ScreenshotLayer(
         Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer());
@@ -283,7 +283,7 @@
   root_window_ = nullptr;
   SetSelectedWindow(nullptr);
   screenshot_delegate_ = nullptr;
-  gfx::Screen::GetScreen()->RemoveObserver(this);
+  display::Screen::GetScreen()->RemoveObserver(this);
   STLDeleteValues(&layers_);
   cursor_setter_.reset();
   EnableMouseWarp(true);
@@ -435,20 +435,22 @@
   event->StopPropagation();
 }
 
-void ScreenshotController::OnDisplayAdded(const gfx::Display& new_display) {
+void ScreenshotController::OnDisplayAdded(const display::Display& new_display) {
   if (!screenshot_delegate_)
     return;
   Cancel();
 }
 
-void ScreenshotController::OnDisplayRemoved(const gfx::Display& old_display) {
+void ScreenshotController::OnDisplayRemoved(
+    const display::Display& old_display) {
   if (!screenshot_delegate_)
     return;
   Cancel();
 }
 
-void ScreenshotController::OnDisplayMetricsChanged(const gfx::Display& display,
-                                                   uint32_t changed_metrics) {}
+void ScreenshotController::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {}
 
 void ScreenshotController::OnWindowDestroying(aura::Window* window) {
   SetSelectedWindow(nullptr);
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index b2ec91e40..96ccad2 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -237,6 +237,7 @@
       ]
 
       configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+      configs += [ "//build/config/gcc:symbol_visibility_default" ]
 
       ldflags = [
         # Don't let linker rip this symbol out, otherwise the heap&cpu
diff --git a/base/base.gyp b/base/base.gyp
index 0bfe5ae..f28923de 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -895,6 +895,8 @@
         'test/perf_time_logger.h',
         'test/power_monitor_test_base.cc',
         'test/power_monitor_test_base.h',
+        'test/scoped_command_line.cc',
+        'test/scoped_command_line.h',
         'test/scoped_locale.cc',
         'test/scoped_locale.h',
         'test/scoped_path_override.cc',
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 8d48f964..3953811f 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -61,6 +61,8 @@
     "perf_time_logger.h",
     "power_monitor_test_base.cc",
     "power_monitor_test_base.h",
+    "scoped_command_line.cc",
+    "scoped_command_line.h",
     "scoped_locale.cc",
     "scoped_locale.h",
     "scoped_path_override.cc",
diff --git a/base/test/scoped_command_line.cc b/base/test/scoped_command_line.cc
new file mode 100644
index 0000000..c74d243
--- /dev/null
+++ b/base/test/scoped_command_line.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_command_line.h"
+
+namespace base {
+namespace test {
+
+ScopedCommandLine::ScopedCommandLine()
+    : original_command_line_(*base::CommandLine::ForCurrentProcess()) {}
+
+ScopedCommandLine::~ScopedCommandLine() {
+  *base::CommandLine::ForCurrentProcess() = original_command_line_;
+}
+
+CommandLine* ScopedCommandLine::GetProcessCommandLine() {
+  return base::CommandLine::ForCurrentProcess();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/scoped_command_line.h b/base/test/scoped_command_line.h
new file mode 100644
index 0000000..dea0c6ac
--- /dev/null
+++ b/base/test/scoped_command_line.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_COMMAND_LINE_H_
+#define BASE_TEST_SCOPED_COMMAND_LINE_H_
+
+#include "base/command_line.h"
+
+namespace base {
+namespace test {
+
+// Helper class to restore the original command line at the end of the scope.
+// NOTE: In most unit tests, the command line is automatically restored per
+//       test, so this class is not necessary if the command line applies to
+//       the entire single test.
+class ScopedCommandLine final {
+ public:
+  ScopedCommandLine();
+  ~ScopedCommandLine();
+
+  // Gets the command line for the current process.
+  // NOTE: Do not name this GetCommandLine as this will conflict with Windows's
+  //       GetCommandLine and get renamed to GetCommandLineW.
+  CommandLine* GetProcessCommandLine();
+
+ private:
+  const CommandLine original_command_line_;
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_SCOPED_COMMAND_LINE_H_
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
index c3df28b..fd4e21a 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -114,6 +114,12 @@
 }
 
 void AllocationContextTracker::PopCurrentTaskContext(const char* context) {
+  // Guard for stack underflow. If tracing was started with a TRACE_EVENT in
+  // scope, the context was never pushed, so it is possible that pop is called
+  // on an empty stack.
+  if (task_contexts_.empty())
+    return;
+
   DCHECK_EQ(context, task_contexts_.back())
       << "Encountered an unmatched context end";
   task_contexts_.pop_back();
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 56fda80..902f9d4 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -170,8 +170,10 @@
     std::unique_ptr<MemoryAllocatorDump> mad) {
   auto insertion_result = allocator_dumps_.insert(
       std::make_pair(mad->absolute_name(), std::move(mad)));
-  DCHECK(insertion_result.second) << "Duplicate name: " << mad->absolute_name();
-  return insertion_result.first->second.get();
+  MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get();
+  DCHECK(insertion_result.second) << "Duplicate name: "
+                                  << inserted_mad->absolute_name();
+  return inserted_mad;
 }
 
 MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
diff --git a/blimp/tools/PRESUBMIT.py b/blimp/tools/PRESUBMIT.py
new file mode 100644
index 0000000..a298aa5
--- /dev/null
+++ b/blimp/tools/PRESUBMIT.py
@@ -0,0 +1,27 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit for blimp/tools."""
+
+
+def CommonChecks(input_api, output_api):
+  """Presubmit checks run on both upload and commit.
+
+  This is currently limited to pylint.
+  """
+  checks = []
+  checks.extend(input_api.canned_checks.GetPylint(
+      input_api, output_api, pylintrc='pylintrc'))
+  return input_api.RunTests(checks, False)
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  """Presubmit checks on CL upload."""
+  return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  """Presubmit checks on commit."""
+  return CommonChecks(input_api, output_api)
+
diff --git a/blimp/tools/bundle-engine.py b/blimp/tools/bundle-engine.py
index e94cf22b..4704ef6 100755
--- a/blimp/tools/bundle-engine.py
+++ b/blimp/tools/bundle-engine.py
@@ -11,7 +11,6 @@
 
 
 import argparse
-import errno
 import os
 import subprocess
 import sys
diff --git a/blimp/tools/generate-engine-manifest.py b/blimp/tools/generate-engine-manifest.py
index f8e70bb..327f6a8 100755
--- a/blimp/tools/generate-engine-manifest.py
+++ b/blimp/tools/generate-engine-manifest.py
@@ -8,12 +8,10 @@
 
 
 import argparse
-import errno
 import fnmatch
 import os
 import subprocess
 import sys
-import tarfile
 
 # Returns True if |entry| matches any of the patterns in |blacklist|.
 def IsBlacklisted(entry, blacklist):
@@ -45,14 +43,14 @@
 
   command_line = ' '.join([os.path.basename(sys.argv[0])] + sys.argv[1:])
   header = [
-    '# Runtime dependencies for the Blimp Engine',
-    '#',
-    '# This file was generated by running:',
-    '# ' + command_line + '',
-    '#',
-    '# Note: Any unnecessary dependencies should be added to',
-    '#       manifest-blacklist.txt and this file should be regenerated.',
-    '',
+      '# Runtime dependencies for the Blimp Engine',
+      '#',
+      '# This file was generated by running:',
+      '# ' + command_line + '',
+      '#',
+      '# Note: Any unnecessary dependencies should be added to',
+      '#       manifest-blacklist.txt and this file should be regenerated.',
+      '',
   ]
 
   blacklist_patterns = []
diff --git a/blimp/tools/pylintrc b/blimp/tools/pylintrc
new file mode 100644
index 0000000..b3ab9b775
--- /dev/null
+++ b/blimp/tools/pylintrc
@@ -0,0 +1,23 @@
+[BASIC]
+
+function-rgx=[A-Z_][a-zA-Z0-9_]{2,30}$
+method-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+good-names=e,f,i,j,k,main,_
+
+[FORMAT]
+
+indent-string='  '
+max-line-length=80
+
+[MESSAGES CONTROL]
+
+disable=invalid-name,missing-docstring,too-few-public-methods,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-statements,
+
+[REPORTS]
+
+reports=no
+
+[VARIABLES]
+
+dummy-variables-rgx=^_.*$|dummy
diff --git a/build/common.gypi b/build/common.gypi
index d7d25e0..641cc709f 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -1380,6 +1380,9 @@
     # Experimental setting to optimize Chrome's DLLs with PGO.
     'chrome_pgo_phase%': '0',
 
+    # Experimental setting to build the official builds with full WPO.
+    'full_wpo_on_official%': '0',
+
     # Whether the VS xtree header has been patched to disable warning 4702. If
     # it has, then we don't need to disable 4702 (unreachable code warning).
     # The patch is preapplied to the internal toolchain and hence all bots.
@@ -5506,6 +5509,12 @@
                     'Optimization': '1',
                     # 2, favorSize - Favor small code (/Os)
                     'FavorSizeOrSpeed': '2',
+                    'conditions': [
+                      ['full_wpo_on_official==1', {
+                        # This implies link time code generation.
+                        'WholeProgramOptimization': 'true',
+                      }],
+                    ],
                   },
                 },
               }],
@@ -5528,6 +5537,12 @@
                     'Optimization': '2',
                     # 1, favorSpeed - Favor fast code (/Ot)
                     'FavorSizeOrSpeed': '1',
+                    'conditions': [
+                      ['full_wpo_on_official==1', {
+                        # This implies link time code generation.
+                        'WholeProgramOptimization': 'true',
+                      }],
+                    ],
                   },
                 },
               }],
diff --git a/build/config/arm.gni b/build/config/arm.gni
index fefed65..2ea36fe 100644
--- a/build/config/arm.gni
+++ b/build/config/arm.gni
@@ -53,7 +53,7 @@
     }
 
     if (arm_float_abi == "") {
-      if (is_android) {
+      if (current_os == "android" || target_os == "android") {
         arm_float_abi = "softfp"
       } else {
         arm_float_abi = "hard"
@@ -74,7 +74,7 @@
     }
 
     if (arm_float_abi == "") {
-      if (is_android) {
+      if (current_os == "android" || target_os == "android") {
         arm_float_abi = "softfp"
       } else {
         arm_float_abi = "hard"
@@ -89,7 +89,7 @@
       arm_fpu = "vfpv3-d16"
     }
   }
-} else if (current_cpu == "arm64") {
+} else if (current_cpu == "arm64" || target_cpu == "arm64") {
   # arm64 supports only "hard".
   arm_float_abi = "hard"
 }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index ed5263ab..73d1942 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -48,7 +48,8 @@
   # TODO(GYP): We should be using 64-bit gold for linking on both 64-bit Linux
   # and 32-bit linux; 32-bit Gold runs out of address-space on 32-bit builds.
   # However, something isn't quite working right on the 32-bit builds.
-  use_gold = is_linux && (current_cpu == "x64" || current_cpu == "arm")
+  use_gold =
+      is_linux && (current_cpu == "x64" || current_cpu == "arm") && !use_lld
 
   # When we are going to use gold we need to find it.
   # This is initialized below, after use_gold might have been overridden.
@@ -165,6 +166,11 @@
     configs += [ "//build/config/mac:compiler" ]
   }
 
+  # Applies to all Posix systems.
+  if (is_posix) {
+    configs += [ "//build/config/posix:compiler" ]
+  }
+
   # See the definitions below.
   configs += [
     ":compiler_cpu_abi",
@@ -292,7 +298,9 @@
 
   # Linux-specific compiler flags setup.
   # ------------------------------------
-  if (use_gold) {
+  if (is_posix && use_lld) {
+    ldflags += [ "-fuse-ld=lld" ]
+  } else if (use_gold) {
     ldflags += [
       "-B$gold_path",
 
@@ -316,11 +324,6 @@
       ldflags += [ "-Wl,--gdb-index" ]
     }
 
-    if (!using_sanitizer && !(is_android && use_order_profiling)) {
-      # TODO(brettw) common.gypi has this only for target toolset.
-      ldflags += [ "-Wl,--icf=all" ]
-    }
-
     # TODO(thestig): Make this flag work with GN.
     #if (!is_official_build && !is_chromeos && !(is_asan || is_lsan || is_tsan || is_msan)) {
     #  ldflags += [
@@ -333,6 +336,11 @@
     ldflags += [ "-fuse-ld=bfd" ]
   }
 
+  if (is_posix && (use_gold || use_lld) && !using_sanitizer &&
+      !(is_android && use_order_profiling)) {
+    ldflags += [ "-Wl,--icf=all" ]
+  }
+
   if (linux_use_bundled_binutils) {
     cflags += [ "-B$binutils_path" ]
   }
@@ -413,7 +421,11 @@
 
     # Apply a lower LTO optimization level as the default is too slow.
     if (is_linux) {
-      ldflags += [ "-Wl,-plugin-opt,O1" ]
+      if (use_lld) {
+        ldflags += [ "-Wl,--lto-O1" ]
+      } else {
+        ldflags += [ "-Wl,-plugin-opt,O1" ]
+      }
     } else if (is_mac) {
       ldflags += [ "-Wl,-mllvm,-O1" ]
     }
@@ -1237,12 +1249,9 @@
 # Default "optimization on" config.
 config("optimize") {
   if (is_win) {
-    # TODO(sebmarchand): Enable this once we've decided to ship with full WPO
-    # and when the change to the incompatible projects (FFmpeg and Yasm) have
-    # have been committed.
-    #if (is_official_build) {
-    #  common_optimize_on_cflags += [ "/GL" ]
-    #}
+    if (is_official_build && full_wpo_on_official) {
+      common_optimize_on_cflags += [ "/GL" ]
+    }
 
     # Favor size over speed, /O1 must be before the common flags. The GYP
     # build also specifies /Os and /GF but these are implied by /O1.
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index b5ed037..11735ef2 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -19,6 +19,9 @@
   # 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.
   enable_profiling = false
+
+  # Whether or not the official builds should be build with full WPO.
+  full_wpo_on_official = false
 }
 
 # If it wasn't manually set, set to an appropriate default.
diff --git a/build/config/gcc/BUILD.gn b/build/config/gcc/BUILD.gn
index 2c1c61f..1741446 100644
--- a/build/config/gcc/BUILD.gn
+++ b/build/config/gcc/BUILD.gn
@@ -21,6 +21,13 @@
   cflags = [ "-fvisibility=hidden" ]
 }
 
+# This config is usually set when :symbol_visibility_hidden is removed.
+# It's often a good idea to set visibility explicitly, as there're flags
+# which would error out otherwise (e.g. -fsanitize=cfi-unrelated-cast)
+config("symbol_visibility_default") {
+  cflags = [ "-fvisibility=default" ]
+}
+
 # The rpath is the dynamic library search path. Setting this config on a link
 # step will put the directory where the build generates shared libraries into
 # the rpath.
diff --git a/build/config/mips.gni b/build/config/mips.gni
index 1b40657..cc6d53f 100644
--- a/build/config/mips.gni
+++ b/build/config/mips.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 (current_cpu == "mipsel") {
+if (current_cpu == "mipsel" || target_cpu == "mipsel") {
   declare_args() {
     # MIPS arch variant. Possible values are:
     #   "r1"
@@ -27,11 +27,11 @@
     #   "fpxx": sets the GCC -mfpxx option.
     mips_fpu_mode = "fp32"
   }
-} else if (current_cpu == "mips64el") {
+} else if (current_cpu == "mips64el" || target_cpu == "mips64el") {
   # MIPS arch variant. Possible values are:
   #   "r2"
   #   "r6"
-  if (is_android) {
+  if (current_os == "android" || target_os == "android") {
     declare_args() {
       mips_arch_variant = "r6"
     }
diff --git a/build/config/posix/BUILD.gn b/build/config/posix/BUILD.gn
index fc5dd43..c4198200 100644
--- a/build/config/posix/BUILD.gn
+++ b/build/config/posix/BUILD.gn
@@ -2,7 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/sanitizers/sanitizers.gni")
 import("//build/config/sysroot.gni")
+import("//build/toolchain/toolchain.gni")
 
 assert(is_posix)
 
@@ -10,6 +12,20 @@
   visibility = [ "//:optimize_gn_gen" ]
 }
 
+# This is included by reference in the //build/config/compiler config that
+# is applied to all Posix targets. It is here to separate out the logic that is
+# Posix-only. Note that this is in addition to an OS-specific variant of this
+# config.
+config("compiler") {
+  if ((allow_posix_link_time_opt || is_cfi) && !is_nacl) {
+    arflags = [
+      "--plugin",
+      rebase_path("//third_party/llvm-build/Release+Asserts/lib/LLVMgold.so",
+                  root_build_dir),
+    ]
+  }
+}
+
 # This is included by reference in the //build/config/compiler:runtime_library
 # config that is applied to all targets. It is here to separate out the logic
 # that is Posix-only. Please see that target for advice on what should go in
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index 8c357cb..d853950 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -325,6 +325,12 @@
   }
 }
 
+config("ubsan_no_recover") {
+  if (is_ubsan_no_recover) {
+    cflags = [ "-fno-sanitize-recover=undefined" ]
+  }
+}
+
 config("ubsan_security_flags") {
   if (is_ubsan_security) {
     ubsan_blacklist_path =
@@ -357,6 +363,7 @@
   ":msan_flags",
   ":tsan_flags",
   ":ubsan_flags",
+  ":ubsan_no_recover",
   ":ubsan_security_flags",
   ":ubsan_vptr_flags",
 ]
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index ba9c259..7d6a79f 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -19,6 +19,9 @@
   # undefined behaviour (excludes vptr checks).
   is_ubsan = false
 
+  # Halt the program if a problem is detected.
+  is_ubsan_no_recover = false
+
   # Compile for Undefined Behaviour Sanitizer's vptr checks.
   is_ubsan_vptr = false
 
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 2aab53d..8b40bd45 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -103,6 +103,18 @@
     assert(!is_win_fastlink, "/PROFILE and /DEBUG:FASTLINK are incompatible")
     ldflags = [ "/PROFILE" ]
   }
+
+  # arflags apply only to static_libraries. The normal linker configs are only
+  # set for executable and shared library targets so arflags must be set
+  # elsewhere. Since this is relatively contained, we just apply them in this
+  # more general config and they will only have an effect on static libraries.
+  arflags = [
+    # "No public symbols found; archive member will be inaccessible." This
+    # means that one or more object files in the library can never be
+    # pulled in to targets that link to this library. It's just a warning that
+    # the source file is a no-op.
+    "/ignore:4221",
+  ]
 }
 
 config("vs_code_analysis") {
diff --git a/build/secondary/third_party/nss/BUILD.gn b/build/secondary/third_party/nss/BUILD.gn
index 9a08058..2222840 100644
--- a/build/secondary/third_party/nss/BUILD.gn
+++ b/build/secondary/third_party/nss/BUILD.gn
@@ -886,6 +886,7 @@
     configs += [
       "//build/config/compiler:no_chromium_code",
       "//build/config/compiler:no_size_t_to_int_warning",
+      "//build/config/gcc:symbol_visibility_default",
 
       # nss passes "const char*" through "void*".
       "//build/config/compiler:no_incompatible_pointer_warnings",
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index 6ff2b586..2840ae0a 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -8,12 +8,14 @@
 import("//build/toolchain/goma.gni")
 import("//build/toolchain/toolchain.gni")
 
-# This value will be inherited in the toolchain below.
+# "concurrent_links" is a toolchain variable. By computing it here rather than
+# inside the toolchain, the exec_script will only get run once rather than
+# each time the toolchain template is invoked.
 if (allow_posix_link_time_opt || is_cfi) {
-  concurrent_links =
+  concurrent_links_ =
       exec_script("get_concurrent_links.py", [ "--lto" ], "value")
 } else {
-  concurrent_links = exec_script("get_concurrent_links.py", [], "value")
+  concurrent_links_ = exec_script("get_concurrent_links.py", [], "value")
 }
 
 # This template defines a toolchain for something that works like gcc
@@ -101,6 +103,8 @@
     assert(defined(invoker.toolchain_os),
            "gcc_toolchain() must specify a \"toolchain_os\"")
 
+    concurrent_links = concurrent_links_
+
     if (defined(invoker.cc_wrapper)) {
       cc_wrapper = invoker.cc_wrapper
     }
@@ -216,21 +220,13 @@
 
     tool("alink") {
       rspfile = "{{output}}.rsp"
-      arflags = ""
-      if ((allow_posix_link_time_opt || is_cfi) &&
-          invoker.toolchain_os != "nacl") {
-        gold_plugin_path = rebase_path(
-                "//third_party/llvm-build/Release+Asserts/lib/LLVMgold.so",
-                root_build_dir)
-        arflags = "--plugin \"$gold_plugin_path\""
-      }
 
       # This needs a Python script to avoid using simple sh features in this
       # command, in case the host does not use a POSIX shell (e.g. compiling
       # POSIX-like toolchains such as NaCl on Windows).
       ar_wrapper =
           rebase_path("//build/toolchain/gcc_ar_wrapper.py", root_build_dir)
-      command = "$python_path \"$ar_wrapper\" --output={{output}} --ar=\"$ar\" $arflags rcsD @\"$rspfile\""
+      command = "$python_path \"$ar_wrapper\" --output={{output}} --ar=\"$ar\" {{arflags}} rcsD @\"$rspfile\""
       description = "AR {{output}}"
       rspfile_content = "{{inputs}}"
       outputs = [
diff --git a/build/toolchain/mac/BUILD.gn b/build/toolchain/mac/BUILD.gn
index 2da92a7e..571865c 100644
--- a/build/toolchain/mac/BUILD.gn
+++ b/build/toolchain/mac/BUILD.gn
@@ -111,7 +111,7 @@
     }
 
     tool("alink") {
-      command = "rm -f {{output}} && ./gyp-mac-tool filter-libtool libtool -static -o {{output}} {{inputs}}"
+      command = "rm -f {{output}} && ./gyp-mac-tool filter-libtool libtool -static {{arflags}} -o {{output}} {{inputs}}"
       description = "LIBTOOL-STATIC {{output}}"
       outputs = [
         "{{output_dir}}/{{target_output_name}}{{output_extension}}",
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index 41daacc..dac6163 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -11,6 +11,12 @@
   #
   # TODO(pcc): Remove this flag if/when LTO is enabled in official builds.
   allow_posix_link_time_opt = false
+
+  # Set to true to use lld, the LLVM linker. This flag may be used on Windows
+  # with the shipped LLVM toolchain, or on Linux with a self-built top-of-tree
+  # LLVM toolchain (see llvm_force_head_revision in
+  # build/config/compiler/BUILD.gn).
+  use_lld = false
 }
 
 # Subdirectory within root_out_dir for shared library files.
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index e2458e2..beee0781 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -10,11 +10,6 @@
 # Should only be running on Windows.
 assert(is_win)
 
-declare_args() {
-  # Set to true to use lld, the LLVM linker.
-  use_lld = false
-}
-
 # Setup the Visual Studio state.
 #
 # Its arguments are the VS path and the compiler wrapper tool. It will write
@@ -145,7 +140,7 @@
 
     tool("alink") {
       rspfile = "{{output}}.rsp"
-      command = "$python_path gyp-win-tool link-wrapper $env False $lib /nologo /ignore:4221 /OUT:{{output}} @$rspfile"
+      command = "$python_path gyp-win-tool link-wrapper $env False $lib /nologo {{arflags}} /OUT:{{output}} @$rspfile"
       description = "LIB {{output}}"
       outputs = [
         # Ignore {{output_extension}} and always use .lib, there's no reason to
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index a766cc15..e1394096 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -147,11 +147,23 @@
     tool_source = source_file.readlines()
 
   # Add header and write it out to the current directory (which should be the
-  # root build dir).
-  with open("gyp-win-tool", 'w') as tool_file:
-    tool_file.write(''.join([tool_source[0],
-                             '# Generated by setup_toolchain.py do not edit.\n']
-                            + tool_source[1:]))
+  # root build dir). Don't write the file if a matching file already exists
+  # because that causes a cascade of unnecessary rebuilds.
+  match = False
+  contents = ''.join([tool_source[0],
+                      '# Generated by setup_toolchain.py do not edit.\n']
+                     + tool_source[1:])
+  out_path = 'gyp-win-tool'
+  try:
+    with open(out_path, 'rb') as read_tool_file:
+      existing_contents = read_tool_file.read()
+    if existing_contents == contents:
+      match = True
+  except:
+    pass
+  if not match:
+    with open(out_path, 'wb') as write_tool_file:
+      write_tool_file.write(contents)
 
 
 def main():
diff --git a/cc/blink/BUILD.gn b/cc/blink/BUILD.gn
index 8140a8d..629879f4 100644
--- a/cc/blink/BUILD.gn
+++ b/cc/blink/BUILD.gn
@@ -10,7 +10,6 @@
 
   sources = [
     "cc_blink_export.h",
-    "context_provider_web_context.h",
     "scrollbar_impl.cc",
     "scrollbar_impl.h",
     "web_compositor_support_impl.cc",
diff --git a/cc/blink/context_provider_web_context.h b/cc/blink/context_provider_web_context.h
deleted file mode 100644
index 21d1df3..0000000
--- a/cc/blink/context_provider_web_context.h
+++ /dev/null
@@ -1,24 +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 CC_BLINK_CONTEXT_PROVIDER_WEB_CONTEXT_H_
-#define CC_BLINK_CONTEXT_PROVIDER_WEB_CONTEXT_H_
-
-#include "cc/output/context_provider.h"
-
-namespace blink { class WebGraphicsContext3D; }
-
-namespace cc_blink {
-
-class ContextProviderWebContext : public cc::ContextProvider {
- public:
-  virtual blink::WebGraphicsContext3D* WebContext3D() = 0;
-
- protected:
-  ~ContextProviderWebContext() override {}
-};
-
-}  // namespace cc_blink
-
-#endif  // CC_BLINK_CONTEXT_PROVIDER_WEB_CONTEXT_H_
diff --git a/cc/blink/web_compositor_support_impl.h b/cc/blink/web_compositor_support_impl.h
index 9f12dc1..16449437 100644
--- a/cc/blink/web_compositor_support_impl.h
+++ b/cc/blink/web_compositor_support_impl.h
@@ -12,10 +12,6 @@
 #include "third_party/WebKit/public/platform/WebContentLayerClient.h"
 #include "third_party/WebKit/public/platform/WebLayer.h"
 
-namespace blink {
-class WebGraphicsContext3D;
-}
-
 namespace cc_blink {
 
 class CC_BLINK_EXPORT WebCompositorSupportImpl
diff --git a/cc/blink/web_external_texture_layer_impl.cc b/cc/blink/web_external_texture_layer_impl.cc
index 766040a..d119dd7 100644
--- a/cc/blink/web_external_texture_layer_impl.cc
+++ b/cc/blink/web_external_texture_layer_impl.cc
@@ -13,7 +13,6 @@
 #include "third_party/WebKit/public/platform/WebExternalTextureLayerClient.h"
 #include "third_party/WebKit/public/platform/WebExternalTextureMailbox.h"
 #include "third_party/WebKit/public/platform/WebFloatRect.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/khronos/GLES2/gl2.h"
 
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index bc4b75f..38bd1eb 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -90,12 +90,10 @@
     SkRTreeFactory factory;
     recorder_.reset(new SkPictureRecorder());
 
-    // TODO(fmalita): this is fragile, we should drop canvas_ and use
-    // recorder_->getCanvas() instead.
-    canvas_ = sk_ref_sp(recorder_->beginRecording(
-        layer_rect_.width(), layer_rect_.height(), &factory));
-    canvas_->translate(-layer_rect_.x(), -layer_rect_.y());
-    canvas_->clipRect(gfx::RectToSkRect(layer_rect_));
+    SkCanvas* canvas = recorder_->beginRecording(
+        layer_rect_.width(), layer_rect_.height(), &factory);
+    canvas->translate(-layer_rect_.x(), -layer_rect_.y());
+    canvas->clipRect(gfx::RectToSkRect(layer_rect_));
   }
 }
 
@@ -149,8 +147,8 @@
 
 void DisplayItemList::ProcessAppendedItem(const DisplayItem* item) {
   if (settings_.use_cached_picture) {
-    DCHECK(canvas_);
-    item->Raster(canvas_.get(), gfx::Rect(), nullptr);
+    DCHECK(recorder_);
+    item->Raster(recorder_->getRecordingCanvas(), gfx::Rect(), nullptr);
   }
   if (!retain_individual_display_items_) {
     items_.Clear();
@@ -158,10 +156,10 @@
 }
 
 void DisplayItemList::RasterIntoCanvas(const DisplayItem& item) {
-  DCHECK(canvas_);
+  DCHECK(recorder_);
   DCHECK(!retain_individual_display_items_);
 
-  item.Raster(canvas_.get(), gfx::Rect(), nullptr);
+  item.Raster(recorder_->getRecordingCanvas(), gfx::Rect(), nullptr);
 }
 
 bool DisplayItemList::RetainsIndividualDisplayItems() const {
@@ -192,7 +190,6 @@
     picture_memory_usage_ =
         SkPictureUtils::ApproximateBytesUsed(picture_.get());
     recorder_.reset();
-    canvas_.reset();
     is_suitable_for_gpu_rasterization_ =
         picture_->suitableForGpuRasterization(nullptr);
   }
diff --git a/cc/playback/display_item_list.h b/cc/playback/display_item_list.h
index 9aa9ffc..f0eca30b 100644
--- a/cc/playback/display_item_list.h
+++ b/cc/playback/display_item_list.h
@@ -128,7 +128,6 @@
   sk_sp<SkPicture> picture_;
 
   std::unique_ptr<SkPictureRecorder> recorder_;
-  sk_sp<SkCanvas> canvas_;
   const DisplayItemListSettings settings_;
   bool retain_individual_display_items_;
 
diff --git a/chrome/VERSION b/chrome/VERSION
index b6a4f75b..983060f 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=52
 MINOR=0
-BUILD=2721
+BUILD=2722
 PATCH=0
diff --git a/chrome/android/java/res/drawable-hdpi/pr_amex.png b/chrome/android/java/res/drawable-hdpi/pr_amex.png
new file mode 100644
index 0000000..8584b7a8
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/pr_amex.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/pr_discover.png b/chrome/android/java/res/drawable-hdpi/pr_discover.png
new file mode 100644
index 0000000..a34d9bd
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/pr_discover.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/pr_generic.png b/chrome/android/java/res/drawable-hdpi/pr_generic.png
new file mode 100644
index 0000000..fdec48b
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/pr_generic.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/pr_mc.png b/chrome/android/java/res/drawable-hdpi/pr_mc.png
new file mode 100644
index 0000000..5731888
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/pr_mc.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/pr_visa.png b/chrome/android/java/res/drawable-hdpi/pr_visa.png
new file mode 100644
index 0000000..1f8dc61
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/pr_visa.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/pr_amex.png b/chrome/android/java/res/drawable-mdpi/pr_amex.png
new file mode 100644
index 0000000..67dfb96
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/pr_amex.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/pr_discover.png b/chrome/android/java/res/drawable-mdpi/pr_discover.png
new file mode 100644
index 0000000..c6e04e9
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/pr_discover.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/pr_generic.png b/chrome/android/java/res/drawable-mdpi/pr_generic.png
new file mode 100644
index 0000000..a93ff32d
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/pr_generic.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/pr_mc.png b/chrome/android/java/res/drawable-mdpi/pr_mc.png
new file mode 100644
index 0000000..bcdf48f0
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/pr_mc.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/pr_visa.png b/chrome/android/java/res/drawable-mdpi/pr_visa.png
new file mode 100644
index 0000000..c0c8c08
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/pr_visa.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/pr_amex.png b/chrome/android/java/res/drawable-xhdpi/pr_amex.png
new file mode 100644
index 0000000..f29dcdf57
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/pr_amex.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/pr_discover.png b/chrome/android/java/res/drawable-xhdpi/pr_discover.png
new file mode 100644
index 0000000..6d8f2ff2
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/pr_discover.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/pr_generic.png b/chrome/android/java/res/drawable-xhdpi/pr_generic.png
new file mode 100644
index 0000000..8dd4609
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/pr_generic.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/pr_mc.png b/chrome/android/java/res/drawable-xhdpi/pr_mc.png
new file mode 100644
index 0000000..ade774b8
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/pr_mc.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/pr_visa.png b/chrome/android/java/res/drawable-xhdpi/pr_visa.png
new file mode 100644
index 0000000..493c2ab
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/pr_visa.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/pr_amex.png b/chrome/android/java/res/drawable-xxhdpi/pr_amex.png
new file mode 100644
index 0000000..9cb774c7
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/pr_amex.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/pr_discover.png b/chrome/android/java/res/drawable-xxhdpi/pr_discover.png
new file mode 100644
index 0000000..8c84098
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/pr_discover.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/pr_generic.png b/chrome/android/java/res/drawable-xxhdpi/pr_generic.png
new file mode 100644
index 0000000..da56787c
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/pr_generic.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/pr_mc.png b/chrome/android/java/res/drawable-xxhdpi/pr_mc.png
new file mode 100644
index 0000000..450a1b6c
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/pr_mc.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/pr_visa.png b/chrome/android/java/res/drawable-xxhdpi/pr_visa.png
new file mode 100644
index 0000000..53b39f8
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/pr_visa.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/pr_amex.png b/chrome/android/java/res/drawable-xxxhdpi/pr_amex.png
new file mode 100644
index 0000000..318ec6584
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/pr_amex.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/pr_discover.png b/chrome/android/java/res/drawable-xxxhdpi/pr_discover.png
new file mode 100644
index 0000000..b8109ff
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/pr_discover.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/pr_generic.png b/chrome/android/java/res/drawable-xxxhdpi/pr_generic.png
new file mode 100644
index 0000000..bda5b34
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/pr_generic.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/pr_mc.png b/chrome/android/java/res/drawable-xxxhdpi/pr_mc.png
new file mode 100644
index 0000000..4cdd3a52
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/pr_mc.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/pr_visa.png b/chrome/android/java/res/drawable-xxxhdpi/pr_visa.png
new file mode 100644
index 0000000..ef6aa856
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/pr_visa.png
Binary files differ
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index 261f6cd..3d4c3ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -474,9 +474,17 @@
     }
 
     public List<AutofillProfile> getProfiles() {
+        return getProfilesWithAddressOnlyLabels(false);
+    }
+
+    public List<AutofillProfile> getAddressOnlyProfiles() {
+        return getProfilesWithAddressOnlyLabels(true);
+    }
+
+    private List<AutofillProfile> getProfilesWithAddressOnlyLabels(boolean addressOnly) {
         ThreadUtils.assertOnUiThread();
 
-        String[] profileLabels = nativeGetProfileLabels(mPersonalDataManagerAndroid);
+        String[] profileLabels = nativeGetProfileLabels(mPersonalDataManagerAndroid, addressOnly);
 
         int profileCount = nativeGetProfileCount(mPersonalDataManagerAndroid);
         List<AutofillProfile> profiles = new ArrayList<AutofillProfile>(profileCount);
@@ -578,7 +586,8 @@
 
     private native long nativeInit();
     private native int nativeGetProfileCount(long nativePersonalDataManagerAndroid);
-    private native String[] nativeGetProfileLabels(long nativePersonalDataManagerAndroid);
+    private native String[] nativeGetProfileLabels(long nativePersonalDataManagerAndroid,
+            boolean addressOnly);
     private native AutofillProfile nativeGetProfileByIndex(long nativePersonalDataManagerAndroid,
             int index);
     private native AutofillProfile nativeGetProfileByGUID(long nativePersonalDataManagerAndroid,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
index e75a2709..8f6e02c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -248,6 +248,7 @@
 
     @Override
     public boolean isChecked() {
+        if (mHighlightView == null) return false;
         return mHighlightView.isChecked();
     }
 
@@ -258,7 +259,8 @@
 
     @Override
     public void setChecked(boolean checked) {
-        mHighlightView.setChecked(checked);
+        // Unselectable rows do not have highlight view.
+        if (mHighlightView != null) mHighlightView.setChecked(checked);
     }
 
     // BookmarkUIObserver implementations.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java
new file mode 100644
index 0000000..c3a5ba0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium 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.offlinepages;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.profiles.Profile;
+
+/**
+ * Provides Java scheduling support from native offlining code as
+ * well as JNI interface to tell native code to start processing
+ * queued requests.
+ */
+@JNINamespace("offline_pages::android")
+public class BackgroundSchedulerBridge {
+
+    /**
+     * Callback used to determine when request processing is done.
+     */
+    public interface ProcessingDoneCallback {
+        @CalledByNative("ProcessingDoneCallback")
+        void onProcessingDone(boolean result);
+    }
+
+    // Starts processing of one or more queued background requests.
+    // Returns whether processing was started and that caller should
+    // expect a callback (once processing has completed or terminated).
+    // If processing was already active or not able to process for
+    // some other reason, returns false and this calling instance will
+    // not receive a callback.
+    // TODO(dougarnett): consider adding policy check api to let caller
+    //     separately determine if not allowed by policy.
+    public static boolean startProcessing(
+            Profile profile, ProcessingDoneCallback callback) {
+        return nativeStartProcessing(profile, callback);
+    }
+
+    @CalledByNative
+    private static void schedule() {
+        // TODO(dougarnett): call GcmNetworkManager to schedule for
+        //     OfflinePageUtils.TASK_TAG.
+    }
+
+    @CalledByNative
+    private static void unschedule() {
+        // TODO(dougarnett): call GcmNetworkManager to unschedule for
+        //     OfflinePageUtils.TASK_TAG.
+    }
+
+    private static native boolean nativeStartProcessing(
+            Profile profile, ProcessingDoneCallback callback);
+}
+
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index f5b4dd1..474f62a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -830,11 +830,6 @@
     }
 
     @Override
-    public void onUrlPreFocusChanged(boolean gainFocus) {
-      // TODO(mariakhomenko): remove from the interface entirely as it's unused
-    }
-
-    @Override
     public void setUrlBarFocus(boolean shouldBeFocused) {
         if (shouldBeFocused) {
             mUrlBar.requestFocus();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index e6a0fee..0ee47fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -158,13 +158,6 @@
         Tab getCurrentTab();
 
         /**
-         * Called at the beginning of the focus change event before the underlying TextView
-         * behavior is triggered.
-         * @param gainFocus Whether the URL is gaining focus or not.
-         */
-        void onUrlPreFocusChanged(boolean gainFocus);
-
-        /**
          * Called when the text state has changed and the autocomplete suggestions should be
          * refreshed.
          *
@@ -435,7 +428,6 @@
     @Override
     protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
         mFocused = focused;
-        mUrlBarDelegate.onUrlPreFocusChanged(focused);
         if (!focused) mAutocompleteSpan.clearSpan();
         super.onFocusChanged(focused, direction, previouslyFocusedRect);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java
index bee24da5..4c43892 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java
@@ -33,8 +33,7 @@
      * Builds for the autofill address.
      */
     public AutofillAddress(AutofillProfile profile) {
-        super(profile.getGUID(), profile.getFullName(), profile.getStreetAddress(),
-                PaymentOption.NO_ICON);
+        super(profile.getGUID(), profile.getLabel(), profile.getFullName(), PaymentOption.NO_ICON);
 
         assert profile.getCountryCode() != null : "Country code should not be null";
         assert Pattern.compile(REGION_CODE_PATTERN).matcher(profile.getCountryCode()).matches()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 76e000e..0664569 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -174,7 +174,7 @@
         }
 
         List<AutofillAddress> addresses = new ArrayList<>();
-        List<AutofillProfile> profiles = PersonalDataManager.getInstance().getProfiles();
+        List<AutofillProfile> profiles = PersonalDataManager.getInstance().getAddressOnlyProfiles();
         for (int i = 0; i < profiles.size(); i++) {
             AutofillProfile profile = profiles.get(i);
             if (profile.getCountryCode() != null
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
index c2ee877..ba38f757 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
@@ -11,6 +11,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.os.Handler;
+import android.support.v4.content.ContextCompat;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -575,10 +576,12 @@
             mSelectedSection.setClickable(true);
         }
 
-        sectionLabel.setBackgroundColor(R.color.default_primary_color);
+        int defaultPrimaryColor = ContextCompat.getColor(mContext, R.color.default_primary_color);
+
+        sectionLabel.setBackgroundColor(defaultPrimaryColor);
         sectionLabel.setClickable(false);
 
-        section.setBackgroundColor(R.color.default_primary_color);
+        section.setBackgroundColor(defaultPrimaryColor);
         section.setClickable(false);
 
         ViewGroup noLongerSelectedSection = mSelectedSection;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java
index e0ed430..c1344b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java
@@ -1145,8 +1145,9 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            // Tests might try to destroy this in the wrong thread.
-            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            // Per Object#finalize(), finalizers are run on a single VM-wide finalizer thread, and
+            // the native objects need to be destroyed on the UI thread.
+            ThreadUtils.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     ensureNativeChromeDestroyedOnUIThread();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index 3a8c9ea..a7f51d09 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -618,9 +618,6 @@
     public void setToolbarDataProvider(ToolbarDataProvider model) {}
 
     @Override
-    public void onUrlPreFocusChanged(boolean gainFocus) {}
-
-    @Override
     public void onTextChangedForAutocomplete(boolean canInlineAutocomplete) {}
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index e98416b..d4e2823c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1106,6 +1106,10 @@
                     && tab.getWebContents().isLoadingToDifferentDocument()) {
                 mToolbar.onNavigatedToDifferentPage();
             }
+
+            // Ensure the URL bar loses focus if the tab it was interacting with is changed from
+            // underneath it.
+            setUrlBarFocus(false);
         }
 
         Profile profile = mTabModelSelector.getModel(isIncognito).getProfile();
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 3432c06..66047736 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -503,9 +503,7 @@
         Usage and crash reports
       </message>
       <message name="IDS_USAGE_AND_CRASH_REPORTS_DESCRIPTION" desc="Description for 'Usage and crash reports' preference">
-        Help us prioritize the features and improvements we should work on by sending Google information about the files, applications, and services running whenever you experience a crash.
-
-Usage statistics include information such as preferences, button clicks, and memory usage. They do not include webpage URLs or any personal information. Crash reports contain system information at the time of the crash, and may contain web page URLs or personal information, depending on what was happening at the time of the crash.
+        Automatically send usage statistics and crash reports to Google
       </message>
 
       <!-- Accessibility preferences -->
@@ -1020,7 +1018,7 @@
       <message name="IDS_NTP_RECENT_TABS_SYNC_PROMO_INSTRUCTIONS" desc="Information about sync displayed on the NTP when the user has signed in on mobile but not on desktop">
         Tabs that you've opened in Chrome on your other devices will appear here
       </message>
-      
+
       <message name="IDS_SIGN_IN_SYNC" desc="Sync preference title in signed-in prefs and prefix for notification related to sync [CHAR-LIMIT=32]">
         Sync
       </message>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index c955dbb..fef5ff3 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -516,6 +516,7 @@
   "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageListItem.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageViewHolder.java",
+  "java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/ClientId.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
index 5546e04d..c57ff404 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -131,6 +131,13 @@
                 (LocationBarLayout) getActivity().findViewById(R.id.location_bar);
         final UrlBar urlBar = (UrlBar) getActivity().findViewById(R.id.url_bar);
 
+        ThreadUtils.runOnUiThreadBlocking(new Runnable(){
+            @Override
+            public void run() {
+                urlBar.setUrl("http://www.example.com/", null);
+            }
+        });
+
         final TestAutocompleteController controller = new TestAutocompleteController(
                 locationBar, null, null);
 
@@ -151,6 +158,10 @@
                 return controller.numZeroSuggestRequests();
             }
         }));
+
+        getInstrumentation().waitForIdleSync();
+
+        assertFalse(controller.isStartAutocompleteCalled());
     }
 
     /**
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 91dfa2c..4e1b2f6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2530,6 +2530,9 @@
         <message name="IDS_CERT_SELECTOR_PROVIDER_COLUMN" desc="The text of the header for the certificate provider column in the certificate selector dialog.">
           Provider
         </message>
+        <message name="IDS_CERT_SELECTOR_SERIAL_COLUMN" desc="The text of the header for the certificate serial number column in the certificate selector dialog.">
+          Serial
+        </message>
       </if>
 
       <!-- Certificate viewer dialog strings that are shared across all the platforms -->
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index d1f8edf..cb5af8b 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -63,6 +63,7 @@
 #include "chrome/browser/android/ntp/new_tab_page_prefs.h"
 #include "chrome/browser/android/ntp/ntp_snippets_bridge.h"
 #include "chrome/browser/android/ntp/ntp_snippets_launcher.h"
+#include "chrome/browser/android/offline_pages/background_scheduler_bridge.h"
 #include "chrome/browser/android/offline_pages/offline_page_bridge.h"
 #include "chrome/browser/android/omnibox/answers_image_bridge.h"
 #include "chrome/browser/android/omnibox/autocomplete_controller_android.h"
@@ -228,6 +229,8 @@
      autofill::AutofillPopupViewAndroid::RegisterAutofillPopupViewAndroid},
     {"AutofillProfileBridge", autofill::RegisterAutofillProfileBridge},
     {"AutoSigninSnackbarController", RegisterAutoSigninSnackbarController},
+    {"BackgroundSchedulerBridge",
+      offline_pages::android::RegisterBackgroundSchedulerBridge},
     {"BackgroundSyncLauncherAndroid",
      BackgroundSyncLauncherAndroid::RegisterLauncher},
     {"BluetoothChooserAndroid", BluetoothChooserAndroid::Register},
diff --git a/chrome/browser/android/offline_pages/DEPS b/chrome/browser/android/offline_pages/DEPS
index 568b6d67..7945571 100644
--- a/chrome/browser/android/offline_pages/DEPS
+++ b/chrome/browser/android/offline_pages/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/offline_pages",
+  "+components/offline_pages/background",
 ]
diff --git a/chrome/browser/android/offline_pages/background_scheduler_bridge.cc b/chrome/browser/android/offline_pages/background_scheduler_bridge.cc
new file mode 100644
index 0000000..455c8b9
--- /dev/null
+++ b/chrome/browser/android/offline_pages/background_scheduler_bridge.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium 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/android/scoped_java_ref.h"
+#include "chrome/browser/android/offline_pages/background_scheduler_bridge.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "jni/BackgroundSchedulerBridge_jni.h"
+
+using base::android::ScopedJavaGlobalRef;
+
+namespace offline_pages {
+namespace android {
+
+namespace {
+
+// C++ callback that delegates to Java callback.
+void ProcessingDoneCallback(
+    const ScopedJavaGlobalRef<jobject>& j_callback_obj, jboolean result) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_ProcessingDoneCallback_onProcessingDone(
+      env, j_callback_obj.obj(), result);
+}
+
+}  // namespace
+
+// JNI call to start request processing.
+static jboolean StartProcessing(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jobject>& j_callback_obj) {
+  ScopedJavaGlobalRef<jobject> j_callback_ref;
+  j_callback_ref.Reset(env, j_callback_obj);
+  base::Bind(&ProcessingDoneCallback, j_callback_ref);
+  // TODO(dougarnett): lookup/create RequestCoordinator KeyedService
+  // and call StartProcessing on it with bound j_callback_obj.
+  return false;
+}
+
+BackgroundSchedulerBridge::BackgroundSchedulerBridge() {
+}
+
+BackgroundSchedulerBridge::~BackgroundSchedulerBridge() {
+}
+
+void BackgroundSchedulerBridge::Schedule(
+    const TriggerCondition& trigger_condition) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  // TODO(dougarnett): pass trigger_condition.
+  Java_BackgroundSchedulerBridge_schedule(env);
+}
+
+void BackgroundSchedulerBridge::Unschedule() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_BackgroundSchedulerBridge_unschedule(env);
+}
+
+bool RegisterBackgroundSchedulerBridge(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android
+}  // namespace offline_pages
diff --git a/chrome/browser/android/offline_pages/background_scheduler_bridge.h b/chrome/browser/android/offline_pages/background_scheduler_bridge.h
new file mode 100644
index 0000000..71aad2f
--- /dev/null
+++ b/chrome/browser/android/offline_pages/background_scheduler_bridge.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium 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_OFFLINE_PAGES_BACKGROUND_SCHEDULER_BRIDGE_H_
+#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_BACKGROUND_SCHEDULER_BRIDGE_H_
+
+#include "components/offline_pages/background/scheduler.h"
+
+#include "base/android/jni_android.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace offline_pages {
+namespace android {
+
+// Bridge between C++ and Java for implementing background scheduler
+// on Android.
+class BackgroundSchedulerBridge : Scheduler {
+ public:
+  BackgroundSchedulerBridge();
+  ~BackgroundSchedulerBridge() override;
+
+  // Scheduler implementation.
+  void Schedule(const TriggerCondition& trigger_condition) override;
+  void Unschedule() override;
+};
+
+bool RegisterBackgroundSchedulerBridge(JNIEnv* env);
+
+}  // namespace android
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_BACKGROUND_SCHEDULER_BRIDGE_H_
+
diff --git a/chrome/browser/android/offline_pages/prerendering_loader.cc b/chrome/browser/android/offline_pages/prerendering_loader.cc
new file mode 100644
index 0000000..c29e1fd
--- /dev/null
+++ b/chrome/browser/android/offline_pages/prerendering_loader.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium 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/offline_pages/prerendering_loader.h"
+
+#include "content/public/browser/web_contents.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace offline_pages {
+
+PrerenderingLoader::PrerenderingLoader(PrerenderManager* prerender_manager) {}
+
+PrerenderingLoader::~PrerenderingLoader() {}
+
+bool PrerenderingLoader::LoadPage(
+    const GURL& url,
+    content::SessionStorageNamespace* session_storage_namespace,
+    const gfx::Size& size,
+    const LoadPageCallback& callback) {
+  // TODO(dougarnett): implement.
+  return false;
+}
+
+void PrerenderingLoader::StopLoading() {
+  // TODO(dougarnett): implement.
+}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/android/offline_pages/prerendering_loader.h b/chrome/browser/android/offline_pages/prerendering_loader.h
new file mode 100644
index 0000000..c83c2770
--- /dev/null
+++ b/chrome/browser/android/offline_pages/prerendering_loader.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium 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_OFFLINE_PAGES_PRERENDERING_LOADER_H_
+#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PRERENDERING_LOADER_H_
+
+#include "base/callback.h"
+#include "components/offline_pages/background/offliner.h"
+
+class GURL;
+class PrerenderManager;
+
+namespace content {
+class WebContents;
+class SessionStorageNamespace;
+}  // namespace content
+
+namespace gfx {
+class Size;
+}  // namespace gfx
+
+namespace offline_pages {
+
+// A client-side page loader that integrates with the PrerenderManager to do
+// the page loading in the background.
+class PrerenderingLoader {
+ public:
+  // Reports status of a load page request with loaded contents if available.
+  typedef base::Callback<void(Offliner::CompletionStatus,
+                              content::WebContents*)>
+      LoadPageCallback;
+
+  explicit PrerenderingLoader(PrerenderManager* prerender_manager);
+  ~PrerenderingLoader();
+
+  // Loads a page in the background if possible and returns whether the
+  // request was accepted. If so, the LoadPageCallback will be informed
+  // of status. Only one load request may exist as a time. If a previous
+  // request is still in progress it must be canceled before a new
+  // request will be accepted.
+  bool LoadPage(const GURL& url,
+                content::SessionStorageNamespace* session_storage_namespace,
+                const gfx::Size& size,
+                const LoadPageCallback& callback);
+
+  // Stops (completes or cancels) the load request. Must be called when
+  // LoadPageCallback is done with consuming the contents.
+  // This loader should also be responsible for stopping offline
+  // prerenders when Chrome is transitioned to foreground.
+  void StopLoading();
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PRERENDERING_LOADER_H_
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner.cc b/chrome/browser/android/offline_pages/prerendering_offliner.cc
new file mode 100644
index 0000000..9ad64dc
--- /dev/null
+++ b/chrome/browser/android/offline_pages/prerendering_offliner.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium 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/offline_pages/prerendering_offliner.h"
+
+namespace offline_pages {
+
+PrerenderingOffliner::PrerenderingOffliner(PrerenderManager* prerender_manager,
+                                           OfflinePageModel* offline_page_model)
+    : loader_(new PrerenderingLoader(prerender_manager)) {}
+
+PrerenderingOffliner::~PrerenderingOffliner() {}
+
+bool PrerenderingOffliner::LoadAndSave(const SavePageRequest& request,
+                                       const CompletionCallback& callback) {
+  // TODO(dougarnett): implement.
+  return false;
+}
+
+void PrerenderingOffliner::Cancel() {
+  loader_->StopLoading();
+}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner.h b/chrome/browser/android/offline_pages/prerendering_offliner.h
new file mode 100644
index 0000000..836bc86c
--- /dev/null
+++ b/chrome/browser/android/offline_pages/prerendering_offliner.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium 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_OFFLINE_PAGES_PRERENDERING_OFFLINER_H_
+#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PRERENDERING_OFFLINER_H_
+
+#include "chrome/browser/android/offline_pages/prerendering_loader.h"
+#include "components/offline_pages/background/offliner.h"
+#include "components/offline_pages/offline_page_model.h"
+
+class PrerenderManager;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace offline_pages {
+
+// An Offliner implementation that attempts client-side rendering and saving
+// of an offline page. It uses the PrerenderingLoader to load the page and
+// the OfflinePageModel to save it.
+class PrerenderingOffliner : public Offliner {
+ public:
+  PrerenderingOffliner(PrerenderManager* prerender_manager,
+                       OfflinePageModel* offline_page_model);
+  ~PrerenderingOffliner() override;
+
+  bool LoadAndSave(const SavePageRequest& request,
+                   const CompletionCallback& callback) override;
+
+  void Cancel() override;
+
+ private:
+  std::unique_ptr<PrerenderingLoader> loader_;
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PRERENDERING_OFFLINER_H_
diff --git a/chrome/browser/android/resource_id.h b/chrome/browser/android/resource_id.h
index bd741b8..ae0abf9 100644
--- a/chrome/browser/android/resource_id.h
+++ b/chrome/browser/android/resource_id.h
@@ -71,3 +71,10 @@
 LINK_RESOURCE_ID(IDR_CREDIT_CARD_CVC_HINT_AMEX, R.drawable.cvc_icon_amex)
 LINK_RESOURCE_ID(IDR_AUTOFILL_SETTINGS,
                  org.chromium.chrome.R.drawable.ic_settings)
+
+// PaymentRequest images.
+LINK_RESOURCE_ID(IDR_AUTOFILL_PR_AMEX, R.drawable.pr_amex)
+LINK_RESOURCE_ID(IDR_AUTOFILL_PR_DISCOVER, R.drawable.pr_discover)
+LINK_RESOURCE_ID(IDR_AUTOFILL_PR_GENERIC, R.drawable.pr_generic)
+LINK_RESOURCE_ID(IDR_AUTOFILL_PR_MASTERCARD, R.drawable.pr_mc)
+LINK_RESOURCE_ID(IDR_AUTOFILL_PR_VISA, R.drawable.pr_visa)
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index e103e71e..4b350c31 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -2429,7 +2429,13 @@
 INSTANTIATE_TEST_CASE_P(WebViewTests, WebViewCaptureTest, testing::Bool());
 
 IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestZoomAPI) {
-  TestHelper("testZoomAPI", "web_view/shim", NO_TEST_SERVER);
+  // TODO(wjmaclean): This test is broken, but only when the
+  // --use-cross-process-frames-for-guests flag is specified. This needs to be
+  // fixed. See also comment in
+  // CrossProcessFrameConnector::OnFrameRectChanged().
+  // http://crbug.com/607978
+  if (!GetParam())
+    TestHelper("testZoomAPI", "web_view/shim", NO_TEST_SERVER);
 }
 
 IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestFindAPI) {
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index 48ba691..015b99f 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
 
 #include <stddef.h>
+#include <memory>
 
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
@@ -31,6 +32,7 @@
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
+#include "grit/components_scaled_resources.h"
 #include "jni/PersonalDataManager_jni.h"
 
 using base::android::ConvertJavaStringToUTF8;
@@ -129,35 +131,39 @@
           Java_AutofillProfile_getLanguageCode(env, jprofile.obj())));
 }
 
-// Mapping from Chrome card types to basic card payment spec.
+// Mapping from Chrome card types to PaymentRequest basic card payment spec and
+// icons. Note that "generic" is not in the spec.
 // https://w3c.github.io/browser-payment-api/specs/basic-card-payment.html#method-id
-// Note that "generic" is not in the spec.
-const struct {
+const struct PaymentRequestData {
   const char* card_type;
   const char* basic_card_payment_type;
-} kBasicCardPaymentTypes[] {
-  {"genericCC", "generic"},
-  {"americanExpressCC", "amex"},
-  {"dinersCC", "diners"},
-  {"discoverCC", "discover"},
-  {"jcbCC", "jcb"},
-  {"masterCardCC", "mastercard"},
-  {"visaCC", "visa"}
+  const int icon_resource_id;
+} kPaymentRequestData[] {
+  {"genericCC", "generic", IDR_AUTOFILL_PR_GENERIC},
+  {"americanExpressCC", "amex", IDR_AUTOFILL_PR_AMEX},
+  {"dinersCC", "diners", IDR_AUTOFILL_PR_GENERIC},
+  {"discoverCC", "discover", IDR_AUTOFILL_PR_DISCOVER},
+  {"jcbCC", "jcb", IDR_AUTOFILL_PR_GENERIC},
+  {"masterCardCC", "mastercard", IDR_AUTOFILL_PR_MASTERCARD},
+  {"visaCC", "visa", IDR_AUTOFILL_PR_VISA}
 };
 
-// Returns the type of this card according to the basic card payment spec. Will
-// return "generic" for unrecognized card type.
-const char* ConvertToBasicCardPaymentType(const std::string& type) {
-  for (size_t i = 0; i < arraysize(kBasicCardPaymentTypes); ++i) {
-    if (type == kBasicCardPaymentTypes[i].card_type)
-      return kBasicCardPaymentTypes[i].basic_card_payment_type;
+// Converts the card type into PaymentRequest type according to the basic card
+// payment spec and an icon. Will set the type and the icon to "generic" for
+// unrecognized card type.
+const PaymentRequestData* GetPaymentRequestData(const std::string& type) {
+  for (size_t i = 0; i < arraysize(kPaymentRequestData); ++i) {
+    if (type == kPaymentRequestData[i].card_type)
+      return &kPaymentRequestData[i];
   }
-  return kBasicCardPaymentTypes[0].basic_card_payment_type;
+  return &kPaymentRequestData[0];
 }
 
 ScopedJavaLocalRef<jobject> CreateJavaCreditCardFromNative(
     JNIEnv* env,
     const CreditCard& card) {
+  const PaymentRequestData* payment_request_data =
+      GetPaymentRequestData(card.type());
   return Java_CreditCard_create(
       env, ConvertUTF8ToJavaString(env, card.guid()).obj(),
       ConvertUTF8ToJavaString(env, card.origin()).obj(),
@@ -172,10 +178,11 @@
       ConvertUTF16ToJavaString(env,
                                card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR))
           .obj(),
-      ConvertUTF8ToJavaString(env, ConvertToBasicCardPaymentType(card.type()))
+      ConvertUTF8ToJavaString(env,
+                              payment_request_data->basic_card_payment_type)
           .obj(),
       ResourceMapper::MapFromChromiumId(
-          CreditCard::IconResourceId(card.type())));
+          payment_request_data->icon_resource_id));
 }
 
 void PopulateNativeCreditCardFromJava(
@@ -332,11 +339,28 @@
 
 ScopedJavaLocalRef<jobjectArray> PersonalDataManagerAndroid::GetProfileLabels(
     JNIEnv* env,
-    const JavaParamRef<jobject>& unused_obj) {
+    const JavaParamRef<jobject>& unused_obj,
+    bool address_only) {
+  std::unique_ptr<std::vector<ServerFieldType>> suggested_fields;
+  size_t minimal_fields_shown = 2;
+  if (address_only) {
+    suggested_fields.reset(new std::vector<ServerFieldType>);
+    suggested_fields->push_back(COMPANY_NAME);
+    suggested_fields->push_back(ADDRESS_HOME_LINE1);
+    suggested_fields->push_back(ADDRESS_HOME_LINE2);
+    suggested_fields->push_back(ADDRESS_HOME_DEPENDENT_LOCALITY);
+    suggested_fields->push_back(ADDRESS_HOME_CITY);
+    suggested_fields->push_back(ADDRESS_HOME_STATE);
+    suggested_fields->push_back(ADDRESS_HOME_ZIP);
+    suggested_fields->push_back(ADDRESS_HOME_SORTING_CODE);
+    suggested_fields->push_back(ADDRESS_HOME_COUNTRY);
+    minimal_fields_shown = suggested_fields->size();
+  }
+
   std::vector<base::string16> labels;
   AutofillProfile::CreateInferredLabels(
-      personal_data_manager_->GetProfiles(), NULL, NAME_FULL, 2,
-      g_browser_process->GetApplicationLocale(), &labels);
+      personal_data_manager_->GetProfiles(), suggested_fields.get(), NAME_FULL,
+      minimal_fields_shown, g_browser_process->GetApplicationLocale(), &labels);
 
   return base::android::ToJavaArrayOfStrings(env, labels);
 }
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.h b/chrome/browser/autofill/android/personal_data_manager_android.h
index ddc9fac7..2d12088 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.h
+++ b/chrome/browser/autofill/android/personal_data_manager_android.h
@@ -52,9 +52,15 @@
 
   // Gets the labels for all known profiles. These labels are useful for
   // distinguishing the profiles from one another.
+  //
+  // The labels never contain the full name and include at least 2 fields.
+  //
+  // If |address_only| is true, then such fields as phone number and email
+  // address are also omitted, but all fields are included in the label.
   base::android::ScopedJavaLocalRef<jobjectArray> GetProfileLabels(
       JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& unused_obj);
+      const base::android::JavaParamRef<jobject>& unused_obj,
+      bool address_only);
 
   // These functions act on local credit cards.
   // --------------------
diff --git a/chrome/browser/chromeos/events/event_rewriter_unittest.cc b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
index 21942f7..ab3c767d 100644
--- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc
+++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
@@ -489,12 +489,9 @@
 
 TEST_F(EventRewriterTest, TestRewriteNumPadKeysWithDiamondKeyFlag) {
   // Make sure the num lock works correctly even when Diamond key exists.
-  const base::CommandLine original_cl(*base::CommandLine::ForCurrentProcess());
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       chromeos::switches::kHasChromeOSDiamondKey, "");
-
   TestRewriteNumPadKeys();
-  *base::CommandLine::ForCurrentProcess() = original_cl;
 }
 
 // Tests if the rewriter can handle a Command + Num Pad event.
diff --git a/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc
index 7d42b96f..94cc986 100644
--- a/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/command_line.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_command_line.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -69,12 +70,13 @@
   std::unique_ptr<TestingProfile> profile2(
       static_cast<TestingProfile*>(CreateBrowserContext()));
 
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-  base::CommandLine saved_cmdline_ = *base::CommandLine::ForCurrentProcess();
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+  ActivityLog* activity_log1;
+  {
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch(
       switches::kEnableExtensionActivityLogging);
-  ActivityLog* activity_log1 = ActivityLog::GetInstance(profile1.get());
-  *base::CommandLine::ForCurrentProcess() = saved_cmdline_;
+    activity_log1 = ActivityLog::GetInstance(profile1.get());
+  }
   ActivityLog* activity_log2 = ActivityLog::GetInstance(profile2.get());
 
   EXPECT_EQ(0,
@@ -229,19 +231,17 @@
 }
 
 TEST_F(ActivityLogEnabledTest, AppAndCommandLine) {
-  // Set the command line switch.
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-  base::CommandLine saved_cmdline_ = *base::CommandLine::ForCurrentProcess();
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableExtensionActivityLogging);
 
   std::unique_ptr<TestingProfile> profile(
       static_cast<TestingProfile*>(CreateBrowserContext()));
   // Extension service is destroyed by the profile.
+  base::CommandLine no_program_command_line(base::CommandLine::NO_PROGRAM);
   ExtensionService* extension_service =
     static_cast<TestExtensionSystem*>(
         ExtensionSystem::Get(profile.get()))->CreateExtensionService(
-            &command_line, base::FilePath(), false);
+            &no_program_command_line, base::FilePath(), false);
   static_cast<TestExtensionSystem*>(
       ExtensionSystem::Get(profile.get()))->SetReady();
 
@@ -281,9 +281,6 @@
   EXPECT_EQ(0,
       profile->GetPrefs()->GetInteger(prefs::kWatchdogExtensionActive));
   EXPECT_FALSE(activity_log->IsWatchdogAppActive());
-
-  // Cleanup.
-  *base::CommandLine::ForCurrentProcess() = saved_cmdline_;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/counting_policy_unittest.cc b/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
index 8b08a4ab..42f7420 100644
--- a/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
@@ -48,19 +48,17 @@
 class CountingPolicyTest : public testing::Test {
  public:
   CountingPolicyTest()
-      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
-        saved_cmdline_(base::CommandLine::NO_PROGRAM) {
+      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
 #if defined OS_CHROMEOS
     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
 #endif
-    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-    saved_cmdline_ = *base::CommandLine::ForCurrentProcess();
     profile_.reset(new TestingProfile());
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExtensionActivityLogging);
+    base::CommandLine::ForCurrentProcess()->
+        AppendSwitch(switches::kEnableExtensionActivityLogging);
+    base::CommandLine no_program_command_line(base::CommandLine::NO_PROGRAM);
     extension_service_ = static_cast<TestExtensionSystem*>(
         ExtensionSystem::Get(profile_.get()))->CreateExtensionService
-            (&command_line, base::FilePath(), false);
+            (&no_program_command_line, base::FilePath(), false);
   }
 
   ~CountingPolicyTest() override {
@@ -70,8 +68,6 @@
     base::RunLoop().RunUntilIdle();
     profile_.reset(NULL);
     base::RunLoop().RunUntilIdle();
-    // Restore the original command line and undo the affects of SetUp().
-    *base::CommandLine::ForCurrentProcess() = saved_cmdline_;
   }
 
   // Wait for the task queue for the specified thread to empty.
@@ -526,11 +522,6 @@
   ExtensionService* extension_service_;
   std::unique_ptr<TestingProfile> profile_;
   content::TestBrowserThreadBundle thread_bundle_;
-  // Used to preserve a copy of the original command line.
-  // The test framework will do this itself as well. However, by then,
-  // it is too late to call ActivityLog::RecomputeLoggingIsEnabled() in
-  // TearDown().
-  base::CommandLine saved_cmdline_;
 
 #if defined OS_CHROMEOS
   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
index c1509e4..6a349e9 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
@@ -45,21 +45,18 @@
 class FullStreamUIPolicyTest : public testing::Test {
  public:
   FullStreamUIPolicyTest()
-      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
-        saved_cmdline_(base::CommandLine::NO_PROGRAM) {
+      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
 #if defined OS_CHROMEOS
     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
 #endif
-    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-    saved_cmdline_ = *base::CommandLine::ForCurrentProcess();
+    base::CommandLine no_program_command_line(base::CommandLine::NO_PROGRAM);
     profile_.reset(new TestingProfile());
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExtensionActivityLogging);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExtensionActivityLogTesting);
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    command_line->AppendSwitch(switches::kEnableExtensionActivityLogging);
+    command_line->AppendSwitch(switches::kEnableExtensionActivityLogTesting);
     extension_service_ = static_cast<TestExtensionSystem*>(
         ExtensionSystem::Get(profile_.get()))->CreateExtensionService
-            (&command_line, base::FilePath(), false);
+            (&no_program_command_line, base::FilePath(), false);
   }
 
   ~FullStreamUIPolicyTest() override {
@@ -69,8 +66,6 @@
     base::RunLoop().RunUntilIdle();
     profile_.reset(NULL);
     base::RunLoop().RunUntilIdle();
-    // Restore the original command line and undo the affects of SetUp().
-    *base::CommandLine::ForCurrentProcess() = saved_cmdline_;
   }
 
   // A wrapper function for CheckReadFilteredData, so that we don't need to
@@ -443,11 +438,6 @@
   ExtensionService* extension_service_;
   std::unique_ptr<TestingProfile> profile_;
   content::TestBrowserThreadBundle thread_bundle_;
-  // Used to preserve a copy of the original command line.
-  // The test framework will do this itself as well. However, by then,
-  // it is too late to call ActivityLog::RecomputeLoggingIsEnabled() in
-  // TearDown().
-  base::CommandLine saved_cmdline_;
 
 #if defined OS_CHROMEOS
   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index 673b0bd..034888e0 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -303,6 +303,11 @@
     result = RouteRequestResult::FromError(
         error, RouteRequestResult::OFF_THE_RECORD_MISMATCH);
   } else {
+    // Off the record media routes do not support custom controllers.
+    // crbug.com/590376
+    if (media_route->off_the_record)
+      media_route->custom_controller_path = nullptr;
+
     result = RouteRequestResult::FromSuccess(
         media_route.To<std::unique_ptr<MediaRoute>>(), presentation_id);
   }
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index eee1a19..819a4380 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -107,6 +107,18 @@
   return mojoIssue;
 }
 
+interfaces::MediaRoutePtr CreateMojoRoute() {
+  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
+  route->media_source = kSource;
+  route->media_sink_id = kSinkId;
+  route->media_route_id = kRouteId;
+  route->description = kDescription;
+  route->is_local = true;
+  route->for_display = true;
+  route->off_the_record = false;
+  return route;
+}
+
 }  // namespace
 
 class RouteResponseCallbackHandler {
@@ -217,15 +229,7 @@
              const mojo::String& presentation_id, const mojo::String& origin,
              int tab_id, int64_t timeout_millis, bool off_the_record,
              const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
-            interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-            route->media_source = kSource;
-            route->media_sink_id = kSinkId;
-            route->media_route_id = kRouteId;
-            route->description = kDescription;
-            route->is_local = true;
-            route->for_display = true;
-            route->off_the_record = false;
-            cb.Run(std::move(route), mojo::String(),
+            cb.Run(CreateMojoRoute(), mojo::String(),
                    interfaces::RouteRequestResultCode::OK);
           }));
 
@@ -243,6 +247,44 @@
   run_loop.Run();
 }
 
+TEST_F(MediaRouterMojoImplTest, CreateOffTheRecordRoute) {
+  MediaSource media_source(kSource);
+  MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false, "",
+                            false);
+  expected_route.set_off_the_record(true);
+
+  // Use a lambda function as an invocation target here to work around
+  // a limitation with GMock::Invoke that prevents it from using move-only types
+  // in runnable parameter lists.
+  EXPECT_CALL(mock_media_route_provider_,
+              CreateRoute(mojo::String(kSource), mojo::String(kSinkId), _,
+                          mojo::String(kOrigin), kInvalidTabId, _, _, _))
+      .WillOnce(Invoke(
+          [](const mojo::String& source, const mojo::String& sink,
+             const mojo::String& presentation_id, const mojo::String& origin,
+             int tab_id, int64_t timeout_millis, bool off_the_record,
+             const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
+            interfaces::MediaRoutePtr route = CreateMojoRoute();
+            route->custom_controller_path = "custom/controller/path";
+            route->off_the_record = true;
+            cb.Run(std::move(route), mojo::String(),
+                   interfaces::RouteRequestResultCode::OK);
+          }));
+
+  base::RunLoop run_loop;
+  RouteResponseCallbackHandler handler;
+  EXPECT_CALL(handler, DoInvoke(Pointee(Equals(expected_route)), Not(""), "",
+                                RouteRequestResult::OK))
+      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
+  std::vector<MediaRouteResponseCallback> route_response_callbacks;
+  route_response_callbacks.push_back(base::Bind(
+      &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
+  router()->CreateRoute(
+      kSource, kSinkId, GURL(kOrigin), nullptr, route_response_callbacks,
+      base::TimeDelta::FromMilliseconds(kTimeoutMillis), true);
+  run_loop.Run();
+}
+
 TEST_F(MediaRouterMojoImplTest, CreateRouteFails) {
   EXPECT_CALL(
       mock_media_route_provider_,
@@ -281,15 +323,7 @@
              const mojo::String& presentation_id, const mojo::String& origin,
              int tab_id, int64_t timeout_millis, bool off_the_record,
              const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
-            interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-            route->media_source = kSource;
-            route->media_sink_id = kSinkId;
-            route->media_route_id = kRouteId;
-            route->description = kDescription;
-            route->is_local = true;
-            route->for_display = true;
-            route->off_the_record = false;
-            cb.Run(std::move(route), mojo::String(),
+            cb.Run(CreateMojoRoute(), mojo::String(),
                    interfaces::RouteRequestResultCode::OK);
           }));
 
@@ -310,13 +344,7 @@
 }
 
 TEST_F(MediaRouterMojoImplTest, OffTheRecordRoutesTerminatedOnProfileShutdown) {
-  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-  route->media_source = kSource;
-  route->media_sink_id = kSinkId;
-  route->media_route_id = kRouteId;
-  route->description = kDescription;
-  route->is_local = true;
-  route->for_display = true;
+  interfaces::MediaRoutePtr route = CreateMojoRoute();
   route->off_the_record = true;
 
   EXPECT_CALL(mock_media_route_provider_,
@@ -328,13 +356,7 @@
              const mojo::String& presentation_id, const mojo::String& origin,
              int tab_id, int64_t timeout_millis, bool off_the_record,
              const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
-            interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-            route->media_source = kSource;
-            route->media_sink_id = kSinkId;
-            route->media_route_id = kRouteId;
-            route->description = kDescription;
-            route->is_local = true;
-            route->for_display = true;
+            interfaces::MediaRoutePtr route = CreateMojoRoute();
             route->off_the_record = true;
             cb.Run(std::move(route), mojo::String(),
                    interfaces::RouteRequestResultCode::OK);
@@ -365,15 +387,8 @@
 
   MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false, "",
                             false);
-  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-  route->media_source = kSource;
-  route->media_sink_id = kSinkId;
-  route->media_route_id = kRouteId;
-  route->description = kDescription;
-  route->is_local = true;
-  route->for_display = true;
-  route->off_the_record = false;
 
+  interfaces::MediaRoutePtr route = CreateMojoRoute();
   // Make sure the MR has received an update with the route, so it knows there
   // is a local route to join.
   mojo::Array<interfaces::MediaRoutePtr> mojo_routes(1);
@@ -427,19 +442,10 @@
 }
 
 TEST_F(MediaRouterMojoImplTest, JoinRouteTimedOutFails) {
-  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-  route->media_source = kSource;
-  route->media_sink_id = kSinkId;
-  route->media_route_id = kRouteId;
-  route->description = kDescription;
-  route->is_local = true;
-  route->for_display = true;
-  route->off_the_record = false;
-
   // Make sure the MR has received an update with the route, so it knows there
   // is a local route to join.
   mojo::Array<interfaces::MediaRoutePtr> mojo_routes(1);
-  mojo_routes[0] = route->Clone();
+  mojo_routes[0] = CreateMojoRoute();
   router()->OnRoutesUpdated(std::move(mojo_routes), mojo::String(),
                             mojo::Array<mojo::String>());
 
@@ -471,14 +477,7 @@
 }
 
 TEST_F(MediaRouterMojoImplTest, JoinRouteOffTheRecordMismatchFails) {
-  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-  route->media_source = kSource;
-  route->media_sink_id = kSinkId;
-  route->media_route_id = kRouteId;
-  route->description = kDescription;
-  route->is_local = true;
-  route->for_display = true;
-  route->off_the_record = false;
+  interfaces::MediaRoutePtr route = CreateMojoRoute();
 
   // Make sure the MR has received an update with the route, so it knows there
   // is a local route to join.
@@ -524,14 +523,7 @@
   MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false, "",
                             false);
   expected_route.set_off_the_record(false);
-  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-  route->media_source = kSource;
-  route->media_sink_id = kSinkId;
-  route->media_route_id = kRouteId;
-  route->description = kDescription;
-  route->is_local = true;
-  route->for_display = true;
-  route->off_the_record = false;
+  interfaces::MediaRoutePtr route = CreateMojoRoute();
 
   // Use a lambda function as an invocation target here to work around
   // a limitation with GMock::Invoke that prevents it from using move-only types
@@ -594,14 +586,7 @@
 }
 
 TEST_F(MediaRouterMojoImplTest, ConnectRouteByIdOffTheRecordMismatchFails) {
-  interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
-  route->media_source = kSource;
-  route->media_sink_id = kSinkId;
-  route->media_route_id = kRouteId;
-  route->description = kDescription;
-  route->is_local = true;
-  route->for_display = true;
-  route->off_the_record = false;
+  interfaces::MediaRoutePtr route = CreateMojoRoute();
 
   // Use a lambda function as an invocation target here to work around
   // a limitation with GMock::Invoke that prevents it from using move-only types
@@ -907,19 +892,9 @@
   mojo_joinable_routes[1] = kJoinableRouteId2;
 
   mojo::Array<interfaces::MediaRoutePtr> mojo_routes(2);
-  mojo_routes[0] = interfaces::MediaRoute::New();
-  mojo_routes[0]->media_route_id = kRouteId;
-  mojo_routes[0]->media_source = kSource;
-  mojo_routes[0]->media_sink_id = kSinkId;
-  mojo_routes[0]->description = kDescription;
-  mojo_routes[0]->is_local = false;
-  mojo_routes[0]->off_the_record = false;
-  mojo_routes[1] = interfaces::MediaRoute::New();
+  mojo_routes[0] = CreateMojoRoute();
+  mojo_routes[1] = CreateMojoRoute();
   mojo_routes[1]->media_route_id = kRouteId2;
-  mojo_routes[1]->media_source = kSource;
-  mojo_routes[1]->media_sink_id = kSinkId;
-  mojo_routes[1]->description = kDescription;
-  mojo_routes[1]->is_local = false;
   mojo_routes[1]->off_the_record = true;
 
   EXPECT_CALL(routes_observer, OnRoutesUpdated(SequenceEquals(expected_routes),
@@ -1260,11 +1235,10 @@
                             false);
   auto& media_router_proxy = media_router_proxy_;
 
-  EXPECT_CALL(
-      mock_media_route_provider_,
-      SearchSinksAndCreateRoute_(mojo::String(kSinkId), mojo::String(kSource),
-                                 _, _, mojo::String(kOrigin), kInvalidTabId, _,
-                                 false, _))
+  EXPECT_CALL(mock_media_route_provider_,
+              SearchSinksAndCreateRoute_(
+                  mojo::String(kSinkId), mojo::String(kSource), _, _,
+                  mojo::String(kOrigin), kInvalidTabId, _, false, _))
       .WillOnce(Invoke([&search_input, &domain, &media_router_proxy](
           const mojo::String& sink_id, const mojo::String& source,
           const interfaces::SinkSearchCriteriaPtr& search_criteria,
@@ -1272,13 +1246,9 @@
           int32_t tab_id, int64_t timeout_millis, bool off_the_record,
           const interfaces::MediaRouteProvider::
               SearchSinksAndCreateRouteCallback& cb) {
-        interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
+        interfaces::MediaRoutePtr route = CreateMojoRoute();
         route->media_source = source;
         route->media_sink_id = kSinkId2;
-        route->media_route_id = kRouteId;
-        route->description = kDescription;
-        route->is_local = true;
-        route->for_display = true;
         route->off_the_record = off_the_record;
         EXPECT_EQ(search_input, search_criteria->input);
         EXPECT_EQ(domain, search_criteria->domain);
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
index dc2349a..12287f6e 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
@@ -175,6 +175,8 @@
     return false;
   if (WasStartedInForegroundEventInForeground(time_to_abort, info))
     return true;
+  if (!info.started_in_foreground)
+    return false;
   DCHECK_GT(time_to_abort, info.first_background_time);
   base::TimeDelta bg_abort_delta = time_to_abort - info.first_background_time;
   // Consider this a foregrounded abort if it occurred within 100ms of a
diff --git a/chrome/browser/password_manager/native_backend_libsecret.cc b/chrome/browser/password_manager/native_backend_libsecret.cc
index 21581c0c..7d5b258 100644
--- a/chrome/browser/password_manager/native_backend_libsecret.cc
+++ b/chrome/browser/password_manager/native_backend_libsecret.cc
@@ -4,10 +4,11 @@
 
 #include "chrome/browser/password_manager/native_backend_libsecret.h"
 
-#include <dlfcn.h>
 #include <stddef.h>
 #include <stdint.h>
 
+#include <libsecret/secret.h>
+
 #include <limits>
 #include <list>
 #include <memory>
@@ -32,68 +33,6 @@
 const int kMaxPossibleTimeTValue = std::numeric_limits<int>::max();
 }  // namespace
 
-typeof(&::secret_password_store_sync)
-    LibsecretLoader::secret_password_store_sync;
-typeof(&::secret_service_search_sync)
-    LibsecretLoader::secret_service_search_sync;
-typeof(&::secret_password_clear_sync)
-    LibsecretLoader::secret_password_clear_sync;
-typeof(&::secret_item_get_secret) LibsecretLoader::secret_item_get_secret;
-typeof(&::secret_value_get_text) LibsecretLoader::secret_value_get_text;
-typeof(&::secret_item_get_attributes)
-    LibsecretLoader::secret_item_get_attributes;
-typeof(&::secret_item_load_secret_sync)
-    LibsecretLoader::secret_item_load_secret_sync;
-typeof(&::secret_value_unref) LibsecretLoader::secret_value_unref;
-
-bool LibsecretLoader::libsecret_loaded = false;
-
-const LibsecretLoader::FunctionInfo LibsecretLoader::functions[] = {
-    {"secret_password_store_sync",
-     reinterpret_cast<void**>(&secret_password_store_sync)},
-    {"secret_service_search_sync",
-     reinterpret_cast<void**>(&secret_service_search_sync)},
-    {"secret_password_clear_sync",
-     reinterpret_cast<void**>(&secret_password_clear_sync)},
-    {"secret_item_get_secret",
-     reinterpret_cast<void**>(&secret_item_get_secret)},
-    {"secret_value_get_text", reinterpret_cast<void**>(&secret_value_get_text)},
-    {"secret_item_get_attributes",
-     reinterpret_cast<void**>(&secret_item_get_attributes)},
-    {"secret_item_load_secret_sync",
-     reinterpret_cast<void**>(&secret_item_load_secret_sync)},
-    {"secret_value_unref", reinterpret_cast<void**>(&secret_value_unref)},
-    {nullptr, nullptr}};
-
-bool LibsecretLoader::LoadLibsecret() {
-  if (libsecret_loaded)
-    return true;
-
-  void* handle = dlopen("libsecret-1.so.0", RTLD_NOW | RTLD_GLOBAL);
-  if (!handle) {
-    // We wanted to use libsecret, but we couldn't load it. Warn, because
-    // either the user asked for this, or we autodetected it incorrectly. (Or
-    // the system has broken libraries, which is also good to warn about.)
-    LOG(WARNING) << "Could not load libsecret-1.so.0: " << dlerror();
-    return false;
-  }
-
-  for (size_t i = 0; functions[i].name; ++i) {
-    dlerror();
-    *functions[i].pointer = dlsym(handle, functions[i].name);
-    const char* error = dlerror();
-    if (error) {
-      VLOG(1) << "Unable to load symbol " << functions[i].name << ": " << error;
-      dlclose(handle);
-      return false;
-    }
-  }
-
-  libsecret_loaded = true;
-  // We leak the library handle. That's OK: this function is called only once.
-  return true;
-}
-
 namespace {
 
 const char kLibsecretAppString[] = "chrome";
@@ -215,48 +154,6 @@
   return form;
 }
 
-class LibsecretAttributesBuilder {
- public:
-  LibsecretAttributesBuilder();
-  ~LibsecretAttributesBuilder();
-  void Append(const std::string& name, const std::string& value);
-  void Append(const std::string& name, int64_t value);
-  // GHashTable, its keys and values returned from Get() are destroyed in
-  // |LibsecretAttributesBuilder| desctructor.
-  GHashTable* Get() { return attrs_; }
-
- private:
-  // |name_values_| is a storage for strings referenced in |attrs_|.
-  std::list<std::string> name_values_;
-  GHashTable* attrs_;
-};
-
-LibsecretAttributesBuilder::LibsecretAttributesBuilder() {
-  attrs_ = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                 nullptr,   // no deleter for keys
-                                 nullptr);  // no deleter for values
-}
-
-LibsecretAttributesBuilder::~LibsecretAttributesBuilder() {
-  g_hash_table_destroy(attrs_);
-}
-
-void LibsecretAttributesBuilder::Append(const std::string& name,
-                                        const std::string& value) {
-  name_values_.push_back(name);
-  gpointer name_str =
-      static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
-  name_values_.push_back(value);
-  gpointer value_str =
-      static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
-  g_hash_table_insert(attrs_, name_str, value_str);
-}
-
-void LibsecretAttributesBuilder::Append(const std::string& name,
-                                        int64_t value) {
-  Append(name, base::Int64ToString(value));
-}
-
 // Generates a profile-specific app string based on profile_id_.
 std::string GetProfileSpecificAppString(LocalProfileId id) {
   // Originally, the application string was always just "chrome" and used only
@@ -267,30 +164,6 @@
 
 }  // namespace
 
-bool LibsecretLoader::LibsecretIsAvailable() {
-  if (!libsecret_loaded)
-    return false;
-  // A dummy query is made to check for availability, because libsecret doesn't
-  // have a dedicated availability function. For performance reasons, the query
-  // is meant to return an empty result.
-  LibsecretAttributesBuilder attrs;
-  attrs.Append("application", "chrome-string_to_get_empty_result");
-
-  GError* error = nullptr;
-  GList* found = secret_service_search_sync(nullptr,  // default secret service
-                                            &kLibsecretSchema, attrs.Get(),
-                                            SECRET_SEARCH_ALL,
-                                            nullptr,  // no cancellable ojbect
-                                            &error);
-  bool success = (error == nullptr);
-  if (error)
-    g_error_free(error);
-  if (found)
-    g_list_free(found);
-
-  return success;
-}
-
 NativeBackendLibsecret::NativeBackendLibsecret(LocalProfileId id)
     : app_string_(GetProfileSpecificAppString(id)) {
 }
@@ -299,7 +172,7 @@
 }
 
 bool NativeBackendLibsecret::Init() {
-  return LoadLibsecret() && LibsecretIsAvailable();
+  return LibsecretLoader::EnsureLibsecretLoaded();
 }
 
 password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin(
@@ -373,14 +246,14 @@
     password_manager::PasswordStoreChangeList* changes) {
   DCHECK(changes);
   GError* error = nullptr;
-  if (secret_password_clear_sync(
-          &kLibsecretSchema, nullptr, &error,
-          "origin_url", form.origin.spec().c_str(),
-          "username_element", UTF16ToUTF8(form.username_element).c_str(),
-          "username_value", UTF16ToUTF8(form.username_value).c_str(),
-          "password_element", UTF16ToUTF8(form.password_element).c_str(),
-          "signon_realm", form.signon_realm.c_str(),
-          "application", app_string_.c_str(), nullptr)) {
+  if (LibsecretLoader::secret_password_clear_sync(
+          &kLibsecretSchema, nullptr, &error, "origin_url",
+          form.origin.spec().c_str(), "username_element",
+          UTF16ToUTF8(form.username_element).c_str(), "username_value",
+          UTF16ToUTF8(form.username_value).c_str(), "password_element",
+          UTF16ToUTF8(form.password_element).c_str(), "signon_realm",
+          form.signon_realm.c_str(), "application", app_string_.c_str(),
+          nullptr)) {
     changes->push_back(password_manager::PasswordStoreChange(
         password_manager::PasswordStoreChange::REMOVE, form));
   }
@@ -443,11 +316,11 @@
   attrs.Append("application", app_string_);
 
   GError* error = nullptr;
-  GList* found = secret_service_search_sync(nullptr,  // default secret service
-                                            &kLibsecretSchema, attrs.Get(),
-                                            SECRET_SEARCH_ALL,
-                                            nullptr,  // no cancellable ojbect
-                                            &error);
+  GList* found = LibsecretLoader::secret_service_search_sync(
+      nullptr,  // default secret service
+      &kLibsecretSchema, attrs.Get(), SECRET_SEARCH_ALL,
+      nullptr,  // no cancellable ojbect
+      &error);
   if (error) {
     LOG(ERROR) << "Unable to get logins " << error->message;
     g_error_free(error);
@@ -471,7 +344,7 @@
   SerializeFormDataToBase64String(form.form_data, &form_data);
   GError* error = nullptr;
   // clang-format off
-  secret_password_store_sync(
+  LibsecretLoader::secret_password_store_sync(
       &kLibsecretSchema,
       nullptr,                     // Default collection.
       form.origin.spec().c_str(),  // Display name.
@@ -546,11 +419,11 @@
     attrs.Append("signon_realm", lookup_form->signon_realm);
 
   GError* error = nullptr;
-  GList* found = secret_service_search_sync(nullptr,  // default secret service
-                                            &kLibsecretSchema, attrs.Get(),
-                                            SECRET_SEARCH_ALL,
-                                            nullptr,  // no cancellable ojbect
-                                            &error);
+  GList* found = LibsecretLoader::secret_service_search_sync(
+      nullptr,  // default secret service
+      &kLibsecretSchema, attrs.Get(), SECRET_SEARCH_ALL,
+      nullptr,  // no cancellable ojbect
+      &error);
   if (error) {
     LOG(ERROR) << "Unable to get logins " << error->message;
     g_error_free(error);
@@ -646,7 +519,7 @@
       error = nullptr;
       continue;
     }
-    GHashTable* attrs = secret_item_get_attributes(secretItem);
+    GHashTable* attrs = LibsecretLoader::secret_item_get_attributes(secretItem);
     std::unique_ptr<PasswordForm> form(FormOutOfAttributes(attrs));
     g_hash_table_unref(attrs);
     if (form) {
@@ -667,10 +540,12 @@
           continue;
         }
       }
-      SecretValue* secretValue = secret_item_get_secret(secretItem);
+      SecretValue* secretValue =
+          LibsecretLoader::secret_item_get_secret(secretItem);
       if (secretValue) {
-        form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue));
-        secret_value_unref(secretValue);
+        form->password_value =
+            UTF8ToUTF16(LibsecretLoader::secret_value_get_text(secretValue));
+        LibsecretLoader::secret_value_unref(secretValue);
       } else {
         LOG(WARNING) << "Unable to access password from list element!";
       }
diff --git a/chrome/browser/password_manager/native_backend_libsecret.h b/chrome/browser/password_manager/native_backend_libsecret.h
index f53768c8..b50b276f 100644
--- a/chrome/browser/password_manager/native_backend_libsecret.h
+++ b/chrome/browser/password_manager/native_backend_libsecret.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_LIBSECRET_H_
 #define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_LIBSECRET_H_
 
-#include <libsecret/secret.h>
-
 #include <string>
 
 #include "base/compiler_specific.h"
@@ -16,39 +14,13 @@
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/password_manager/password_store_x.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/os_crypt/libsecret_util_posix.h"
 
 namespace autofill {
 struct PasswordForm;
 }
 
-class LibsecretLoader {
- public:
-  static decltype(&::secret_password_store_sync) secret_password_store_sync;
-  static decltype(&::secret_service_search_sync) secret_service_search_sync;
-  static decltype(&::secret_password_clear_sync) secret_password_clear_sync;
-  static decltype(&::secret_item_get_secret) secret_item_get_secret;
-  static decltype(&::secret_value_get_text) secret_value_get_text;
-  static decltype(&::secret_item_get_attributes) secret_item_get_attributes;
-  static decltype(&::secret_item_load_secret_sync) secret_item_load_secret_sync;
-  static decltype(&::secret_value_unref) secret_value_unref;
-
- protected:
-  static bool LoadLibsecret();
-  static bool LibsecretIsAvailable();
-
-  static bool libsecret_loaded;
-
- private:
-  struct FunctionInfo {
-    const char* name;
-    void** pointer;
-  };
-
-  static const FunctionInfo functions[];
-};
-
-class NativeBackendLibsecret : public PasswordStoreX::NativeBackend,
-                               public LibsecretLoader {
+class NativeBackendLibsecret : public PasswordStoreX::NativeBackend {
  public:
   explicit NativeBackendLibsecret(LocalProfileId id);
 
diff --git a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
index d032803..02f65bd 100644
--- a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
+++ b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
@@ -215,7 +215,7 @@
                                    mock_secret_item_load_secret_sync;
     secret_value_unref =
         (decltype(&::secret_value_unref)) & mock_secret_value_unref;
-    libsecret_loaded = true;
+    libsecret_loaded_ = true;
     // Reset the state of the mock library.
     global_mock_libsecret_items->clear();
     global_mock_libsecret_reject_local_ids = false;
diff --git a/chrome/browser/permissions/permission_uma_util.cc b/chrome/browser/permissions/permission_uma_util.cc
index 8d38d045..19a2efa1 100644
--- a/chrome/browser/permissions/permission_uma_util.cc
+++ b/chrome/browser/permissions/permission_uma_util.cc
@@ -36,8 +36,6 @@
 
 namespace {
 
-// Deprecated. This method is used for the single-dimensional RAPPOR metrics
-// that are being replaced by the multi-dimensional ones.
 const std::string GetRapporMetric(PermissionType permission,
                                   PermissionAction action) {
   std::string action_str;
@@ -144,50 +142,55 @@
                    << " not accounted for";
   }
 
-  // There are two sets of semi-redundant RAPPOR metrics being reported:
-  // The soon-to-be-deprecated single dimensional ones, and the new
-  // multi-dimensional ones.
+  // Retrieve the name of the RAPPOR metric. Currently, the new metric name is
+  // the deprecated name with "2" on the end, e.g.
+  // ContentSettings.PermissionActions_Geolocation.Granted.Url2. For simplicity,
+  // we retrieve the deprecated name and append the "2" for the new name.
+  // TODO(dominickn): remove the deprecated metric and replace it solely with
+  // the new one in GetRapporMetric - crbug.com/605836.
+  const std::string deprecated_metric = GetRapporMetric(permission, action);
   rappor::RapporService* rappor_service = g_browser_process->rappor_service();
-  const std::string rappor_metric = GetRapporMetric(permission, action);
-  if (!rappor_metric.empty())
-    rappor::SampleDomainAndRegistryFromGURL(
-        rappor_service, rappor_metric, requesting_origin);
+  if (!deprecated_metric.empty() && rappor_service) {
+    rappor::SampleDomainAndRegistryFromGURL(rappor_service, deprecated_metric,
+                                            requesting_origin);
 
-  // Add multi-dimensional RAPPOR reporting for safe-browsing users.
-  std::string permission_str =
-      PermissionUtil::GetPermissionString(permission);
-  if (!rappor_service || permission_str.empty())
-    return;
-
-  std::unique_ptr<rappor::Sample> sample =
-      rappor_service->CreateSample(rappor::SAFEBROWSING_RAPPOR_TYPE);
-  sample->SetStringField("Scheme", requesting_origin.scheme());
-  sample->SetStringField("Host", requesting_origin.host());
-  sample->SetStringField("Port", requesting_origin.port());
-  sample->SetStringField("Domain",
-      rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
-  sample->SetFlagsField("Actions", static_cast<uint64_t>(1) << action,
-                        PermissionAction::PERMISSION_ACTION_NUM);
-  rappor_service->RecordSampleObj("Permissions.Action." + permission_str,
-                                  std::move(sample));
+    std::string rappor_metric = deprecated_metric + "2";
+    rappor_service->RecordSample(
+        rappor_metric, rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
+        rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
+  }
 }
 
 void RecordPermissionRequest(PermissionType permission,
                              const GURL& requesting_origin,
                              const GURL& embedding_origin,
                              Profile* profile) {
-  bool secure_origin = content::IsOriginSecure(requesting_origin);
-  if (permission == PermissionType::GEOLOCATION) {
+  rappor::RapporService* rappor_service = g_browser_process->rappor_service();
+  if (rappor_service) {
+    if (permission == PermissionType::GEOLOCATION) {
+      // TODO(dominickn): remove this deprecated metric - crbug.com/605836.
       rappor::SampleDomainAndRegistryFromGURL(
-          g_browser_process->rappor_service(),
+          rappor_service,
           "ContentSettings.PermissionRequested.Geolocation.Url",
           requesting_origin);
-  } else if (permission == PermissionType::NOTIFICATIONS) {
+      rappor_service->RecordSample(
+          "ContentSettings.PermissionRequested.Geolocation.Url2",
+          rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
+          rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
+    } else if (permission == PermissionType::NOTIFICATIONS) {
+      // TODO(dominickn): remove this deprecated metric - crbug.com/605836.
       rappor::SampleDomainAndRegistryFromGURL(
-          g_browser_process->rappor_service(),
+          rappor_service,
           "ContentSettings.PermissionRequested.Notifications.Url",
           requesting_origin);
+      rappor_service->RecordSample(
+          "ContentSettings.PermissionRequested.Notifications.Url2",
+          rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
+          rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
+    }
   }
+
+  bool secure_origin = content::IsOriginSecure(requesting_origin);
   UMA_HISTOGRAM_ENUMERATION(
       "ContentSettings.PermissionRequested",
       static_cast<base::HistogramBase::Sample>(permission),
diff --git a/chrome/browser/renderer_preferences_util.cc b/chrome/browser/renderer_preferences_util.cc
index 2fff3f3..1a1a910 100644
--- a/chrome/browser/renderer_preferences_util.cc
+++ b/chrome/browser/renderer_preferences_util.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/renderer_preferences.h"
 #include "content/public/common/webrtc_ip_handling_policy.h"
@@ -19,10 +18,6 @@
 #include "ui/gfx/font_render_params.h"
 #endif
 
-#if !defined(OS_ANDROID)
-#include "components/ui/zoom/zoom_controller.h"
-#endif
-
 #if defined(TOOLKIT_VIEWS)
 #include "ui/views/controls/textfield/textfield.h"
 #endif
@@ -62,24 +57,6 @@
   }
 #endif
 
-  double default_zoom_level = 0;
-  bool default_zoom_level_set = false;
-#if !defined(OS_ANDROID)
-  ui_zoom::ZoomController* zoom_controller =
-      ui_zoom::ZoomController::FromWebContents(web_contents);
-  if (zoom_controller) {
-    default_zoom_level = zoom_controller->GetDefaultZoomLevel();
-    default_zoom_level_set = true;
-  }
-#endif
-
-  if (!default_zoom_level_set) {
-    default_zoom_level =
-        content::HostZoomMap::Get(web_contents->GetSiteInstance())
-            ->GetDefaultZoomLevel();
-  }
-  prefs->default_zoom_level = default_zoom_level;
-
 #if defined(USE_DEFAULT_RENDER_THEME)
   prefs->focus_ring_color = SkColorSetRGB(0x4D, 0x90, 0xFE);
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/resources/PRESUBMIT.py b/chrome/browser/resources/PRESUBMIT.py
index 502d47a..6d60a3a 100644
--- a/chrome/browser/resources/PRESUBMIT.py
+++ b/chrome/browser/resources/PRESUBMIT.py
@@ -2,14 +2,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Presubmit script for HTML files in chrome/browser/resources.
+"""Presubmit script for files in chrome/browser/resources.
 
 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
 for more details about the presubmit API built into depot_tools.
 """
 
+import re
+
+
 ACTION_XML_PATH = '../../../tools/metrics/actions/actions.xml'
 
+
 def CheckUserActionUpdate(input_api, output_api, action_xml_path):
   """Checks if any new user action has been added."""
   if any('actions.xml' == input_api.os_path.basename(f) for f in
@@ -93,3 +97,26 @@
 
 def CheckChangeOnCommit(input_api, output_api):
   return CheckUserActionUpdate(input_api, output_api, ACTION_XML_PATH)
+
+
+def PostUploadHook(cl, change, output_api):
+  rietveld_obj = cl.RpcServer()
+  description = rietveld_obj.get_description(cl.issue)
+
+  existing_bots = (change.CQ_INCLUDE_TRYBOTS or '').split(';')
+  clean_bots = set(filter(None, map(lambda s: s.strip(), existing_bots)))
+  new_bots = clean_bots | set(['tryserver.chromium.linux:closure_compilation'])
+  new_tag = 'CQ_INCLUDE_TRYBOTS=%s' % ';'.join(new_bots)
+
+  if clean_bots:
+    tag_reg = '^CQ_INCLUDE_TRYBOTS=.*$'
+    new_description = re.sub(tag_reg, new_tag, description, flags=re.M | re.I)
+  else:
+    new_description = description + '\n' + new_tag
+
+  if new_description == description:
+    return []
+
+  rietveld_obj.update_description(cl.issue, new_description)
+  return [output_api.PresubmitNotifyResult(
+      'Automatically added optional Closure bots to run on CQ.')]
diff --git a/chrome/browser/resources/md_history/history.html b/chrome/browser/resources/md_history/history.html
index 2c115fa7..82ac05b 100644
--- a/chrome/browser/resources/md_history/history.html
+++ b/chrome/browser/resources/md_history/history.html
@@ -25,11 +25,7 @@
       flex: 0 0 56px;
     }
 
-    #history-list {
-      flex: 1 0 0;
-      overflow: auto;
-    }
-
+    #history-list,
     #history-synced-device-manager {
       flex: 1 0 0;
     }
diff --git a/chrome/browser/resources/md_history/history_toolbar.html b/chrome/browser/resources/md_history/history_toolbar.html
index 85bf236..27156ac 100644
--- a/chrome/browser/resources/md_history/history_toolbar.html
+++ b/chrome/browser/resources/md_history/history_toolbar.html
@@ -46,9 +46,12 @@
         flex: 1 0 var(--side-bar-width);
       }
 
-      #centered-buttons {
-        flex: 0 1 var(--card-max-width);
-        padding: 0 var(--card-padding-side);
+      #centered-content {
+        /** Padding-start gives space on one side, flex-basis gives space on the
+          other. */
+        -webkit-padding-start: var(--card-padding-side);
+        display: flex;
+        flex: 1 1 calc(var(--card-max-width) + var(--card-padding-side));
       }
 
       #right-content {
@@ -98,18 +101,18 @@
         <div id="left-content">
           <h1 id="title">$i18n{title}</h1>
         </div>
-        <div id="centered-buttons">
+        <div id="centered-content">
           <paper-button on-tap="onClearBrowsingDataTap_"
               id="clear-browsing-data-button">
             $i18n{clearBrowsingData}
           </paper-button>
-        </div>
-        <div id="right-content">
-          <paper-spinner id="searching-spinner" active="[[searching]]">
-          </paper-spinner>
-          <cr-search-field id="search-input" label="$i18n{search}"
-              clear-label="$i18n{clearSearch}">
-          </cr-search-field>
+          <div id="right-content">
+            <paper-spinner id="searching-spinner" active="[[searching]]">
+            </paper-spinner>
+            <cr-search-field id="search-input" label="$i18n{search}"
+                clear-label="$i18n{clearSearch}">
+            </cr-search-field>
+          </div>
         </div>
       </div>
 
diff --git a/chrome/browser/resources/settings/advanced_page/advanced_page.html b/chrome/browser/resources/settings/advanced_page/advanced_page.html
index 91b063f..749d79fb16 100644
--- a/chrome/browser/resources/settings/advanced_page/advanced_page.html
+++ b/chrome/browser/resources/settings/advanced_page/advanced_page.html
@@ -8,6 +8,7 @@
 <link rel="import" href="chrome://md-settings/reset_page/reset_page.html">
 <link rel="import" href="chrome://md-settings/settings_page/main_page_behavior.html">
 <link rel="import" href="chrome://md-settings/settings_page/settings_page_visibility.html">
+<link rel="import" href="chrome://md-settings/settings_page/settings_router.html">
 <link rel="import" href="chrome://md-settings/settings_page/settings_section.html">
 <link rel="import" href="chrome://md-settings/site_settings/constants.html">
 <link rel="import" href="chrome://md-settings/site_settings/site_settings_category.html">
diff --git a/chrome/browser/resources/settings/advanced_page/advanced_page.js b/chrome/browser/resources/settings/advanced_page/advanced_page.js
index 8df43773..845f8852 100644
--- a/chrome/browser/resources/settings/advanced_page/advanced_page.js
+++ b/chrome/browser/resources/settings/advanced_page/advanced_page.js
@@ -31,6 +31,7 @@
 
     /**
      * The current active route.
+     * @type {SettingsRoute}
      */
     currentRoute: {
       type: Object,
diff --git a/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp b/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp
index 5a4abab..0ec5c39 100644
--- a/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp
@@ -9,6 +9,7 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '../settings_page/compiled_resources2.gyp:main_page_behavior',
         '../settings_page/compiled_resources2.gyp:settings_page_visibility',
+        '../settings_page/compiled_resources2.gyp:settings_router',
         '../settings_page/compiled_resources2.gyp:transition_behavior',
         '../system_page/compiled_resources2.gyp:system_page',
       ],
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index cb30d70..657f1fc 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -4,6 +4,7 @@
 <link rel="import" href="chrome://md-settings/search_page/search_page.html">
 <link rel="import" href="chrome://md-settings/settings_page/main_page_behavior.html">
 <link rel="import" href="chrome://md-settings/settings_page/settings_page_visibility.html">
+<link rel="import" href="chrome://md-settings/settings_page/settings_router.html">
 <link rel="import" href="chrome://md-settings/settings_page/settings_section.html">
 <link rel="import" href="chrome://md-settings/on_startup_page/on_startup_page.html">
 <link rel="import" href="chrome://md-settings/people_page/people_page.html">
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.js b/chrome/browser/resources/settings/basic_page/basic_page.js
index 80787fc..b16ed1d 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.js
+++ b/chrome/browser/resources/settings/basic_page/basic_page.js
@@ -16,6 +16,8 @@
 Polymer({
   is: 'settings-basic-page',
 
+  behaviors: [I18nBehavior, SettingsPageVisibility, RoutableBehavior],
+
   properties: {
     /**
      * Preferences state.
@@ -27,6 +29,7 @@
 
     /**
      * The current active route.
+     * @type {SettingsRoute}
      */
     currentRoute: {
       type: Object,
@@ -59,8 +62,6 @@
     this.scroller = this.parentElement;
   },
 
-  behaviors: [I18nBehavior, SettingsPageVisibility, RoutableBehavior],
-
   onResetDone_: function() {
     this.showResetProfileBanner_ = false;
   },
diff --git a/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp b/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp
index b3013ed..c37246c2 100644
--- a/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp
@@ -10,6 +10,7 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '../settings_page/compiled_resources2.gyp:main_page_behavior',
         '../settings_page/compiled_resources2.gyp:settings_page_visibility',
+        '../settings_page/compiled_resources2.gyp:settings_router',
         '../settings_page/compiled_resources2.gyp:transition_behavior',
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index e0957cf..679a02c 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -19,7 +19,11 @@
     <style include="settings-shared">
       iron-icon {
         --iron-icon-fill-color: var(--settings-nav-grey);
-        -webkit-margin-end: 16px;
+        -webkit-margin-end: 24px;
+      }
+
+      .iron-selected:not(.menu-trigger) > iron-icon {
+        fill: var(--google-blue-500);
       }
 
       .menu-trigger span {
@@ -63,96 +67,98 @@
       }
     </style>
     <paper-menu name="root-menu">
-      <paper-submenu opened="{{basicOpened_}}" data-page="basic"
-          on-tap="openPage_">
-        <div class="menu-trigger">
+      <paper-submenu opened="{{basicOpened_}}">
+        <div class="menu-trigger" data-page="basic" data-section=""
+            on-tap="openPage_">
           <span i18n-content="basicPageTitle"></span>
           <iron-icon icon="{{arrowState_(basicOpened_)}}" item-icon></iron-icon>
         </div>
         <paper-menu class="menu-content">
 <if expr="chromeos">
-          <div>
+          <div data-page="basic" data-section="internet" on-tap="openPage_">
             <iron-icon icon="device:network-wifi" item-icon></iron-icon>
             <span i18n-content="internetPageTitle"></span>
           </div>
 </if>
-          <div>
+          <div data-page="basic" data-section="people" on-tap="openPage_">
             <iron-icon icon="social:people" item-icon></iron-icon>
             <span i18n-content="peoplePageTitle"></span>
           </div>
-          <div>
+          <div data-page="basic" data-section="appearance" on-tap="openPage_">
             <iron-icon icon="image:palette" item-icon></iron-icon>
             <span i18n-content="appearancePageTitle"></span>
           </div>
 <if expr="chromeos">
-          <div>
+          <div data-page="basic" data-section="device" on-tap="openPage_">
             <iron-icon icon="hardware:laptop-chromebook" item-icon></iron-icon>
             <span i18n-content="devicePageTitle"></span>
           </div>
 </if>
-          <div>
+          <div data-page="basic" data-section="search" on-tap="openPage_">
             <iron-icon icon="search" item-icon></iron-icon>
             <span i18n-content="searchPageTitle"></span>
           </div>
 <if expr="not chromeos">
-          <div>
+          <div data-page="basic" data-section="defaultBrowser"
+              on-tap="openPage_">
             <iron-icon icon="av:web" item-icon></iron-icon>
             <span i18n-content="defaultBrowser"></span>
           </div>
 </if>
-          <div>
+          <div data-page="basic" data-section="onStartup" on-tap="openPage_">
             <iron-icon icon="icons:power-settings-new" item-icon></iron-icon>
             <span i18n-content="onStartup"></span>
           </div>
         </paper-menu>
       </paper-submenu>
-      <paper-submenu opened="{{advancedOpened_}}" data-page="advanced"
-          on-tap="openPage_">
-        <div class="menu-trigger">
+      <paper-submenu opened="{{advancedOpened_}}">
+        <div class="menu-trigger" data-page="advanced" data-section=""
+            on-tap="openPage_">
           <span i18n-content="advancedPageTitle"></span>
           <iron-icon icon="{{arrowState_(advancedOpened_)}}" item-icon>
           </iron-icon>
         </div>
         <paper-menu class="menu-content">
 <if expr="chromeos">
-          <div>
+          <div data-page="advanced" data-section="dateTime" on-tap="openPage_">
             <iron-icon icon="device:access-time" item-icon></iron-icon>
             <span i18n-content="dateTimePageTitle"></span>
           </div>
 </if>
-          <div>
+          <div data-page="advanced" data-section="privacy" on-tap="openPage_">
             <iron-icon icon="hardware:security" item-icon></iron-icon>
             <span i18n-content="privacyPageTitle"></span>
           </div>
 <if expr="chromeos">
-          <div>
+          <div data-page="advanced" data-section="bluetooth" on-tap="openPage_">
             <iron-icon icon="device:bluetooth" item-icon></iron-icon>
             <span i18n-content="bluetoothPageTitle"></span>
           </div>
 </if>
-          <div>
+          <div data-page="advanced" data-section="passwordsAndForms"
+              on-tap="openPage_">
             <iron-icon icon="icons:assignment" item-icon></iron-icon>
             <span i18n-content="passwordsAndAutofillPageTitle"></span>
           </div>
-          <div>
+          <div data-page="advanced" data-section="languages" on-tap="openPage_">
             <iron-icon icon="icons:language" item-icon></iron-icon>
             <span i18n-content="languagesPageTitle"></span>
           </div>
-          <div>
+          <div data-page="advanced" data-section="downloads" on-tap="openPage_">
             <iron-icon icon="icons:file-download" item-icon></iron-icon>
             <span i18n-content="downloadsPageTitle"></span>
           </div>
-          <div>
+          <div data-page="advanced" data-section="a11y" on-tap="openPage_">
             <iron-icon icon="icons:accessibility" item-icon></iron-icon>
             <span i18n-content="a11yPageTitle"></span>
           </div>
 <if expr="not chromeos">
-          <div>
+          <div data-page="advanced" data-section="system" on-tap="openPage_">
             <iron-icon icon="icons:build" item-icon></iron-icon>
             <span i18n-content="systemPageTitle"></span>
           </div>
 </if>
-          <div>
+          <div data-page="advanced" data-section="reset" on-tap="openPage_">
             <iron-icon icon="icons:restore" item-icon></iron-icon>
             <span i18n-content="resetPageTitle"></span>
           </div>
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.js b/chrome/browser/resources/settings/settings_menu/settings_menu.js
index d701db5..45ddc5f 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.js
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.js
@@ -45,7 +45,7 @@
     if (submenuRoute) {
       this.currentRoute = {
         page: submenuRoute,
-        section: '',
+        section: event.currentTarget.dataset.section,
         subpage: [],
       };
     }
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index 99dbc67..f00f038dd 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -309,12 +309,14 @@
   },
 };
 
+
 /** @polymerBehavior */
 var MainPageBehavior = [
   TransitionBehavior,
   MainPageBehaviorImpl
 ];
 
+
 /**
  * TODO(michaelpg): integrate slide animations.
  * @polymerBehavior RoutableBehavior
@@ -329,13 +331,23 @@
     },
   },
 
+  /**
+   * @param {string} section Name of the item to scroll into view.
+   */
+  scrollToSection: function(section) {
+    if (section) {
+      // TODO(dschuyler): Determine whether this setTimeout can be removed.
+      // See also: https://github.com/Polymer/polymer/issues/3629
+      setTimeout(function() {
+        this.getSection_(section).scrollIntoView();
+      }.bind(this));
+    }
+  },
+
   /** @private */
   currentRouteChanged_: function(newRoute, oldRoute) {
-    // route.section is only non-empty when the user is within a subpage.
-    // When the user is not in a subpage, but on the Basic page, route.section
-    // is an empty string.
-    var newRouteIsSubpage = newRoute && newRoute.section;
-    var oldRouteIsSubpage = oldRoute && oldRoute.section;
+    var newRouteIsSubpage = newRoute && newRoute.subpage.length;
+    var oldRouteIsSubpage = oldRoute && oldRoute.subpage.length;
 
     if (!oldRoute && newRouteIsSubpage) {
       // Allow the page to load before expanding the section. TODO(michaelpg):
@@ -357,6 +369,8 @@
       var section = this.getSection_(newRoute.section);
       if (section)
         this.expandSection(section);
+    } else if (newRoute && newRoute.section) {
+      this.scrollToSection(newRoute.section);
     }
   },
 
@@ -372,6 +386,7 @@
   },
 };
 
+
 /** @polymerBehavior */
 var RoutableBehavior = [
   MainPageBehavior,
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
index fc205a6d..08a561a 100644
--- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
+++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
@@ -88,11 +88,8 @@
       return;
     }
 
-    // route.section is only non-empty when the user is within a subpage.
-    // When the user is not in a subpage, but on the Basic page, route.section
-    // is an empty string.
-    var newRouteIsSubpage = newRoute && newRoute.section == this.section;
-    var oldRouteIsSubpage = oldRoute && oldRoute.section == this.section;
+    var newRouteIsSubpage = newRoute && newRoute.subpage.length;
+    var oldRouteIsSubpage = oldRoute && oldRoute.subpage.length;
 
     if (newRouteIsSubpage)
       this.ensureSubpageInstance_();
diff --git a/chrome/browser/resources/settings/settings_page/settings_router.js b/chrome/browser/resources/settings/settings_page/settings_router.js
index 70e6f517..8919d5cf 100644
--- a/chrome/browser/resources/settings/settings_page/settings_router.js
+++ b/chrome/browser/resources/settings/settings_page/settings_router.js
@@ -3,6 +3,16 @@
 // found in the LICENSE file.
 
 /**
+ * @typedef {{
+ *   url: string,
+ *   page: string,
+ *   section: string,
+ *   subpage: !Array<string>,
+ * }}
+ */
+var SettingsRoute;
+
+/**
  * @fileoverview
  * 'settings-router' is a simple router for settings. Its responsibilities:
  *  - Update the URL when the routing state changes.
@@ -31,6 +41,7 @@
      * the user is on. The previous elements are the ancestor subpages. This
      * enables support for multiple paths to the same subpage. This is used by
      * both the Back button and the Breadcrumb to determine ancestor subpages.
+     * @type {SettingsRoute}
      */
     currentRoute: {
       notify: true,
@@ -43,6 +54,7 @@
           var route = this.routes_[i];
           if (route.url == window.location.pathname) {
             return {
+              url: route.url,
               page: route.page,
               section: route.section,
               subpage: route.subpage,
@@ -72,11 +84,11 @@
   },
 
 
- /**
-  * @private
-  * The 'url' property is not accessible to other elements.
-  */
- routes_: [
+  /**
+   * @private {!Array<!SettingsRoute>}
+   * The 'url' property is not accessible to other elements.
+   */
+  routes_: [
     {
       url: '/',
       page: 'basic',
@@ -91,6 +103,12 @@
     },
 <if expr="chromeos">
     {
+      url: '/internet',
+      page: 'basic',
+      section: 'internet',
+      subpage: [],
+    },
+    {
       url: '/networkDetail',
       page: 'basic',
       section: 'internet',
@@ -104,22 +122,46 @@
     },
 </if>
     {
+      url: '/appearance',
+      page: 'basic',
+      section: 'appearance',
+      subpage: [],
+    },
+    {
       url: '/fonts',
       page: 'basic',
       section: 'appearance',
       subpage: ['appearance-fonts'],
     },
     {
+      url: '/defaultBrowser',
+      page: 'basic',
+      section: 'defaultBrowser',
+      subpage: [],
+    },
+    {
+      url: '/search',
+      page: 'basic',
+      section: 'search',
+      subpage: [],
+    },
+    {
       url: '/searchEngines',
       page: 'basic',
       section: 'search',
       subpage: ['search-engines'],
     },
     {
-      url: '/searchEngines/advanced',
+      url: '/onStartup',
       page: 'basic',
-      section: 'search',
-      subpage: ['search-engines', 'search-engines-advanced'],
+      section: 'onStartup',
+      subpage: [],
+    },
+    {
+      url: '/people',
+      page: 'basic',
+      section: 'people',
+      subpage: [],
     },
 <if expr="chromeos">
     {
@@ -152,6 +194,12 @@
     },
 </if>
     {
+      url: '/advanced',
+      page: 'advanced',
+      section: 'privacy',
+      subpage: [],
+    },
+    {
       url: '/certificates',
       page: 'advanced',
       section: 'privacy',
@@ -302,6 +350,12 @@
     },
 <if expr="chromeos">
     {
+      url: '/bluetooth',
+      page: 'advanced',
+      section: 'bluetooth',
+      subpage: [],
+    },
+    {
       url: '/bluetoothAddDevice',
       page: 'advanced',
       section: 'bluetooth',
@@ -321,12 +375,24 @@
       subpage: ['manage-autofill'],
     },
     {
+      url: '/pw',  // TODO(dschuyler): find a better url.
+      page: 'advanced',
+      section: 'passwordsAndForms',
+      subpage: [],
+    },
+    {
       url: '/passwords',
       page: 'advanced',
       section: 'passwordsAndForms',
       subpage: ['manage-passwords'],
     },
     {
+      url: '/ln',  // TODO(dschuyler): find a better url.
+      page: 'advanced',
+      section: 'languages',
+      subpage: [],
+    },
+    {
       url: '/languages',
       page: 'advanced',
       section: 'languages',
@@ -354,8 +420,38 @@
       subpage: ['edit-dictionary'],
     },
 </if>
+    {
+      url: '/downloadsDirectory',
+      page: 'advanced',
+      section: 'downloads',
+      subpage: [],
+    },
+    {
+      url: '/accessibility',
+      page: 'advanced',
+      section: 'a11y',
+      subpage: [],
+    },
+    {
+      url: '/system',
+      page: 'advanced',
+      section: 'system',
+      subpage: [],
+    },
+    {
+      url: '/reset',
+      page: 'advanced',
+      section: 'reset',
+      subpage: [],
+    },
 <if expr="chromeos">
     {
+      url: '/device',
+      page: 'basic',
+      section: 'device',
+      subpage: [],
+    },
+    {
       url: '/pointer-overlay',
       page: 'basic',
       section: 'device',
@@ -387,10 +483,12 @@
   },
 
   /**
-   * @private
    * Is called when another element modifies the route. This observer validates
    * the route change against the pre-defined list of routes, and updates the
    * URL appropriately.
+   * @param {!SettingsRoute} newRoute Where we're headed.
+   * @param {!SettingsRoute|undefined} oldRoute Where we've been.
+   * @private
    */
   currentRouteChanged_: function(newRoute, oldRoute) {
     for (var i = 0; i < this.routes_.length; ++i) {
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
index 67eb6ae..101fa5b9 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
@@ -179,7 +179,11 @@
   reporting_info.metric_prefix = GetMetricPrefix();
   reporting_info.extra_suffix = GetExtraMetricsSuffix();
   reporting_info.rappor_prefix = GetRapporPrefix();
-  reporting_info.rappor_report_type = rappor::SAFEBROWSING_RAPPOR_TYPE;
+  reporting_info.deprecated_rappor_prefix = GetDeprecatedRapporPrefix();
+  reporting_info.rappor_report_type =
+      rappor::LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE;
+  reporting_info.deprecated_rappor_report_type =
+      rappor::SAFEBROWSING_RAPPOR_TYPE;
   set_metrics_helper(base::WrapUnique(new ChromeMetricsHelper(
       web_contents, request_url(), reporting_info, GetSamplingEventName())));
   metrics_helper()->RecordUserDecision(
@@ -568,6 +572,19 @@
 std::string SafeBrowsingBlockingPage::GetRapporPrefix() const {
   switch (interstitial_reason_) {
     case SB_REASON_MALWARE:
+      return "malware2";
+    case SB_REASON_HARMFUL:
+      return "harmful2";
+    case SB_REASON_PHISHING:
+      return "phishing2";
+  }
+  NOTREACHED();
+  return std::string();
+}
+
+std::string SafeBrowsingBlockingPage::GetDeprecatedRapporPrefix() const {
+  switch (interstitial_reason_) {
+    case SB_REASON_MALWARE:
       return "malware";
     case SB_REASON_HARMFUL:
       return "harmful";
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
index 2b7ce6c..9770c71 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
@@ -212,6 +212,7 @@
   std::string GetMetricPrefix() const;
   std::string GetExtraMetricsSuffix() const;
   std::string GetRapporPrefix() const;
+  std::string GetDeprecatedRapporPrefix() const;
   std::string GetSamplingEventName() const;
 
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPage);
diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc
index 0d27556..89b20db 100644
--- a/chrome/browser/ssl/ssl_blocking_page.cc
+++ b/chrome/browser/ssl/ssl_blocking_page.cc
@@ -61,7 +61,8 @@
 
 // Rappor prefix, which is used for both overridable and non-overridable
 // interstitials so we don't leak the "overridable" bit.
-const char kSSLRapporPrefix[] = "ssl2";
+const char kDeprecatedSSLRapporPrefix[] = "ssl2";
+const char kSSLRapporPrefix[] = "ssl3";
 
 std::string GetSamplingEventName(const bool overridable, const int cert_error) {
   std::string event_name(kEventNameBase);
@@ -142,7 +143,9 @@
   reporting_info.metric_prefix =
       overridable_ ? "ssl_overridable" : "ssl_nonoverridable";
   reporting_info.rappor_prefix = kSSLRapporPrefix;
-  reporting_info.rappor_report_type = rappor::UMA_RAPPOR_TYPE;
+  reporting_info.deprecated_rappor_prefix = kDeprecatedSSLRapporPrefix;
+  reporting_info.rappor_report_type = rappor::LOW_FREQUENCY_UMA_RAPPOR_TYPE;
+  reporting_info.deprecated_rappor_report_type = rappor::UMA_RAPPOR_TYPE;
   ChromeMetricsHelper* chrome_metrics_helper =
       new ChromeMetricsHelper(web_contents, request_url, reporting_info,
                               GetSamplingEventName(overridable_, cert_error));
diff --git a/chrome/browser/ui/app_list/app_list_service_mac.mm b/chrome/browser/ui/app_list/app_list_service_mac.mm
index 096c9ff..386b3b31 100644
--- a/chrome/browser/ui/app_list/app_list_service_mac.mm
+++ b/chrome/browser/ui/app_list/app_list_service_mac.mm
@@ -49,8 +49,8 @@
 #include "ui/app_list/search_box_model.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/display.h"
-#include "ui/gfx/screen.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 
 namespace gfx {
 class ImageSkia;
@@ -179,7 +179,7 @@
 
 // Determines which screen edge the dock is aligned to.
 AppListPositioner::ScreenEdge DockLocationInDisplay(
-    const gfx::Display& display) {
+    const display::Display& display) {
   // Assume the dock occupies part of the work area either on the left, right or
   // bottom of the display. Note in the autohide case, it is always 4 pixels.
   const gfx::Rect work_area = display.work_area();
@@ -199,7 +199,7 @@
 // If |display|'s work area is too close to its boundary on |dock_edge|, adjust
 // the work area away from the edge by a constant amount to reduce overlap and
 // ensure the dock icon can still be clicked to dismiss the app list.
-void AdjustWorkAreaForDock(const gfx::Display& display,
+void AdjustWorkAreaForDock(const display::Display& display,
                            AppListPositioner* positioner,
                            AppListPositioner::ScreenEdge dock_edge) {
   const int kAutohideDockThreshold = 10;
@@ -232,10 +232,10 @@
 
 void GetAppListWindowOrigins(
     NSWindow* window, NSPoint* target_origin, NSPoint* start_origin) {
-  gfx::Screen* const screen = gfx::Screen::GetScreen();
+  display::Screen* const screen = display::Screen::GetScreen();
   // Ensure y coordinates are flipped back into AppKit's coordinate system.
   bool cursor_is_visible = CGCursorIsVisible();
-  gfx::Display display;
+  display::Display display;
   gfx::Point cursor;
   if (!cursor_is_visible) {
     // If Chrome is the active application, display on the same display as
@@ -287,7 +287,7 @@
 
 // static
 void AppListServiceMac::FindAnchorPoint(const gfx::Size& window_size,
-                                        const gfx::Display& display,
+                                        const display::Display& display,
                                         int primary_display_height,
                                         bool cursor_is_visible,
                                         const gfx::Point& cursor,
diff --git a/chrome/browser/ui/app_list/app_list_service_mac_unittest.mm b/chrome/browser/ui/app_list/app_list_service_mac_unittest.mm
index 40c1822..978e143 100644
--- a/chrome/browser/ui/app_list/app_list_service_mac_unittest.mm
+++ b/chrome/browser/ui/app_list/app_list_service_mac_unittest.mm
@@ -6,7 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -123,7 +123,7 @@
   }
 
  private:
-  gfx::Display display_;
+  display::Display display_;
   gfx::Size window_size_;
   bool cursor_is_visible_;
   gfx::Point cursor_;
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index b72e312..4fcd54b 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -16,10 +16,12 @@
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/sys_info.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_content_browser_client.h"
@@ -105,7 +107,12 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
 #include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/x509_certificate.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/ssl/ssl_cipher_suite_names.h"
+#include "net/ssl/ssl_connection_status_flags.h"
+#include "net/test/cert_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
@@ -3064,27 +3071,121 @@
 
 namespace {
 
+// After AddNonsecureUrlHandler() is called, requests to this hostname
+// will use obsolete TLS settings.
+const char kMockNonsecureHostname[] = "example-nonsecure.test";
+
+// A URLRequestMockHTTPJob that mocks a TLS connection with an obsolete
+// protocol version.
+class URLRequestObsoleteTLSJob : public net::URLRequestMockHTTPJob {
+ public:
+  URLRequestObsoleteTLSJob(net::URLRequest* request,
+                           net::NetworkDelegate* network_delegate,
+                           const base::FilePath& file_path,
+                           scoped_refptr<net::X509Certificate> cert,
+                           scoped_refptr<base::TaskRunner> task_runner)
+      : net::URLRequestMockHTTPJob(request,
+                                   network_delegate,
+                                   file_path,
+                                   task_runner),
+        cert_(std::move(cert)) {}
+
+  void GetResponseInfo(net::HttpResponseInfo* info) override {
+    net::URLRequestMockHTTPJob::GetResponseInfo(info);
+    net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_1,
+                                       &info->ssl_info.connection_status);
+    const uint16_t kTlsEcdheRsaWithAes128CbcSha = 0xc013;
+    net::SSLConnectionStatusSetCipherSuite(kTlsEcdheRsaWithAes128CbcSha,
+                                           &info->ssl_info.connection_status);
+    info->ssl_info.cert = cert_;
+  }
+
+ protected:
+  ~URLRequestObsoleteTLSJob() override {}
+
+ private:
+  const scoped_refptr<net::X509Certificate> cert_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestObsoleteTLSJob);
+};
+
+// A URLRequestInterceptor that handles requests with
+// URLRequestObsoleteTLSJob jobs.
+class URLRequestNonsecureInterceptor : public net::URLRequestInterceptor {
+ public:
+  URLRequestNonsecureInterceptor(
+      const base::FilePath& base_path,
+      scoped_refptr<base::SequencedWorkerPool> worker_pool,
+      scoped_refptr<net::X509Certificate> cert)
+      : base_path_(base_path),
+        worker_pool_(std::move(worker_pool)),
+        cert_(std::move(cert)) {}
+
+  ~URLRequestNonsecureInterceptor() override {}
+
+  // net::URLRequestInterceptor:
+  net::URLRequestJob* MaybeInterceptRequest(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const override {
+    return new URLRequestObsoleteTLSJob(
+        request, network_delegate, base_path_, cert_,
+        worker_pool_->GetTaskRunnerWithShutdownBehavior(
+            base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+  }
+
+ private:
+  const base::FilePath base_path_;
+  const scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+  const scoped_refptr<net::X509Certificate> cert_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestNonsecureInterceptor);
+};
+
+// Installs a handler to serve HTTPS requests to
+// |kMockNonsecureHostname| with connections that have obsolete TLS
+// settings.
+void AddNonsecureUrlHandler(
+    const base::FilePath& base_path,
+    scoped_refptr<net::X509Certificate> cert,
+    scoped_refptr<base::SequencedWorkerPool> worker_pool) {
+  net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
+  filter->AddHostnameInterceptor(
+      "https", kMockNonsecureHostname,
+      std::unique_ptr<net::URLRequestInterceptor>(
+          new URLRequestNonsecureInterceptor(base_path, worker_pool, cert)));
+}
+
 class BrowserTestNonsecureURLRequest : public BrowserTest {
  public:
-  BrowserTestNonsecureURLRequest() : BrowserTest() {}
+  BrowserTestNonsecureURLRequest() : BrowserTest(), cert_(nullptr) {}
+
+  void SetUpInProcessBrowserTestFixture() override {
+    cert_ =
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
+    ASSERT_TRUE(cert_);
+  }
+
   void SetUpOnMainThread() override {
-    base::FilePath root_http;
-    PathService::Get(chrome::DIR_TEST_DATA, &root_http);
+    base::FilePath serve_file;
+    PathService::Get(chrome::DIR_TEST_DATA, &serve_file);
+    serve_file = serve_file.Append(FILE_PATH_LITERAL("title1.html"));
     content::BrowserThread::PostTask(
         content::BrowserThread::IO, FROM_HERE,
         base::Bind(
-            &net::URLRequestMockHTTPJob::AddUrlHandlers, root_http,
+            &AddNonsecureUrlHandler, serve_file, cert_,
             make_scoped_refptr(content::BrowserThread::GetBlockingPool())));
   }
 
  private:
+  scoped_refptr<net::X509Certificate> cert_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserTestNonsecureURLRequest);
 };
 
 }  // namespace
 
-// Tests that a nonsecure connection does not get a secure connection
-// explanation.
+// Tests that a connection with obsolete TLS settings does not get a
+// secure connection explanation.
 IN_PROC_BROWSER_TEST_F(BrowserTestNonsecureURLRequest,
                        SecurityStyleChangedObserverNonsecureConnection) {
   content::WebContents* web_contents =
@@ -3092,7 +3193,16 @@
   SecurityStyleTestObserver observer(web_contents);
 
   ui_test_utils::NavigateToURL(
-      browser(), net::URLRequestMockHTTPJob::GetMockHttpsUrl(std::string()));
+      browser(), GURL(std::string("https://") + kMockNonsecureHostname));
+
+  // The security style of the page doesn't get downgraded for obsolete
+  // TLS settings, so it should remain at SECURITY_STYLE_AUTHENTICATED.
+  EXPECT_EQ(content::SECURITY_STYLE_AUTHENTICATED,
+            observer.latest_security_style());
+
+  // The messages explaining the security style do, however, get
+  // downgraded: SECURE_PROTOCOL_AND_CIPHERSUITE should not show up when
+  // the TLS settings are obsolete.
   for (const auto& explanation :
        observer.latest_explanations().secure_explanations) {
     EXPECT_NE(l10n_util::GetStringUTF8(IDS_SECURE_PROTOCOL_AND_CIPHERSUITE),
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 41c9db3..b1282f99 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -99,9 +99,9 @@
 #import "ui/base/cocoa/nsview_additions.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/display/screen.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 #include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
-#include "ui/gfx/screen.h"
 
 using bookmarks::BookmarkModel;
 using bookmarks::BookmarkNode;
@@ -1939,7 +1939,7 @@
   // However, if the user is using multiple monitors and turned off
   // "Separate Space in Each Display", use Immersive Fullscreen so
   // that the other monitors won't blank out.
-  gfx::Screen* screen = gfx::Screen::GetScreen();
+  display::Screen* screen = display::Screen::GetScreen();
   BOOL hasMultipleMonitors = screen && screen->GetNumDisplays() > 1;
   if (chrome::mac::SupportsSystemFullscreen() &&
       base::mac::IsOSYosemiteOrLater() &&
diff --git a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h
index 1c09fd58..8bc360f 100644
--- a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h
+++ b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h
@@ -141,10 +141,16 @@
 
   base::scoped_nsobject<NSString> suggestText_;
   base::scoped_nsobject<NSColor> suggestColor_;
+
+  base::scoped_nsobject<NSView> shadowView_;
 }
 
 @property(nonatomic) AutocompleteTextFieldObserver* observer;
 
+// Returns the color of the shadow that's drawn under the AutocompleteTextField
+// in Incognito mode in Material Design.
++ (NSColor*)shadowColor;
+
 // Convenience method to return the cell, casted appropriately.
 - (AutocompleteTextFieldCell*)cell;
 
diff --git a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.mm b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.mm
index 36b9a024..cd230fc 100644
--- a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.mm
+++ b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.mm
@@ -22,8 +22,79 @@
 
 namespace {
 const CGFloat kAnimationDuration = 0.2;
+const CGFloat kShadowInset = 3;
+const CGFloat kShadowColor = 69 / 255.;
+
 }
 
+// A view that draws a 1px shadow line beneath the autocomplete textfield.
+@interface AutocompleteTextFieldShadowView : NSView {
+ @private
+  AutocompleteTextField* textField_;  // Weak. Owns this.
+}
+// This is the designated initializer for AutocompleteTextFieldShadowView.
+- (instancetype)initWithTextField:(AutocompleteTextField*)aTextField;
+@end
+
+@interface AutocompleteTextFieldShadowView(Private)
+// Adjusts the shadow view's position whenever its AutocompleteTextField changes
+// its frame.
+- (void)adjustFrame;
+@end
+
+@implementation AutocompleteTextFieldShadowView
+
+- (instancetype)initWithTextField:(AutocompleteTextField*)aTextField {
+  if ((self = [self initWithFrame:NSZeroRect])) {
+    textField_ = aTextField;
+    [[NSNotificationCenter defaultCenter]
+         addObserver:self
+            selector:@selector(adjustFrame)
+                name:NSViewFrameDidChangeNotification
+              object:textField_];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+  [super dealloc];
+}
+
+- (void)adjustFrame {
+  if (![self window]) {
+    return;
+  }
+  // Make the shadow view 1pt tall and slightly inset from the edges of the
+  // autocomplete textfield.
+  NSRect frame = [textField_ frame];
+  frame.origin.x += kShadowInset;
+  frame.size.width -= kShadowInset * 2;
+  frame.origin.y -= 1;
+  frame.size.height = 1;
+  [self setFrame:frame];
+}
+
+- (void)viewDidMoveToWindow {
+  [self adjustFrame];
+}
+
+- (void)drawRect:(NSRect)rect {
+  // Don't draw anything on a Retina display because on Retina there's room
+  // for the shadow just beneath the autocomplete textfield path stroke. Why
+  // even add this view? If the user drags the Incognito window between Retina
+  // and non-Retina screens there would have to be logic to add and remove the
+  // view. It's easier just to always add it for Incognito mode and draw
+  // nothing into it.
+  if (![[self window] inIncognitoMode] || [self cr_lineWidth] < 1) {
+    return;
+  }
+  [[AutocompleteTextField shadowColor] set];
+  NSRectFill(rect);
+}
+
+@end
+
 @implementation AutocompleteTextField
 
 @synthesize observer = observer_;
@@ -32,8 +103,13 @@
   return [AutocompleteTextFieldCell class];
 }
 
++ (NSColor*)shadowColor {
+  return [NSColor colorWithGenericGamma22White:kShadowColor alpha:1];
+}
+
 - (void)dealloc {
   [[NSNotificationCenter defaultCenter] removeObserver:self];
+  [shadowView_ removeFromSuperview];
   [super dealloc];
 }
 
@@ -386,13 +462,15 @@
   // a custom theme.
   bool inDarkMode = [[self window] inIncognitoModeWithSystemTheme];
   [self setBackgroundColor:
-      inDarkMode ? [NSColor colorWithCalibratedWhite:115 / 255. alpha:1]
+      inDarkMode ? [NSColor colorWithGenericGamma22White:115 / 255. alpha:1]
                  : [NSColor whiteColor]];
   [self setTextColor:OmniboxViewMac::BaseTextColor(inDarkMode)];
 }
 
 - (void)viewDidMoveToWindow {
   if (![self window]) {
+    [shadowView_ removeFromSuperview];
+    shadowView_.reset();
     return;
   }
 
@@ -403,6 +481,11 @@
         [BrowserWindowController browserWindowControllerForView:self];
     [[browserWindowController toolbarController] locationBarWasAddedToWindow];
 
+    // Add a 1px shadow below the autocomplete textfield.
+    shadowView_.reset(
+        [[AutocompleteTextFieldShadowView alloc] initWithTextField:self]);
+    [[self superview] addSubview:shadowView_];
+
     [self updateColorsToMatchTheme];
   }
 
diff --git a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.mm b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.mm
index 2bc741b..c27a489 100644
--- a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.mm
+++ b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.mm
@@ -317,10 +317,6 @@
 
   // Compute the border's bezier path.
   NSRect pathRect = NSInsetRect(frame, insetSize, insetSize);
-  // In dark mode, make room for a shadow beneath the bottom edge.
-  if (inDarkMode && isModeMaterial) {
-    pathRect.size.height -= singlePixelLineWidth_;
-  }
   NSBezierPath* path =
       [NSBezierPath bezierPathWithRoundedRect:pathRect
                                       xRadius:kCornerRadius
@@ -369,7 +365,7 @@
       }
 
       // Draw a highlight beneath the top edge, and a shadow beneath the bottom
-      // edge.
+      // edge when on a Retina screen.
       {
         gfx::ScopedNSGraphicsContextSaveGState saveState;
         [NSBezierPath setDefaultLineWidth:singlePixelLineWidth_];
@@ -383,10 +379,12 @@
         [NSBezierPath strokeLineFromPoint:origin
                                   toPoint:destination];
 
-        origin.y = destination.y = NSMaxY(pathRect) + singlePixelLineWidth_;
-        [[NSColor colorWithCalibratedWhite:69 / 255. alpha:1] set];
-        [NSBezierPath strokeLineFromPoint:origin
-                                  toPoint:destination];
+        if (singlePixelLineWidth_ < 1) {
+          origin.y = destination.y = NSMaxY(pathRect) + singlePixelLineWidth_;
+          [[AutocompleteTextField shadowColor] set];
+          [NSBezierPath strokeLineFromPoint:origin
+                                    toPoint:destination];
+        }
       }
     }
   } else {
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h
index dd405a88..239cef42 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h
@@ -8,6 +8,8 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/macros.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/vector_icons_public.h"
 
 // Base class for decorations at the left and right of the location
 // bar.  For instance, the location icon.
@@ -88,6 +90,9 @@
   // The return value is in the same coordinate system as |frame|.
   virtual NSPoint GetBubblePointInFrame(NSRect frame);
 
+  // Gets the Material Design vector-based icon.
+  NSImage* GetMaterialIcon(bool location_bar_is_dark) const;
+
   static void DrawLabel(NSString* label,
                         NSDictionary* attributes,
                         const NSRect& frame);
@@ -100,6 +105,17 @@
   // omitted for this width;
   static const CGFloat kOmittedWidth;
 
+ protected:
+  // Gets the color used to draw the Material Design icon. The default
+  // implementation satisfies most cases - few subclasses should need to
+  // override.
+  virtual SkColor GetMaterialIconColor(bool location_bar_is_dark) const;
+
+  // Gets the id of the decoration's Material Design vector icon. Subclasses
+  // should override to return the correct id. Not an abstract method because
+  // some decorations are assigned their icon (vs. creating it themselves).
+  virtual gfx::VectorIconId GetMaterialVectorIconId() const;
+
  private:
   bool visible_;
 
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
index 58f7f79..e5f4677 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
@@ -7,7 +7,10 @@
 #include "base/logging.h"
 #include "base/mac/scoped_nsobject.h"
 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/font.h"
+#include "ui/gfx/image/image_skia_util_mac.h"
+#include "ui/gfx/paint_vector_icon.h"
 
 const CGFloat LocationBarDecoration::kOmittedWidth = 0.0;
 
@@ -80,6 +83,23 @@
   return frame.origin;
 }
 
+NSImage* LocationBarDecoration::GetMaterialIcon(
+    bool location_bar_is_dark) const {
+  const int kIconSize = 16;
+  gfx::VectorIconId vector_icon_id = GetMaterialVectorIconId();
+  if (vector_icon_id == gfx::VectorIconId::VECTOR_ICON_NONE) {
+    // Return an empty image when the decoration specifies no vector icon, so
+    // that its bubble is positioned correctly (the position is based on the
+    // width of the image; returning nil will mess up the positioning).
+    NSSize icon_size = NSMakeSize(kIconSize, kIconSize);
+    return [[[NSImage alloc] initWithSize:icon_size] autorelease];
+  }
+
+  SkColor vector_icon_color = GetMaterialIconColor(location_bar_is_dark);
+  return NSImageFromImageSkia(
+      gfx::CreateVectorIcon(vector_icon_id, kIconSize, vector_icon_color));
+}
+
 // static
 void LocationBarDecoration::DrawLabel(NSString* label,
                                       NSDictionary* attributes,
@@ -103,3 +123,14 @@
                                            NSDictionary* attributes) {
   return [label sizeWithAttributes:attributes];
 }
+
+SkColor LocationBarDecoration::GetMaterialIconColor(
+    bool location_bar_is_dark) const {
+  return location_bar_is_dark ? SkColorSetA(SK_ColorWHITE, 0xCC)
+                              : gfx::kChromeIconGrey;
+}
+
+gfx::VectorIconId LocationBarDecoration::GetMaterialVectorIconId() const {
+  NOTREACHED();
+  return gfx::VectorIconId::VECTOR_ICON_NONE;
+}
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
index 18c595c..eb64a73e 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
@@ -615,16 +615,15 @@
 }
 
 void LocationBarViewMac::OnChanged() {
-  NSImage* image = nil;
-  if (ui::MaterialDesignController::IsModeMaterial()) {
-    UpdateLocationIcon();
-  } else {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
     const int resource_id = omnibox_view_->GetIcon();
-    image = OmniboxViewMac::ImageForResource(resource_id);
+    NSImage* image = OmniboxViewMac::ImageForResource(resource_id);
     location_icon_decoration_->SetImage(image);
     ev_bubble_decoration_->SetImage(image);
     Layout();
+    return;
   }
+  UpdateLocationIcon();
 }
 
 void LocationBarViewMac::OnSetFocus() {
diff --git a/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.h b/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.h
index d1b01e3..8acc0d1 100644
--- a/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.h
@@ -57,6 +57,10 @@
   // Accessor for the platform-independent interface.
   ManagePasswordsIconCocoa* icon() { return icon_.get(); }
 
+ protected:
+  // Overridden from LocationBarDecoration:
+  gfx::VectorIconId GetMaterialVectorIconId() const override;
+
  private:
   // Triggers a redraw after a state change.
   void OnChange();
diff --git a/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.mm b/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.mm
index 86b9b97..aa520b21 100644
--- a/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.mm
@@ -90,20 +90,11 @@
   SetVisible(true);
   if (!ui::MaterialDesignController::IsModeMaterial()) {
     SetImage(OmniboxViewMac::ImageForResource(icon_->icon_id()));
-  } else {
-    int resource_id = icon_->icon_id();
-    bool locationBarIsDark =
-        [[location_bar_->GetAutocompleteTextField() window]
-            inIncognitoModeWithSystemTheme];
-    SkColor vectorIconColor = gfx::kGoogleBlue700;
-    if (resource_id != IDR_SAVE_PASSWORD_ACTIVE) {
-      vectorIconColor = locationBarIsDark ? SK_ColorWHITE
-                                          : gfx::kChromeIconGrey;
-    }
-    NSImage* theImage = NSImageFromImageSkia(gfx::CreateVectorIcon(
-        gfx::VectorIconId::AUTOLOGIN, 16, vectorIconColor));
-    SetImage(theImage);
+    return;
   }
+  bool location_bar_is_dark = [[location_bar_->GetAutocompleteTextField()
+                                    window] inIncognitoModeWithSystemTheme];
+  SetImage(GetMaterialIcon(location_bar_is_dark));
 }
 
 void ManagePasswordsDecoration::UpdateVisibleUI() {
@@ -115,3 +106,7 @@
   if (icon()->active() && ManagePasswordsBubbleCocoa::instance())
     ManagePasswordsBubbleCocoa::instance()->Close();
 }
+
+gfx::VectorIconId ManagePasswordsDecoration::GetMaterialVectorIconId() const {
+  return gfx::VectorIconId::AUTOLOGIN;
+}
diff --git a/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm b/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm
index b682a19..1a797fe 100644
--- a/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm
@@ -13,18 +13,15 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/vector_icons_public.h"
 
 SelectedKeywordDecoration::SelectedKeywordDecoration() {
   if (!ui::MaterialDesignController::IsModeMaterial()) {
     search_image_.reset([OmniboxViewMac::ImageForResource(
         IDR_KEYWORD_SEARCH_MAGNIFIER) retain]);
     SetTextColor([NSColor blackColor]);
-  } else {
-    SetTextColor(GetBackgroundBorderColor());
+    return;
   }
+  SetTextColor(GetBackgroundBorderColor());
 }
 
 SelectedKeywordDecoration::~SelectedKeywordDecoration() {}
diff --git a/chrome/browser/ui/cocoa/location_bar/star_decoration.h b/chrome/browser/ui/cocoa/location_bar/star_decoration.h
index c6cf928bc..f63ee2a 100644
--- a/chrome/browser/ui/cocoa/location_bar/star_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/star_decoration.h
@@ -31,6 +31,11 @@
   NSString* GetToolTip() override;
   NSPoint GetBubblePointInFrame(NSRect frame) override;
 
+ protected:
+  // Overridden from LocationBarDecoration:
+  SkColor GetMaterialIconColor(bool location_bar_is_dark) const override;
+  gfx::VectorIconId GetMaterialVectorIconId() const override;
+
  private:
   // For bringing up bookmark bar.
   CommandUpdater* command_updater_;  // Weak, owned by Browser.
diff --git a/chrome/browser/ui/cocoa/location_bar/star_decoration.mm b/chrome/browser/ui/cocoa/location_bar/star_decoration.mm
index 08ee25e..5c677b7 100644
--- a/chrome/browser/ui/cocoa/location_bar/star_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/star_decoration.mm
@@ -14,9 +14,6 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/vector_icons_public.h"
 
 namespace {
 
@@ -37,29 +34,16 @@
 StarDecoration::~StarDecoration() {
 }
 
-void StarDecoration::SetStarred(bool starred, bool locationBarIsDark) {
+void StarDecoration::SetStarred(bool starred, bool location_bar_is_dark) {
   starred_ = starred;
-  const int image_id = starred ? IDR_STAR_LIT : IDR_STAR;
   const int tip_id = starred ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR;
-  if (ui::MaterialDesignController::IsModeMaterial()) {
-    NSImage* theImage;
-    gfx::VectorIconId iconId = starred_ ?
-        gfx::VectorIconId::LOCATION_BAR_STAR_ACTIVE :
-        gfx::VectorIconId::LOCATION_BAR_STAR;
-    SkColor starColor = gfx::kPlaceholderColor;
-    if (locationBarIsDark) {
-      starColor = SK_ColorWHITE;
-    } else if (starred_) {
-      starColor = gfx::kGoogleBlue500;
-    } else {
-      starColor = gfx::kChromeIconGrey;
-    }
-    theImage = NSImageFromImageSkia(gfx::CreateVectorIcon(
-        iconId, 16, starColor));
-    SetImage(theImage);
-  } else {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    const int image_id = starred ? IDR_STAR_LIT : IDR_STAR;
     SetImage(OmniboxViewMac::ImageForResource(image_id));
+    tooltip_.reset([l10n_util::GetNSStringWithFixup(tip_id) retain]);
+    return;
   }
+  SetImage(GetMaterialIcon(location_bar_is_dark));
   tooltip_.reset([l10n_util::GetNSStringWithFixup(tip_id) retain]);
 }
 
@@ -81,3 +65,15 @@
 NSString* StarDecoration::GetToolTip() {
   return tooltip_.get();
 }
+
+SkColor StarDecoration::GetMaterialIconColor(bool location_bar_is_dark) const {
+  if (location_bar_is_dark) {
+    return starred_ ? gfx::kGoogleBlue300 : SkColorSetA(SK_ColorWHITE, 0xCC);
+  }
+  return starred_ ? gfx::kGoogleBlue500 : gfx::kChromeIconGrey;
+}
+
+gfx::VectorIconId StarDecoration::GetMaterialVectorIconId() const {
+  return starred_ ? gfx::VectorIconId::LOCATION_BAR_STAR_ACTIVE
+                  : gfx::VectorIconId::LOCATION_BAR_STAR;
+}
diff --git a/chrome/browser/ui/cocoa/location_bar/translate_decoration.h b/chrome/browser/ui/cocoa/location_bar/translate_decoration.h
index b90f2ef..1b630c3 100644
--- a/chrome/browser/ui/cocoa/location_bar/translate_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/translate_decoration.h
@@ -30,6 +30,10 @@
   NSString* GetToolTip() override;
   NSPoint GetBubblePointInFrame(NSRect frame) override;
 
+ protected:
+  // Overridden from LocationBarDecoration:
+  gfx::VectorIconId GetMaterialVectorIconId() const override;
+
  private:
   // For showing the translate bubble up.
   CommandUpdater* command_updater_;  // Weak, owned by Browser.
diff --git a/chrome/browser/ui/cocoa/location_bar/translate_decoration.mm b/chrome/browser/ui/cocoa/location_bar/translate_decoration.mm
index 11e5b3dd..4217b997 100644
--- a/chrome/browser/ui/cocoa/location_bar/translate_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/translate_decoration.mm
@@ -11,10 +11,6 @@
 #include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/material_design/material_design_controller.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/vector_icons_public.h"
 
 TranslateDecoration::TranslateDecoration(CommandUpdater* command_updater)
     : command_updater_(command_updater) {
@@ -23,19 +19,13 @@
 
 TranslateDecoration::~TranslateDecoration() {}
 
-void TranslateDecoration::SetLit(bool on, bool locationBarIsDark) {
-  if (ui::MaterialDesignController::IsModeMaterial()) {
-    SkColor vectorIconColor = locationBarIsDark ? SK_ColorWHITE
-                                                : gfx::kChromeIconGrey;
-    NSImage* theImage =
-        NSImageFromImageSkia(gfx::CreateVectorIcon(gfx::VectorIconId::TRANSLATE,
-                                                   16,
-                                                   vectorIconColor));
-    SetImage(theImage);
-  } else {
+void TranslateDecoration::SetLit(bool on, bool location_bar_is_dark) {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
     const int image_id = on ? IDR_TRANSLATE_ACTIVE : IDR_TRANSLATE;
     SetImage(OmniboxViewMac::ImageForResource(image_id));
+    return;
   }
+  SetImage(GetMaterialIcon(location_bar_is_dark));
 }
 
 NSPoint TranslateDecoration::GetBubblePointInFrame(NSRect frame) {
@@ -55,3 +45,7 @@
 NSString* TranslateDecoration::GetToolTip() {
   return l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_TRANSLATE);
 }
+
+gfx::VectorIconId TranslateDecoration::GetMaterialVectorIconId() const {
+  return gfx::VectorIconId::TRANSLATE;
+}
diff --git a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.h b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.h
index 8fd72d1..e4ad3332 100644
--- a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.h
@@ -52,6 +52,9 @@
                                NSString* tooltip_string,
                                bool location_bar_is_dark);
 
+  // Overridden from LocationBarDecoration:
+  gfx::VectorIconId GetMaterialVectorIconId() const override;
+
  private:
   friend ZoomDecorationTest;
 
@@ -79,6 +82,8 @@
   // The string to show for a tooltip.
   base::scoped_nsobject<NSString> tooltip_;
 
+  gfx::VectorIconId vector_icon_id_;
+
   DISALLOW_COPY_AND_ASSIGN(ZoomDecoration);
 };
 
diff --git a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
index 8b5b27be..7fb1d0a 100644
--- a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
@@ -17,15 +17,11 @@
 #include "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/material_design/material_design_controller.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/vector_icons_public.h"
 
 ZoomDecoration::ZoomDecoration(LocationBarViewMac* owner)
     : owner_(owner),
-      bubble_(nil) {
-}
+      bubble_(nil),
+      vector_icon_id_(gfx::VectorIconId::VECTOR_ICON_NONE) {}
 
 ZoomDecoration::~ZoomDecoration() {
   [bubble_ closeWithoutAnimation];
@@ -98,36 +94,17 @@
                                      NSString* tooltip_string,
                                      bool location_bar_is_dark) {
   if (ui::MaterialDesignController::IsModeMaterial()) {
-    gfx::VectorIconId iconId = gfx::VectorIconId::VECTOR_ICON_NONE;
+    vector_icon_id_ = gfx::VectorIconId::VECTOR_ICON_NONE;
     ui_zoom::ZoomController::RelativeZoom relative_zoom =
         zoom_controller->GetZoomRelativeToDefault();
     if (relative_zoom == ui_zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM) {
-      iconId = gfx::VectorIconId::ZOOM_MINUS;
+      vector_icon_id_ = gfx::VectorIconId::ZOOM_MINUS;
     } else if (relative_zoom ==
                ui_zoom::ZoomController::ZOOM_ABOVE_DEFAULT_ZOOM) {
-      iconId = gfx::VectorIconId::ZOOM_PLUS;
+      vector_icon_id_ = gfx::VectorIconId::ZOOM_PLUS;
     }
 
-    NSImage* theImage = nil;
-    if (iconId != gfx::VectorIconId::VECTOR_ICON_NONE) {
-      SkColor vectorIconColor = location_bar_is_dark ? SK_ColorWHITE
-                                                     : gfx::kChromeIconGrey;
-      theImage = NSImageFromImageSkia(gfx::CreateVectorIcon(iconId,
-                                                            16,
-                                                            vectorIconColor));
-    } else {
-      // Under Material Design there is no icon for ZOOM_NORMAL. This means
-      // it should be OK to set a nil image. However if the user is actively
-      // changing the zoom level and drives it back to 100%, there will be no
-      // icon and the zoom bubble will still be visible. ShowBubble() asks the
-      // autocomplete textfield for the zoom decoration's frame in order to
-      // position the bubble, but when the decoration's image is nil it has
-      // no frame. The result is the bubble positioned incorrectly. So, we have
-      // to set an empty image.
-      theImage =
-          [[[NSImage alloc] initWithSize:NSMakeSize(16, 16)] autorelease];
-    }
-    SetImage(theImage);
+    SetImage(GetMaterialIcon(location_bar_is_dark));
   } else {
     int image_id = IDR_ZOOM_NORMAL;
     ui_zoom::ZoomController::RelativeZoom relative_zoom =
@@ -198,3 +175,7 @@
     owner_->OnDecorationsChanged();
   }
 }
+
+gfx::VectorIconId ZoomDecoration::GetMaterialVectorIconId() const {
+  return vector_icon_id_;
+}
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
index 34d69e76..53c08c87 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
@@ -45,6 +45,7 @@
 @property(readonly, retain, nonatomic) NSAttributedString* description;
 @property(readonly, retain, nonatomic) NSAttributedString* prefix;
 @property(readonly, retain, nonatomic) NSImage* image;
+@property(retain, nonatomic) NSImage* incognitoImage;
 @property(readonly, retain, nonatomic) NSImage* answerImage;
 @property(readonly, nonatomic) CGFloat contentsOffset;
 @property(readonly, nonatomic) BOOL isContentsRTL;
@@ -54,7 +55,8 @@
 - (instancetype)initWithMatch:(const AutocompleteMatch&)match
                contentsOffset:(CGFloat)contentsOffset
                         image:(NSImage*)image
-                  answerImage:(NSImage*)answerImage;
+                  answerImage:(NSImage*)answerImage
+                 forDarkTheme:(BOOL)isDarkTheme;
 
 // Returns the width of the match contents.
 - (CGFloat)getMatchContentsWidth;
@@ -72,7 +74,7 @@
 // shared by all OmniboxPopupCell instances through OmniboxPopupViewMac parent.
 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match;
 
-+ (NSAttributedString*)createSeparatorString;
++ (NSAttributedString*)createSeparatorStringForDarkTheme:(BOOL)isDarkTheme;
 
 @end
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
index 5b042bb..3db9f8a 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
@@ -18,11 +18,14 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h"
 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
+#import "chrome/browser/ui/cocoa/themed_window.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "components/omnibox/browser/suggestion_answer.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/font.h"
 
 namespace {
@@ -30,15 +33,34 @@
 // How far to offset image column from the left.
 const CGFloat kImageXOffset = 5.0;
 
-// How far to offset image and text.
-const CGFloat kPaddingOffset = 3.0;
+// How far to offset text.
+const CGFloat kVerticalTextPadding = 3.0;
 
-// How far to offset the text column from the left.
+const CGFloat kVerticalImagePadding = 3.0;
+const CGFloat kMaterialVerticalImagePadding = 5.0;
+
 const CGFloat kTextStartOffset = 28.0;
+const CGFloat kMaterialTextStartOffset = 25.0;
 
 // Rounding radius of selection and hover background on popup items.
 const CGFloat kCellRoundingRadius = 2.0;
 
+// How far to offset the image.
+CGFloat VerticalImagePadding() {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return kVerticalImagePadding;
+  }
+  return kMaterialVerticalImagePadding;
+}
+
+// How far to offset the text column from the left.
+CGFloat TextStartOffset() {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return kTextStartOffset;
+  }
+  return kMaterialTextStartOffset;
+}
+
 // Flips the given |rect| in context of the given |frame|.
 NSRect FlipIfRTL(NSRect rect, NSRect frame) {
   DCHECK_LE(NSMinX(frame), NSMinX(rect));
@@ -51,18 +73,36 @@
   return rect;
 }
 
-NSColor* SelectedBackgroundColor() {
-  return [NSColor selectedControlColor];
+NSColor* SelectedBackgroundColor(BOOL is_dark_theme) {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return [NSColor selectedControlColor];
+  }
+  return is_dark_theme
+             ? skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x14))
+             : skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorBLACK, 0x14));
 }
-NSColor* HoveredBackgroundColor() {
+
+NSColor* HoveredBackgroundColor(BOOL is_dark_theme) {
+  if (is_dark_theme) {
+    return skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x0D));
+  }
   return [NSColor controlHighlightColor];
 }
 
-NSColor* ContentTextColor() {
+NSColor* ContentTextColor(BOOL is_dark_theme) {
+  if (ui::MaterialDesignController::IsModeMaterial() && is_dark_theme) {
+    return [NSColor whiteColor];
+  }
   return [NSColor blackColor];
 }
-NSColor* DimTextColor() {
-  return [NSColor darkGrayColor];
+NSColor* DimTextColor(BOOL is_dark_theme) {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return [NSColor darkGrayColor];
+  }
+  if (is_dark_theme) {
+    return skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x7F));
+  }
+  return skia::SkColorToSRGBNSColor(SkColorSetRGB(0x64, 0x64, 0x64));
 }
 NSColor* PositiveTextColor() {
   return skia::SkColorToCalibratedNSColor(SkColorSetRGB(0x3d, 0x94, 0x00));
@@ -70,8 +110,12 @@
 NSColor* NegativeTextColor() {
   return skia::SkColorToCalibratedNSColor(SkColorSetRGB(0xdd, 0x4b, 0x39));
 }
-NSColor* URLTextColor() {
-  return [NSColor colorWithCalibratedRed:0.0 green:0.55 blue:0.0 alpha:1.0];
+NSColor* URLTextColor(BOOL is_dark_theme) {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return [NSColor colorWithCalibratedRed:0.0 green:0.55 blue:0.0 alpha:1.0];
+  }
+  return is_dark_theme ? skia::SkColorToSRGBNSColor(gfx::kGoogleBlue300)
+                       : skia::SkColorToSRGBNSColor(gfx::kGoogleBlue700);
 }
 
 NSFont* FieldFont() {
@@ -96,17 +140,20 @@
 }
 
 CGFloat GetContentAreaWidth(NSRect cellFrame) {
-  return NSWidth(cellFrame) - kTextStartOffset;
+  return NSWidth(cellFrame) - TextStartOffset();
 }
 
 NSAttributedString* CreateAnswerStringHelper(const base::string16& text,
                                              NSInteger style_type,
-                                             bool is_bold) {
+                                             bool is_bold,
+                                             BOOL is_dark_theme) {
   NSDictionary* answer_style = nil;
+  NSFont* answer_font = nil;
+  bool is_mode_material = ui::MaterialDesignController::IsModeMaterial();
   switch (style_type) {
     case SuggestionAnswer::TOP_ALIGNED:
       answer_style = @{
-        NSForegroundColorAttributeName : DimTextColor(),
+        NSForegroundColorAttributeName : DimTextColor(is_dark_theme),
         NSFontAttributeName : LargeSuperscriptFont(),
         NSSuperscriptAttributeName : @1
       };
@@ -125,38 +172,40 @@
       break;
     case SuggestionAnswer::PERSONALIZED_SUGGESTION:
       answer_style = @{
-        NSForegroundColorAttributeName : ContentTextColor(),
+        NSForegroundColorAttributeName : ContentTextColor(is_dark_theme),
         NSFontAttributeName : FieldFont()
       };
       break;
     case SuggestionAnswer::ANSWER_TEXT_MEDIUM:
       answer_style = @{
-        NSForegroundColorAttributeName : ContentTextColor(),
+        NSForegroundColorAttributeName : ContentTextColor(is_dark_theme),
         NSFontAttributeName : FieldFont()
       };
       break;
     case SuggestionAnswer::ANSWER_TEXT_LARGE:
       answer_style = @{
-        NSForegroundColorAttributeName : ContentTextColor(),
+        NSForegroundColorAttributeName : ContentTextColor(is_dark_theme),
         NSFontAttributeName : LargeFont()
       };
       break;
     case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_SMALL:
+      answer_font = is_mode_material ? FieldFont() : SmallFont();
       answer_style = @{
-        NSForegroundColorAttributeName : DimTextColor(),
-        NSFontAttributeName : SmallFont()
+        NSForegroundColorAttributeName : DimTextColor(is_dark_theme),
+        NSFontAttributeName : answer_font
       };
       break;
     case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_MEDIUM:
+      answer_font = is_mode_material ? LargeSuperscriptFont() : FieldFont();
       answer_style = @{
-        NSForegroundColorAttributeName : DimTextColor(),
-        NSFontAttributeName : FieldFont()
+        NSForegroundColorAttributeName : DimTextColor(is_dark_theme),
+        NSFontAttributeName : answer_font
       };
       break;
     case SuggestionAnswer::SUGGESTION:  // Fall through.
     default:
       answer_style = @{
-        NSForegroundColorAttributeName : ContentTextColor (),
+        NSForegroundColorAttributeName : ContentTextColor(is_dark_theme),
         NSFontAttributeName : FieldFont()
       };
       break;
@@ -176,7 +225,8 @@
 }
 
 NSAttributedString* CreateAnswerString(const base::string16& text,
-                                       NSInteger style_type) {
+                                       NSInteger style_type,
+                                       BOOL is_dark_theme) {
   // TODO(dschuyler): make this better.  Right now this only supports unnested
   // bold tags.  In the future we'll need to flag unexpected tags while adding
   // support for b, i, u, sub, and sup.  We'll also need to support HTML
@@ -189,48 +239,50 @@
   while (true) {
     size_t end = text.find(begin_tag, begin);
     if (end == base::string16::npos) {
-      [result
-          appendAttributedString:CreateAnswerStringHelper(
-                                         text.substr(begin),
-                                         style_type, false)];
+      [result appendAttributedString:CreateAnswerStringHelper(
+                                         text.substr(begin), style_type, false,
+                                         is_dark_theme)];
       break;
     }
     [result appendAttributedString:CreateAnswerStringHelper(
                                        text.substr(begin, end - begin),
-                                       style_type, false)];
+                                       style_type, false, is_dark_theme)];
     begin = end + begin_tag.length();
     end = text.find(end_tag, begin);
     if (end == base::string16::npos)
       break;
     [result appendAttributedString:CreateAnswerStringHelper(
                                        text.substr(begin, end - begin),
-                                       style_type, true)];
+                                       style_type, true, is_dark_theme)];
     begin = end + end_tag.length();
   }
   return result.autorelease();
 }
 
-NSAttributedString* CreateAnswerLine(const SuggestionAnswer::ImageLine& line) {
+NSAttributedString* CreateAnswerLine(const SuggestionAnswer::ImageLine& line,
+                                     BOOL is_dark_theme) {
   base::scoped_nsobject<NSMutableAttributedString> answer_string(
       [[NSMutableAttributedString alloc] init]);
   DCHECK(!line.text_fields().empty());
   for (const SuggestionAnswer::TextField& text_field : line.text_fields()) {
-    [answer_string
-        appendAttributedString:CreateAnswerString(text_field.text(),
-                                                  text_field.type())];
+    [answer_string appendAttributedString:CreateAnswerString(text_field.text(),
+                                                             text_field.type(),
+                                                             is_dark_theme)];
   }
   const base::string16 space(base::ASCIIToUTF16(" "));
   const SuggestionAnswer::TextField* text_field = line.additional_text();
   if (text_field) {
     [answer_string
         appendAttributedString:CreateAnswerString(space + text_field->text(),
-                                                  text_field->type())];
+                                                  text_field->type(),
+                                                  is_dark_theme)];
   }
   text_field = line.status_text();
   if (text_field) {
     [answer_string
         appendAttributedString:CreateAnswerString(space + text_field->text(),
-                                                  text_field->type())];
+                                                  text_field->type(),
+                                                  is_dark_theme)];
   }
   base::scoped_nsobject<NSMutableParagraphStyle> style(
       [[NSMutableParagraphStyle alloc] init]);
@@ -277,7 +329,8 @@
 NSAttributedString* CreateClassifiedAttributedString(
     const base::string16& text,
     NSColor* text_color,
-    const ACMatchClassifications& classifications) {
+    const ACMatchClassifications& classifications,
+    BOOL is_dark_theme) {
   NSMutableAttributedString* attributedString =
       CreateAttributedString(text, text_color);
   NSUInteger match_length = [attributedString length];
@@ -304,11 +357,11 @@
 
     if (0 != (i->style & ACMatchClassification::URL)) {
       [attributedString addAttribute:NSForegroundColorAttributeName
-                               value:URLTextColor()
+                               value:URLTextColor(is_dark_theme)
                                range:range];
     } else if (0 != (i->style & ACMatchClassification::DIM)) {
       [attributedString addAttribute:NSForegroundColorAttributeName
-                               value:DimTextColor()
+                               value:DimTextColor(is_dark_theme)
                                range:range];
     }
   }
@@ -322,10 +375,12 @@
 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
                withFrame:(NSRect)cellFrame
                   origin:(NSPoint)origin
-            withMaxWidth:(int)maxWidth;
+            withMaxWidth:(int)maxWidth
+            forDarkTheme:(BOOL)isDarkTheme;
 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
                           tableView:(OmniboxPopupMatrix*)tableView
-               withContentsMaxWidth:(int*)contentsMaxWidth;
+               withContentsMaxWidth:(int*)contentsMaxWidth
+                       forDarkTheme:(BOOL)isDarkTheme;
 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView;
 @end
 
@@ -335,6 +390,7 @@
 @synthesize description = description_;
 @synthesize prefix = prefix_;
 @synthesize image = image_;
+@synthesize incognitoImage = incognitoImage_;
 @synthesize answerImage = answerImage_;
 @synthesize contentsOffset = contentsOffset_;
 @synthesize isContentsRTL = isContentsRTL_;
@@ -344,7 +400,8 @@
 - (instancetype)initWithMatch:(const AutocompleteMatch&)match
                contentsOffset:(CGFloat)contentsOffset
                         image:(NSImage*)image
-                  answerImage:(NSImage*)answerImage {
+                  answerImage:(NSImage*)answerImage
+                 forDarkTheme:(BOOL)isDarkTheme {
   if ((self = [super init])) {
     image_ = [image retain];
     answerImage_ = [answerImage retain];
@@ -360,27 +417,36 @@
     // of the contents so we force the directionality.
     NSTextAlignment textAlignment =
         isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment;
-    prefix_ =
-        [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo(
-                                    kACMatchPropertyContentsPrefix)),
-                                ContentTextColor(), textAlignment) retain];
+    prefix_ = [CreateAttributedString(
+        base::UTF8ToUTF16(
+            match.GetAdditionalInfo(kACMatchPropertyContentsPrefix)),
+        ContentTextColor(isDarkTheme), textAlignment) retain];
 
     isAnswer_ = !!match.answer;
     if (isAnswer_) {
-      contents_ = [CreateAnswerLine(match.answer->first_line()) retain];
-      description_ = [CreateAnswerLine(match.answer->second_line()) retain];
+      contents_ =
+          [CreateAnswerLine(match.answer->first_line(), isDarkTheme) retain];
+      description_ =
+          [CreateAnswerLine(match.answer->second_line(), isDarkTheme) retain];
     } else {
       contents_ = [CreateClassifiedAttributedString(
-          match.contents, ContentTextColor(), match.contents_class) retain];
+          match.contents, ContentTextColor(isDarkTheme), match.contents_class,
+          isDarkTheme) retain];
       if (!match.description.empty()) {
         description_ = [CreateClassifiedAttributedString(
-            match.description, DimTextColor(), match.description_class) retain];
+            match.description, DimTextColor(isDarkTheme),
+            match.description_class, isDarkTheme) retain];
       }
     }
   }
   return self;
 }
 
+- (void)dealloc {
+  [incognitoImage_ release];
+  [super dealloc];
+}
+
 - (instancetype)copyWithZone:(NSZone*)zone {
   return [self retain];
 }
@@ -394,16 +460,25 @@
 @implementation OmniboxPopupCell
 
 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+  OmniboxPopupMatrix* matrix =
+      base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView);
+  BOOL isDarkTheme = [matrix hasDarkTheme];
+
   if ([self state] == NSOnState || [self isHighlighted]) {
-    if ([self state] == NSOnState)
-      [SelectedBackgroundColor() set];
-    else
-      [HoveredBackgroundColor() set];
-    NSBezierPath* path =
-        [NSBezierPath bezierPathWithRoundedRect:cellFrame
-                                        xRadius:kCellRoundingRadius
-                                        yRadius:kCellRoundingRadius];
-    [path fill];
+    if ([self state] == NSOnState) {
+      [SelectedBackgroundColor(isDarkTheme) set];
+    } else {
+      [HoveredBackgroundColor(isDarkTheme) set];
+    }
+    if (ui::MaterialDesignController::IsModeMaterial()) {
+      NSRectFillUsingOperation(cellFrame, NSCompositeSourceOver);
+    } else {
+      NSBezierPath* path =
+          [NSBezierPath bezierPathWithRoundedRect:cellFrame
+                                          xRadius:kCellRoundingRadius
+                                          yRadius:kCellRoundingRadius];
+      [path fill];
+    }
   }
 
   [self drawMatchWithFrame:cellFrame inView:controlView];
@@ -414,7 +489,8 @@
       base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]);
   OmniboxPopupMatrix* tableView =
       base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView);
-  CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
+  CGFloat remainingWidth =
+      GetContentAreaWidth(cellFrame) - [tableView contentLeftPadding];
   CGFloat contentsWidth = [cellData getMatchContentsWidth];
   CGFloat separatorWidth = [[tableView separator] size].width;
   CGFloat descriptionWidth =
@@ -428,34 +504,41 @@
       &contentsMaxWidth,
       &descriptionMaxWidth);
 
+  NSWindow* parentWindow = [[controlView window] parentWindow];
+  BOOL isDarkTheme = [parentWindow hasDarkTheme];
   NSRect imageRect = cellFrame;
-  imageRect.size = [[cellData image] size];
-  imageRect.origin.x += kImageXOffset;
-  imageRect.origin.y += kPaddingOffset;
-  [[cellData image] drawInRect:FlipIfRTL(imageRect, cellFrame)
-                      fromRect:NSZeroRect
-                     operation:NSCompositeSourceOver
-                      fraction:1.0
-                respectFlipped:YES
-                         hints:nil];
+  NSImage* theImage =
+      isDarkTheme ? [cellData incognitoImage] : [cellData image];
+  imageRect.size = [theImage size];
+  imageRect.origin.x += kImageXOffset + [tableView contentLeftPadding];
+  imageRect.origin.y += VerticalImagePadding();
+  [theImage drawInRect:FlipIfRTL(imageRect, cellFrame)
+              fromRect:NSZeroRect
+             operation:NSCompositeSourceOver
+              fraction:1.0
+        respectFlipped:YES
+                 hints:nil];
 
-  NSPoint origin = NSMakePoint(kTextStartOffset, kPaddingOffset);
+  NSPoint origin = NSMakePoint(
+      TextStartOffset() + [tableView contentLeftPadding], kVerticalTextPadding);
   if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
     // Infinite suggestions are rendered with a prefix (usually ellipsis), which
     // appear vertically stacked.
     origin.x += [self drawMatchPrefixWithFrame:cellFrame
                                      tableView:tableView
-                          withContentsMaxWidth:&contentsMaxWidth];
+                          withContentsMaxWidth:&contentsMaxWidth
+                                  forDarkTheme:isDarkTheme];
   }
   origin.x += [self drawMatchPart:[cellData contents]
                         withFrame:cellFrame
                            origin:origin
-                     withMaxWidth:contentsMaxWidth];
+                     withMaxWidth:contentsMaxWidth
+                     forDarkTheme:isDarkTheme];
 
   if (descriptionMaxWidth > 0) {
     if ([cellData isAnswer]) {
-      origin =
-          NSMakePoint(kTextStartOffset, kContentLineHeight - kPaddingOffset);
+      origin = NSMakePoint(TextStartOffset() + [tableView contentLeftPadding],
+                           kContentLineHeight - kVerticalTextPadding);
       CGFloat imageSize = [tableView answerLineHeight];
       NSRect imageRect =
           NSMakeRect(NSMinX(cellFrame) + origin.x, NSMinY(cellFrame) + origin.y,
@@ -466,28 +549,39 @@
                                 fraction:1.0
                           respectFlipped:YES
                                    hints:nil];
-      if ([cellData answerImage])
-        origin.x += imageSize + kPaddingOffset;
+      if ([cellData answerImage]) {
+        origin.x += imageSize + VerticalImagePadding();
+
+        // Have to nudge the baseline down 1pt in Material Design for the text
+        // that follows, so that it's the same as the bottom of the image.
+        if (ui::MaterialDesignController::IsModeMaterial()) {
+          origin.y += 1;
+        }
+      }
     } else {
       origin.x += [self drawMatchPart:[tableView separator]
                             withFrame:cellFrame
                                origin:origin
-                         withMaxWidth:separatorWidth];
+                         withMaxWidth:separatorWidth
+                         forDarkTheme:isDarkTheme];
     }
     origin.x += [self drawMatchPart:[cellData description]
                           withFrame:cellFrame
                              origin:origin
-                       withMaxWidth:descriptionMaxWidth];
+                       withMaxWidth:descriptionMaxWidth
+                       forDarkTheme:isDarkTheme];
   }
 }
 
 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
                           tableView:(OmniboxPopupMatrix*)tableView
-               withContentsMaxWidth:(int*)contentsMaxWidth {
+               withContentsMaxWidth:(int*)contentsMaxWidth
+                       forDarkTheme:(BOOL)isDarkTheme {
   OmniboxPopupCellData* cellData =
       base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]);
   CGFloat offset = 0.0f;
-  CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
+  CGFloat remainingWidth =
+      GetContentAreaWidth(cellFrame) - [tableView contentLeftPadding];
   CGFloat prefixWidth = [[cellData prefix] size].width;
 
   CGFloat prefixOffset = 0.0f;
@@ -516,17 +610,21 @@
   }
   *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth),
                                *contentsMaxWidth);
+  NSPoint origin = NSMakePoint(
+      prefixOffset + TextStartOffset() + [tableView contentLeftPadding], 0);
   [self drawMatchPart:[cellData prefix]
             withFrame:cellFrame
-               origin:NSMakePoint(prefixOffset + kTextStartOffset, 0)
-         withMaxWidth:prefixWidth];
+               origin:origin
+         withMaxWidth:prefixWidth
+         forDarkTheme:isDarkTheme];
   return offset;
 }
 
 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
                withFrame:(NSRect)cellFrame
                   origin:(NSPoint)origin
-            withMaxWidth:(int)maxWidth {
+            withMaxWidth:(int)maxWidth
+            forDarkTheme:(BOOL)isDarkTheme {
   NSRect renderRect = NSIntersectionRect(
       cellFrame, NSOffsetRect(cellFrame, origin.x, origin.y));
   renderRect.size.width =
@@ -556,7 +654,7 @@
 
   // Color does not matter.
   NSAttributedString* attributedString =
-      CreateAttributedString(inputText, DimTextColor());
+      CreateAttributedString(inputText, DimTextColor(false));
   base::scoped_nsobject<NSTextStorage> textStorage(
       [[NSTextStorage alloc] initWithAttributedString:attributedString]);
   base::scoped_nsobject<NSLayoutManager> layoutManager(
@@ -604,10 +702,10 @@
   return base::i18n::IsRTL() ? (inputWidth - glyphOffset) : glyphOffset;
 }
 
-+ (NSAttributedString*)createSeparatorString {
++ (NSAttributedString*)createSeparatorStringForDarkTheme:(BOOL)isDarkTheme {
   base::string16 raw_separator =
       l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
-  return CreateAttributedString(raw_separator, DimTextColor());
+  return CreateAttributedString(raw_separator, DimTextColor(isDarkTheme));
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
index 7b28327..3ac81947 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
@@ -46,7 +46,8 @@
        initWithMatch:match
       contentsOffset:0
                image:[NSImage imageNamed:NSImageNameInfo]
-         answerImage:nil]);
+         answerImage:nil
+        forDarkTheme:NO]);
   [cell_ setObjectValue:cellData_];
   [control_ display];
 }
@@ -58,7 +59,8 @@
   cellData_.reset([[OmniboxPopupCellData alloc] initWithMatch:match
                                                contentsOffset:0
                                                         image:nil
-                                                  answerImage:nil]);
+                                                  answerImage:nil
+                                                 forDarkTheme:NO]);
   [cell_ setObjectValue:cellData_];
   [control_ display];
 }
@@ -84,7 +86,8 @@
   cellData_.reset([[OmniboxPopupCellData alloc] initWithMatch:match
                                                contentsOffset:0
                                                         image:nil
-                                                  answerImage:nil]);
+                                                  answerImage:nil
+                                                 forDarkTheme:NO]);
   EXPECT_NSEQ([[cellData_ description] string], finalString);
   size_t length = [[[cellData_ description] string] length];
   const NSRange checkValues[] = {{0, 2}, {2, 2}, {4, 4}};
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h
index 920c8cfc..07daf9b 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h
@@ -68,14 +68,23 @@
   CGFloat maxMatchContentsWidth_;
 
   CGFloat answerLineHeight_;
+
+  // Left margin padding for the content (i.e. icon and text) in a cell.
+  CGFloat contentLeftPadding_;
+
+  // true if the OmniboxPopupMatrix should use the dark theme style.
+  BOOL hasDarkTheme_;
 }
 
 @property(retain, nonatomic) NSAttributedString* separator;
 @property(nonatomic) CGFloat maxMatchContentsWidth;
 @property(nonatomic) CGFloat answerLineHeight;
+@property(nonatomic) CGFloat contentLeftPadding;
+@property(readonly, nonatomic) BOOL hasDarkTheme;
 
 // Create a zero-size matrix.
-- (instancetype)initWithObserver:(OmniboxPopupMatrixObserver*)observer;
+- (instancetype)initWithObserver:(OmniboxPopupMatrixObserver*)observer
+                    forDarkTheme:(BOOL)isDarkTheme;
 
 // Sets the observer.
 - (void)setObserver:(OmniboxPopupMatrixObserver*)observer;
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
index c3dc7e6..e7c2f03 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
@@ -32,6 +32,7 @@
                            popupView:(const OmniboxPopupViewMac&)popupView
                          answerImage:(NSImage*)answerImage {
   base::scoped_nsobject<NSMutableArray> array([[NSMutableArray alloc] init]);
+  BOOL isDarkTheme = [tableView hasDarkTheme];
   CGFloat max_match_contents_width = 0.0f;
   CGFloat contentsOffset = -1.0f;
   for (const AutocompleteMatch& match : result) {
@@ -42,8 +43,12 @@
         [[OmniboxPopupCellData alloc]
              initWithMatch:match
             contentsOffset:contentsOffset
-                     image:popupView.ImageForMatch(match)
-               answerImage:(match.answer ? answerImage : nil)]);
+                     image:popupView.ImageForMatch(match, NO)
+               answerImage:(match.answer ? answerImage : nil)
+              forDarkTheme:isDarkTheme]);
+    if (isDarkTheme) {
+      [cellData setIncognitoImage:popupView.ImageForMatch(match, YES)];
+    }
     [array addObject:cellData];
     if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
       max_match_contents_width =
@@ -118,10 +123,14 @@
 @synthesize separator = separator_;
 @synthesize maxMatchContentsWidth = maxMatchContentsWidth_;
 @synthesize answerLineHeight = answerLineHeight_;
+@synthesize contentLeftPadding = contentLeftPadding_;
+@synthesize hasDarkTheme = hasDarkTheme_;
 
-- (instancetype)initWithObserver:(OmniboxPopupMatrixObserver*)observer {
+- (instancetype)initWithObserver:(OmniboxPopupMatrixObserver*)observer
+                    forDarkTheme:(BOOL)isDarkTheme {
   if ((self = [super initWithFrame:NSZeroRect])) {
     observer_ = observer;
+    hasDarkTheme_ = isDarkTheme;
 
     base::scoped_nsobject<NSTableColumn> column(
         [[NSTableColumn alloc] initWithIdentifier:@"MainColumn"]);
@@ -132,7 +141,9 @@
     [self setIntercellSpacing:NSMakeSize(0.0, 0.0)];
 
     [self setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
-    [self setBackgroundColor:[NSColor controlBackgroundColor]];
+    NSColor* backgroundColor =
+        OmniboxPopupViewMac::BackgroundColor(hasDarkTheme_);
+    [self setBackgroundColor:backgroundColor];
     [self setAllowsEmptySelection:YES];
     [self deselectAll:self];
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix_unittest.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix_unittest.mm
index 7ccef29..4c3aa75 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix_unittest.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix_unittest.mm
@@ -31,7 +31,8 @@
 
   void SetUp() override {
     CocoaTest::SetUp();
-    matrix_.reset([[OmniboxPopupMatrix alloc] initWithObserver:this]);
+    matrix_.reset([[OmniboxPopupMatrix alloc] initWithObserver:this
+                                                  forDarkTheme:NO]);
     [[test_window() contentView] addSubview:matrix_];
 
     NSMutableArray* array = [NSMutableArray array];
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h
index ae2355a..1b91be76 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h
@@ -19,9 +19,12 @@
 
 // A view used to draw a drop shadow beneath the omnibox popup.
 @interface OmniboxPopupBottomSeparatorView : NSView {
+ @private
+  BOOL isDarkTheme_;
 }
 
 + (CGFloat)preferredHeight;
+- (instancetype)initWithFrame:(NSRect)frame forDarkTheme:(BOOL)isDarkTheme;
 
 @end
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.mm
index 49f9a0d..b981d7d 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.mm
@@ -4,6 +4,7 @@
 
 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h"
 
+#import "base/mac/scoped_nsobject.h"
 #include "grit/theme_resources.h"
 #import "ui/base/cocoa/nsview_additions.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -32,11 +33,40 @@
   return [shadowImage size].height;
 }
 
+- (instancetype)initWithFrame:(NSRect)frame forDarkTheme:(BOOL)isDarkTheme {
+  if ((self = [self initWithFrame:frame])) {
+    isDarkTheme_ = isDarkTheme;
+    // For dark themes the OmniboxPopupBottomSeparatorView will render a shadow
+    // rather than blit a bitmap. Shadows are expensive to draw so use a layer
+    // to cache the result.
+    if (isDarkTheme_) {
+      [self setWantsLayer:YES];
+    }
+  }
+  return self;
+}
+
 - (void)drawRect:(NSRect)rect {
-  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   NSRect bounds = [self bounds];
 
+  if (isDarkTheme_) {
+    // There's an image for the shadow the Omnibox casts, but this shadow is
+    // an opaque mix of white and black, which makes it look strange against a
+    // dark NTP page. For dark mode, draw the shadow in code instead so that
+    // it has some transparency.
+    base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
+    [shadow setShadowBlurRadius:8];
+    [shadow setShadowColor:[NSColor blackColor]];
+    [shadow set];
+
+    // Fill a rect that's out of view to get just the shadow it casts.
+    [[NSColor blackColor] set];
+    NSRectFill(NSMakeRect(-3, NSMaxY(bounds), NSWidth(bounds) + 6, 5));
+    return;
+  }
+
   // Draw the shadow.
+  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   NSImage* shadowImage =
       rb.GetNativeImageNamed(IDR_OVERLAY_DROP_SHADOW).ToNSImage();
   [shadowImage drawInRect:bounds
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h
index 5d6c1b4..3d2dc9b 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h
@@ -32,6 +32,9 @@
                       NSTextField* field);
   ~OmniboxPopupViewMac() override;
 
+  // Return the OmniboxPopupViewMac background color.
+  static NSColor* BackgroundColor(bool is_dark_theme);
+
   // Overridden from OmniboxPopupView:
   bool IsOpen() const override;
   void InvalidateLine(size_t line) override {}
@@ -50,7 +53,8 @@
                                 size_t row) override;
 
   // Returns the NSImage that should be used as an icon for the given match.
-  NSImage* ImageForMatch(const AutocompleteMatch& match) const;
+  NSImage* ImageForMatch(const AutocompleteMatch& match,
+                         BOOL ignore_dark_theme) const;
 
   OmniboxPopupMatrix* matrix() { return matrix_; }
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
index f8a10b8..e18b78ed 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
@@ -25,23 +25,31 @@
 #import "ui/base/cocoa/cocoa_base_utils.h"
 #import "ui/base/cocoa/flipped_view.h"
 #include "ui/base/cocoa/window_size_constants.h"
+#include "ui/base/material_design/material_design_controller.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image_skia_util_mac.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
 #include "ui/gfx/text_elider.h"
+#include "ui/gfx/vector_icons_public.h"
 
 namespace {
 
+const int kPopupPaddingVertical = 5;
+const int kMaterialPopupPaddingVertical = 4;
+
 // Padding between matrix and the top and bottom of the popup window.
-const CGFloat kPopupPaddingVertical = 5.0;
+CGFloat PopupPaddingVertical() {
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return kPopupPaddingVertical;
+  }
+  return kMaterialPopupPaddingVertical;
+}
 
 // Animation duration when animating the popup window smaller.
 const NSTimeInterval kShrinkAnimationDuration = 0.1;
 
-// Background colors for different states of the popup elements.
-NSColor* BackgroundColor() {
-  return [NSColor controlBackgroundColor];
-}
-
 }  // namespace
 
 OmniboxPopupViewMac::OmniboxPopupViewMac(OmniboxView* omnibox_view,
@@ -66,6 +74,16 @@
   [matrix_ setObserver:NULL];
 }
 
+// Background colors for different states of the popup elements.
+// static
+NSColor* OmniboxPopupViewMac::BackgroundColor(bool is_dark_theme) {
+  const CGFloat kMDDarkControlBackround = 40 / 255.;
+  return is_dark_theme
+             ? [NSColor colorWithGenericGamma22White:kMDDarkControlBackround
+                                               alpha:1]
+             : [NSColor controlBackgroundColor];
+}
+
 bool OmniboxPopupViewMac::IsOpen() const {
   return popup_ != nil;
 }
@@ -102,7 +120,9 @@
                                         tableView:matrix_
                                         popupView:*this
                                       answerImage:answerImage]];
-  [matrix_ setSeparator:[OmniboxPopupCell createSeparatorString]];
+  BOOL is_dark_theme = [matrix_ hasDarkTheme];
+  [matrix_ setSeparator:[OmniboxPopupCell
+                            createSeparatorStringForDarkTheme:is_dark_theme]];
 
   // Update the selection before placing (and displaying) the window.
   PaintUpdatesNow();
@@ -165,23 +185,28 @@
         [[FlippedView alloc] initWithFrame:NSZeroRect]);
     [popup_ setContentView:contentView];
 
+    BOOL is_dark_theme = ui::MaterialDesignController::IsModeMaterial() &&
+                         [[field_ window] hasDarkTheme];
+
     // View to draw a background beneath the matrix.
     background_view_.reset([[NSBox alloc] initWithFrame:NSZeroRect]);
     [background_view_ setBoxType:NSBoxCustom];
     [background_view_ setBorderType:NSNoBorder];
-    [background_view_ setFillColor:BackgroundColor()];
+    [background_view_ setFillColor:BackgroundColor(is_dark_theme)];
     [background_view_ setContentViewMargins:NSZeroSize];
     [contentView addSubview:background_view_];
 
-    matrix_.reset([[OmniboxPopupMatrix alloc] initWithObserver:this]);
+    matrix_.reset([[OmniboxPopupMatrix alloc] initWithObserver:this
+                                                  forDarkTheme:is_dark_theme]);
     [background_view_ addSubview:matrix_];
 
     top_separator_view_.reset(
         [[OmniboxPopupTopSeparatorView alloc] initWithFrame:NSZeroRect]);
     [contentView addSubview:top_separator_view_];
 
-    bottom_separator_view_.reset(
-        [[OmniboxPopupBottomSeparatorView alloc] initWithFrame:NSZeroRect]);
+    bottom_separator_view_.reset([[OmniboxPopupBottomSeparatorView alloc]
+        initWithFrame:NSZeroRect
+         forDarkTheme:is_dark_theme]);
     [contentView addSubview:bottom_separator_view_];
 
     // TODO(dtseng): Ignore until we provide NSAccessibility support.
@@ -198,7 +223,7 @@
   // Calculate the popup's position on the screen.
   NSRect popup_frame = anchor_rect_base;
   // Size to fit the matrix and shift down by the size.
-  popup_frame.size.height = matrixHeight + kPopupPaddingVertical * 2.0;
+  popup_frame.size.height = matrixHeight + PopupPaddingVertical() * 2.0;
   popup_frame.size.height += [OmniboxPopupTopSeparatorView preferredHeight];
   popup_frame.size.height += [OmniboxPopupBottomSeparatorView preferredHeight];
   popup_frame.origin.y -= NSHeight(popup_frame);
@@ -232,21 +257,31 @@
   background_rect.origin.y = NSMaxY(top_separator_frame);
   [background_view_ setFrame:background_rect];
 
-  // Calculate the width of the table based on backing out the popup's border
+  // In Material Design, the table is the width of the window. In non-MD,
+  // calculate the width of the table based on backing out the popup's border
   // from the width of the field.
-  const CGFloat tableWidth = NSWidth([field_ bounds]);
-  DCHECK_GT(tableWidth, 0.0);
+  CGFloat table_width = NSWidth([background_view_ bounds]);
+  bool is_mode_material = ui::MaterialDesignController::IsModeMaterial();
+  if (!is_mode_material) {
+    table_width = NSWidth([field_ bounds]);
+  }
+  DCHECK_GT(table_width, 0.0);
 
   // Matrix.
   NSPoint field_origin_base =
       [field_ convertPoint:[field_ bounds].origin toView:nil];
   NSRect matrix_frame = NSZeroRect;
-  matrix_frame.origin.x = field_origin_base.x - NSMinX(anchor_rect_base);
-  matrix_frame.origin.y = kPopupPaddingVertical;
-  matrix_frame.size.width = tableWidth;
+  matrix_frame.origin.x = 0;
+  if (!is_mode_material) {
+    matrix_frame.origin.x = field_origin_base.x - NSMinX(anchor_rect_base);
+  } else {
+    [matrix_ setContentLeftPadding:field_origin_base.x];
+  }
+  matrix_frame.origin.y = PopupPaddingVertical();
+  matrix_frame.size.width = table_width;
   matrix_frame.size.height = matrixHeight;
   [matrix_ setFrame:matrix_frame];
-  [[[matrix_ tableColumns] objectAtIndex:0] setWidth:tableWidth];
+  [[[matrix_ tableColumns] objectAtIndex:0] setWidth:table_width];
 
   // Don't play animation games on first display.
   target_popup_frame_ = popup_frame;
@@ -301,15 +336,28 @@
   }
 }
 
-NSImage* OmniboxPopupViewMac::ImageForMatch(
-    const AutocompleteMatch& match) const {
+NSImage* OmniboxPopupViewMac::ImageForMatch(const AutocompleteMatch& match,
+                                            BOOL ignore_dark_theme) const {
   gfx::Image image = model_->GetIconIfExtensionMatch(match);
   if (!image.IsEmpty())
     return image.AsNSImage();
 
-  const int resource_id = model_->IsStarredMatch(match) ?
-      IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match.type);
-  return OmniboxViewMac::ImageForResource(resource_id);
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    const int resource_id = model_->IsStarredMatch(match)
+                                ? IDR_OMNIBOX_STAR
+                                : AutocompleteMatch::TypeToIcon(match.type);
+    return OmniboxViewMac::ImageForResource(resource_id);
+  }
+  bool is_dark_mode = !ignore_dark_theme && [matrix_ hasDarkTheme];
+  const SkColor icon_color =
+      is_dark_mode ? SkColorSetA(SK_ColorWHITE, 0xCC) : gfx::kChromeIconGrey;
+  const gfx::VectorIconId vector_icon_id =
+      model_->IsStarredMatch(match)
+          ? gfx::VectorIconId::LOCATION_BAR_STAR
+          : AutocompleteMatch::TypeToVectorIcon(match.type);
+  const int kIconSize = 16;
+  return NSImageFromImageSkia(
+      gfx::CreateVectorIcon(vector_icon_id, kIconSize, icon_color));
 }
 
 void OmniboxPopupViewMac::OpenURLForRow(size_t row,
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
index d82e4b67..ec31a5b 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -32,6 +32,8 @@
 #include "components/toolbar/toolbar_model.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/constants.h"
+#include "grit/components_scaled_resources.h"
+#include "grit/theme_resources.h"
 #import "skia/ext/skia_utils_mac.h"
 #import "third_party/mozilla/NSPasteboard+Utils.h"
 #include "ui/base/clipboard/clipboard.h"
@@ -86,29 +88,29 @@
                                    alpha:1.0];
 }
 
-NSColor* HostTextColor(bool inDarkMode) {
+NSColor* HostTextColor(bool in_dark_mode) {
   if (!ui::MaterialDesignController::IsModeMaterial()) {
     return [NSColor blackColor];
   }
-  return inDarkMode ? [NSColor whiteColor] : [NSColor blackColor];
+  return in_dark_mode ? [NSColor whiteColor] : [NSColor blackColor];
 }
-NSColor* SecureSchemeColor(bool inDarkMode) {
+NSColor* SecureSchemeColor(bool in_dark_mode) {
   if (!ui::MaterialDesignController::IsModeMaterial()) {
     return ColorWithRGBBytes(0x07, 0x95, 0x00);
   }
-  return inDarkMode ? [NSColor colorWithCalibratedWhite:1 alpha:0.5] :
-                      skia::SkColorToCalibratedNSColor(gfx::kGoogleGreen700);
+  return in_dark_mode ? [NSColor colorWithCalibratedWhite:1 alpha:0.5]
+                      : skia::SkColorToCalibratedNSColor(gfx::kGoogleGreen700);
 }
-NSColor* SecurityWarningSchemeColor(bool inDarkMode) {
-  return inDarkMode ? [NSColor colorWithCalibratedWhite:1 alpha:0.5] :
-                      skia::SkColorToCalibratedNSColor(gfx::kGoogleYellow700);
+NSColor* SecurityWarningSchemeColor(bool in_dark_mode) {
+  return in_dark_mode ? [NSColor colorWithCalibratedWhite:1 alpha:0.5]
+                      : skia::SkColorToCalibratedNSColor(gfx::kGoogleYellow700);
 }
-NSColor* SecurityErrorSchemeColor(bool inDarkMode) {
+NSColor* SecurityErrorSchemeColor(bool in_dark_mode) {
   if (!ui::MaterialDesignController::IsModeMaterial()) {
     return ColorWithRGBBytes(0xa2, 0x00, 0x00);
   }
-  return inDarkMode ? [NSColor colorWithCalibratedWhite:1 alpha:0.5] :
-                      skia::SkColorToCalibratedNSColor(gfx::kGoogleRed700);
+  return in_dark_mode ? [NSColor colorWithCalibratedWhite:1 alpha:0.5]
+                      : skia::SkColorToCalibratedNSColor(gfx::kGoogleRed700);
 }
 
 const char kOmniboxViewMacStateKey[] = "OmniboxViewMacState";
@@ -545,7 +547,7 @@
     NSMutableAttributedString* attributedString) {
   NSUInteger as_length = [attributedString length];
   NSRange as_entire_string = NSMakeRange(0, as_length);
-  bool inDarkMode = [[field_ window] inIncognitoModeWithSystemTheme];
+  bool in_dark_mode = [[field_ window] inIncognitoModeWithSystemTheme];
 
   ApplyTextStyle(attributedString);
 
@@ -564,12 +566,12 @@
   if (model()->CurrentTextIsURL() &&
       (host.is_nonempty() || grey_out_url)) {
     [attributedString addAttribute:NSForegroundColorAttributeName
-                             value:BaseTextColor(inDarkMode)
+                             value:BaseTextColor(in_dark_mode)
                              range:as_entire_string];
 
     if (!grey_out_url) {
       [attributedString addAttribute:NSForegroundColorAttributeName
-                               value:HostTextColor(inDarkMode)
+                               value:HostTextColor(in_dark_mode)
                                range:ComponentToNSRange(host)];
     }
   }
@@ -587,20 +589,20 @@
     NSColor* color;
     if (security_level == security_state::SecurityStateModel::EV_SECURE ||
         security_level == security_state::SecurityStateModel::SECURE) {
-      color = SecureSchemeColor(inDarkMode);
+      color = SecureSchemeColor(in_dark_mode);
     } else if (security_level ==
                security_state::SecurityStateModel::SECURITY_ERROR) {
-      color = SecurityErrorSchemeColor(inDarkMode);
+      color = SecurityErrorSchemeColor(in_dark_mode);
       // Add a strikethrough through the scheme.
       [attributedString addAttribute:NSStrikethroughStyleAttributeName
                  value:[NSNumber numberWithInt:NSUnderlineStyleSingle]
                  range:ComponentToNSRange(scheme)];
     } else if (security_level ==
                security_state::SecurityStateModel::SECURITY_WARNING) {
-      color = SecurityWarningSchemeColor(inDarkMode);
+      color = SecurityWarningSchemeColor(in_dark_mode);
     } else {
       NOTREACHED();
-      color = BaseTextColor(inDarkMode);
+      color = BaseTextColor(in_dark_mode);
     }
     [attributedString addAttribute:NSForegroundColorAttributeName
                              value:color
@@ -1101,7 +1103,13 @@
 
 NSFont* OmniboxViewMac::GetSmallFont(int style) {
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  return rb.GetFontList(ui::ResourceBundle::SmallFont)
+  if (!ui::MaterialDesignController::IsModeMaterial()) {
+    return rb.GetFontList(ui::ResourceBundle::SmallFont)
+        .Derive(1, style)
+        .GetPrimaryFont()
+        .GetNativeFont();
+  }
+  return rb.GetFontListWithDelta(-2, gfx::Font::NORMAL)
       .Derive(1, style)
       .GetPrimaryFont()
       .GetNativeFont();
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc
index 6b9348b3..b292d2c 100644
--- a/chrome/browser/ui/views/certificate_selector.cc
+++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/certificate_viewer.h"
@@ -39,8 +40,8 @@
 
 namespace chrome {
 
-const int CertificateSelector::kTableViewWidth = 400;
-const int CertificateSelector::kTableViewHeight = 100;
+const int CertificateSelector::kTableViewWidth = 500;
+const int CertificateSelector::kTableViewHeight = 150;
 
 class CertificateSelector::CertificateTableModel : public ui::TableModel {
  public:
@@ -58,6 +59,7 @@
     base::string16 subject;
     base::string16 issuer;
     base::string16 provider;
+    base::string16 serial;
   };
   std::vector<Row> rows_;
 
@@ -74,6 +76,10 @@
     row.subject = base::UTF8ToUTF16(cert->subject().GetDisplayName());
     row.issuer = base::UTF8ToUTF16(cert->issuer().GetDisplayName());
     row.provider = base::UTF8ToUTF16(provider_names[i]);
+    if (cert->serial_number().size() < std::numeric_limits<size_t>::max() / 2) {
+      row.serial = base::UTF8ToUTF16(base::HexEncode(
+          cert->serial_number().data(), cert->serial_number().size()));
+    }
     rows_.push_back(row);
   }
 }
@@ -96,6 +102,8 @@
       return row.issuer;
     case IDS_CERT_SELECTOR_PROVIDER_COLUMN:
       return row.provider;
+    case IDS_CERT_SELECTOR_SERIAL_COLUMN:
+      return row.serial;
     default:
       NOTREACHED();
   }
@@ -199,6 +207,8 @@
     columns.push_back(ui::TableColumn(IDS_CERT_SELECTOR_PROVIDER_COLUMN,
                                       ui::TableColumn::LEFT, -1, 0.4f));
   }
+  columns.push_back(ui::TableColumn(IDS_CERT_SELECTOR_SERIAL_COLUMN,
+                                    ui::TableColumn::LEFT, -1, 0.2f));
   table_ = new views::TableView(model_.get(), columns, views::TEXT_ONLY,
                                 true /* single_selection */);
   table_->SetObserver(this);
@@ -210,6 +220,10 @@
   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
 }
 
+ui::TableModel* CertificateSelector::table_model_for_testing() const {
+  return model_.get();
+}
+
 net::X509Certificate* CertificateSelector::GetSelectedCert() const {
   const int selected = table_->FirstSelectedRow();
   if (selected < 0)  // Nothing is selected in |table_|.
diff --git a/chrome/browser/ui/views/certificate_selector.h b/chrome/browser/ui/views/certificate_selector.h
index 1c01233..5eb701a2 100644
--- a/chrome/browser/ui/views/certificate_selector.h
+++ b/chrome/browser/ui/views/certificate_selector.h
@@ -24,6 +24,10 @@
 class View;
 }
 
+namespace ui {
+class TableModel;
+}
+
 namespace chrome {
 
 // A base class for dialogs that show a given list of certificates to the user.
@@ -79,6 +83,8 @@
   // certificate selection is.
   void InitWithText(std::unique_ptr<views::View> text_label);
 
+  ui::TableModel* table_model_for_testing() const;
+
  private:
   class CertificateTableModel;
 
diff --git a/chrome/browser/ui/views/certificate_selector_browsertest.cc b/chrome/browser/ui/views/certificate_selector_browsertest.cc
index e917720..5824ccc 100644
--- a/chrome/browser/ui/views/certificate_selector_browsertest.cc
+++ b/chrome/browser/ui/views/certificate_selector_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/grit/generated_resources.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"
@@ -18,6 +19,7 @@
 #include "net/cert/x509_certificate.h"
 #include "net/test/cert_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/models/table_model.h"
 #include "ui/views/controls/label.h"
 
 namespace {
@@ -55,6 +57,8 @@
     canceled_ = canceled;
   }
 
+  using chrome::CertificateSelector::table_model_for_testing;
+
   void set_on_destroy(base::Closure on_destroy) { on_destroy_ = on_destroy; }
 
  private:
@@ -102,6 +106,27 @@
 
 }  // namespace
 
+IN_PROC_BROWSER_TEST_F(CertificateSelectorTest, GetRowText) {
+  ui::TableModel* model = selector_->table_model_for_testing();
+  EXPECT_EQ(base::UTF8ToUTF16("Client Cert A"),
+            model->GetText(0, IDS_CERT_SELECTOR_SUBJECT_COLUMN));
+  EXPECT_EQ(base::UTF8ToUTF16("B CA"),
+            model->GetText(0, IDS_CERT_SELECTOR_ISSUER_COLUMN));
+  EXPECT_EQ(base::string16(),
+            model->GetText(0, IDS_CERT_SELECTOR_PROVIDER_COLUMN));
+  EXPECT_EQ(base::UTF8ToUTF16("1000"),
+            model->GetText(0, IDS_CERT_SELECTOR_SERIAL_COLUMN));
+
+  EXPECT_EQ(base::UTF8ToUTF16("Client Cert D"),
+            model->GetText(1, IDS_CERT_SELECTOR_SUBJECT_COLUMN));
+  EXPECT_EQ(base::UTF8ToUTF16("E CA"),
+            model->GetText(1, IDS_CERT_SELECTOR_ISSUER_COLUMN));
+  EXPECT_EQ(base::string16(),
+            model->GetText(1, IDS_CERT_SELECTOR_PROVIDER_COLUMN));
+  EXPECT_EQ(base::UTF8ToUTF16("1002"),
+            model->GetText(1, IDS_CERT_SELECTOR_SERIAL_COLUMN));
+}
+
 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,
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
index 50af6480..39378e3 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
@@ -604,7 +604,9 @@
 }
 
 void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
-  GetCloudPrintProxyService()->RefreshStatusFromService();
+  auto* service = GetCloudPrintProxyService();
+  if (service)
+    service->RefreshStatusFromService();
 }
 
 CloudPrintProxyService* LocalDiscoveryUIHandler::GetCloudPrintProxyService() {
diff --git a/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js b/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js
index 7dcf876..428016b 100644
--- a/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js
+++ b/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js
@@ -232,7 +232,8 @@
   }.bind(this));
 });
 
-TEST_F('BluetoothWebUITestAsync', 'testConnect', function() {
+// TODO(crbug.com/608126) Test is flaky.
+TEST_F('BluetoothWebUITestAsync', 'DISABLED_testConnect', function() {
   assertEquals(this.browsePreload, document.location.href);
 
   // Enable bluetooth.
diff --git a/chrome/browser_tests.isolate b/chrome/browser_tests.isolate
index 4e97ddb..cec61ef8 100644
--- a/chrome/browser_tests.isolate
+++ b/chrome/browser_tests.isolate
@@ -99,6 +99,8 @@
         'files': [
           '<(PRODUCT_DIR)/chrome_material_100_percent.pak',
           '<(PRODUCT_DIR)/chrome_100_percent.pak',
+          '<(PRODUCT_DIR)/chrome_material_200_percent.pak',
+          '<(PRODUCT_DIR)/chrome_200_percent.pak',
         ],
       },
     }],
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 239de656..6fc7db4 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1544,6 +1544,8 @@
       'browser/bookmarks/startup_task_runner_service_factory.h',
     ],
     'chrome_browser_offline_pages_sources': [
+      'browser/android/offline_pages/background_scheduler_bridge.cc',
+      'browser/android/offline_pages/background_scheduler_bridge.h',
       'browser/android/offline_pages/offline_page_bridge.cc',
       'browser/android/offline_pages/offline_page_bridge.h',
       'browser/android/offline_pages/offline_page_mhtml_archiver.cc',
@@ -1554,6 +1556,10 @@
       'browser/android/offline_pages/offline_page_tab_helper.h',
       'browser/android/offline_pages/offline_page_utils.cc',
       'browser/android/offline_pages/offline_page_utils.h',
+      'browser/android/offline_pages/prerendering_loader.cc',
+      'browser/android/offline_pages/prerendering_loader.h',
+      'browser/android/offline_pages/prerendering_offliner.cc',
+      'browser/android/offline_pages/prerendering_offliner.h',
       'browser/android/offline_pages/recent_tab_helper.cc',
       'browser/android/offline_pages/recent_tab_helper.h',
     ],
@@ -1958,6 +1964,7 @@
       'android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java',
       'android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java',
       'android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java',
+      'android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java',
       'android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java',
       'android/java/src/org/chromium/chrome/browser/omnibox/AnswersImage.java',
       'android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java',
diff --git a/chrome/interactive_ui_tests.isolate b/chrome/interactive_ui_tests.isolate
index e8a8815..a78e861 100644
--- a/chrome/interactive_ui_tests.isolate
+++ b/chrome/interactive_ui_tests.isolate
@@ -65,6 +65,8 @@
         'files': [
           '<(PRODUCT_DIR)/chrome_material_100_percent.pak',
           '<(PRODUCT_DIR)/chrome_100_percent.pak',
+          '<(PRODUCT_DIR)/chrome_material_200_percent.pak',
+          '<(PRODUCT_DIR)/chrome_200_percent.pak',
           '<(PRODUCT_DIR)/locales/en-US.pak',
           '<(PRODUCT_DIR)/locales/fr.pak',
           '<(PRODUCT_DIR)/resources.pak',
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 72929f8..32678c0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -316,6 +316,8 @@
       data += [
         "$root_out_dir/chrome_100_percent.pak",
         "$root_out_dir/chrome_material_100_percent.pak",
+        "$root_out_dir/chrome_200_percent.pak",
+        "$root_out_dir/chrome_material_200_percent.pak",
         "$root_out_dir/locales/en-US.pak",
         "$root_out_dir/locales/fr.pak",
       ]
@@ -862,6 +864,8 @@
       "$root_out_dir/browser_tests.pak",
       "$root_out_dir/chrome_100_percent.pak",
       "$root_out_dir/chrome_material_100_percent.pak",
+      "$root_out_dir/chrome_200_percent.pak",
+      "$root_out_dir/chrome_material_200_percent.pak",
       "$root_out_dir/locales/",
       "$root_out_dir/remoting/unittests/",
       "$root_out_dir/resources.pak",
@@ -1388,6 +1392,8 @@
       data += [
         "$root_out_dir/chrome_100_percent.pak",
         "$root_out_dir/chrome_material_100_percent.pak",
+        "$root_out_dir/chrome_200_percent.pak",
+        "$root_out_dir/chrome_material_200_percent.pak",
         "$root_out_dir/locales/en-US.pak",
       ]
     }
@@ -1596,7 +1602,11 @@
     ]
   }
   if (is_linux || is_win) {
-    data += [ "$root_out_dir/chrome_material_100_percent.pak" ]
+    data += [
+      "$root_out_dir/chrome_200_percent.pak",
+      "$root_out_dir/chrome_material_100_percent.pak",
+      "$root_out_dir/chrome_material_200_percent.pak",
+    ]
   }
 
   defines = []
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java
index 671c9b2..b4a1814 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java
@@ -18,6 +18,7 @@
 import org.chromium.base.test.BaseTestResult;
 import org.chromium.base.test.util.RestrictionSkipCheck;
 import org.chromium.base.test.util.SkipCheck;
+import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.test.util.ChromeRestriction;
 import org.chromium.chrome.test.util.DisableInTabbedMode;
@@ -68,9 +69,14 @@
                 return true;
             }
             if (TextUtils.equals(restriction,
-                                 ChromeRestriction.RESTRICTION_TYPE_GOOGLE_PLAY_SERVICES)
+                    ChromeRestriction.RESTRICTION_TYPE_GOOGLE_PLAY_SERVICES)
                     && (ConnectionResult.SUCCESS != GoogleApiAvailability.getInstance()
-                            .isGooglePlayServicesAvailable(getTargetContext()))) {
+                    .isGooglePlayServicesAvailable(getTargetContext()))) {
+                return true;
+            }
+            if (TextUtils.equals(restriction,
+                    ChromeRestriction.RESTRICTION_TYPE_OFFICIAL_BUILD)
+                    && (!ChromeVersionInfo.isOfficialBuild())) {
                 return true;
             }
             return false;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeRestriction.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeRestriction.java
index a67f8e25..efac204 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeRestriction.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeRestriction.java
@@ -15,4 +15,6 @@
     public static final String RESTRICTION_TYPE_PHONE = "Phone";
     /** Specifies the test is only valid on tablet form factors. */
     public static final String RESTRICTION_TYPE_TABLET = "Tablet";
+    /** Specifies the test is only valid on official build. */
+    public static final String RESTRICTION_TYPE_OFFICIAL_BUILD = "Official_Build";
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
index 296f441..8476705c 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
@@ -131,6 +131,7 @@
         private final Map<String, List<SuggestionsResult>> mSuggestions;
         private Runnable mSuggestionsDispatcher;
         private int mZeroSuggestCalledCount;
+        private boolean mStartAutocompleteCalled;
 
         public TestAutocompleteController(
                 View view,
@@ -145,6 +146,7 @@
         public void start(
                 Profile profile, String url ,
                 final String text, boolean preventInlineAutocomplete) {
+            mStartAutocompleteCalled = true;
             mSuggestionsDispatcher = new Runnable() {
                 @Override
                 public void run() {
@@ -173,6 +175,10 @@
             return mZeroSuggestCalledCount;
         }
 
+        public boolean isStartAutocompleteCalled() {
+            return mStartAutocompleteCalled;
+        }
+
         @Override
         public void stop(boolean clear) {
             if (mSuggestionsDispatcher != null) mView.removeCallbacks(mSuggestionsDispatcher);
diff --git a/chrome/test/data/webui/settings/people_page_test.js b/chrome/test/data/webui/settings/people_page_test.js
index 8a4bbe2..40a16ea 100644
--- a/chrome/test/data/webui/settings/people_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_test.js
@@ -47,11 +47,6 @@
 
         PolymerTest.clearBody();
         peoplePage = document.createElement('settings-people-page');
-        peoplePage.currentRoute = {
-          url: '/',
-          page: 'basic',
-          section: '',
-        };
         document.body.appendChild(peoplePage);
       });
 
diff --git a/chrome/unit_tests.isolate b/chrome/unit_tests.isolate
index ec9fe33..16c1eca 100644
--- a/chrome/unit_tests.isolate
+++ b/chrome/unit_tests.isolate
@@ -61,6 +61,13 @@
         ],
       },
     }],
+    ['OS=="linux" or OS=="win"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/chrome_200_percent.pak',
+        ],
+      },
+    }],
     ['OS=="linux"', {
       'variables': {
         'files': [
diff --git a/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc b/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
index bea332f3..4eeb7e91 100644
--- a/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
+++ b/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
@@ -188,6 +188,7 @@
   void TearDown() override {
     // Pipeline must be destroyed before finalizing media shlib.
     backend_.reset();
+    effects_backends_.clear();
     CastMediaShlib::Finalize();
   }
 
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index 3fb375a..73e1fac 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -100,11 +100,6 @@
   }
 }
 
-void CastContentRendererClient::AddKeySystems(
-    std::vector<::media::KeySystemInfo>* key_systems_info) {
-  AddChromecastKeySystemsInfo(key_systems_info);
-}
-
 void CastContentRendererClient::AddSupportedKeySystems(
     std::vector<std::unique_ptr<::media::KeySystemProperties>>*
         key_systems_properties) {
diff --git a/chromecast/renderer/cast_content_renderer_client.h b/chromecast/renderer/cast_content_renderer_client.h
index 245958e..b0bd2e4 100644
--- a/chromecast/renderer/cast_content_renderer_client.h
+++ b/chromecast/renderer/cast_content_renderer_client.h
@@ -34,8 +34,6 @@
   // ContentRendererClient implementation:
   void RenderThreadStarted() override;
   void RenderViewCreated(content::RenderView* render_view) override;
-  void AddKeySystems(
-      std::vector<::media::KeySystemInfo>* key_systems_info) override;
   void AddSupportedKeySystems(
       std::vector<std::unique_ptr<::media::KeySystemProperties>>*
           key_systems_properties) override;
diff --git a/chromecast/renderer/key_systems_cast.cc b/chromecast/renderer/key_systems_cast.cc
index 4acfd110..76646540 100644
--- a/chromecast/renderer/key_systems_cast.cc
+++ b/chromecast/renderer/key_systems_cast.cc
@@ -12,46 +12,75 @@
 #include "chromecast/media/base/key_systems_common.h"
 #include "components/cdm/renderer/widevine_key_system_properties.h"
 #include "media/base/eme_constants.h"
+#include "media/base/key_system_properties.h"
 
 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
 
+using ::media::EmeConfigRule;
 using ::media::EmeFeatureSupport;
+using ::media::EmeInitDataType;
+using ::media::EmeMediaType;
 using ::media::EmeRobustness;
 using ::media::EmeSessionTypeSupport;
+using ::media::SupportedCodecs;
 
 namespace chromecast {
 namespace shell {
 namespace {
 
 #if defined(PLAYREADY_CDM_AVAILABLE)
-void AddKeySystemWithCodecs(
-    const std::string& key_system_name,
-    std::vector<::media::KeySystemInfo>* key_systems_info) {
-  ::media::KeySystemInfo info;
-  info.key_system = key_system_name;
-  info.supported_init_data_types = ::media::kInitDataTypeMaskCenc;
-  info.supported_codecs =
-      ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1;
-  info.max_audio_robustness = EmeRobustness::EMPTY;
-  info.max_video_robustness = EmeRobustness::EMPTY;
+class PlayReadyKeySystemProperties : public ::media::KeySystemProperties {
+ public:
+  std::string GetKeySystemName() const override {
+    return media::kChromecastPlayreadyKeySystem;
+  }
+
+  bool IsSupportedInitDataType(EmeInitDataType init_data_type) const override {
+    return init_data_type == EmeInitDataType::CENC;
+  }
+
+  SupportedCodecs GetSupportedCodecs() const override {
+    return ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1;
+  }
+
+  EmeConfigRule GetRobustnessConfigRule(
+      EmeMediaType media_type,
+      const std::string& requested_robustness) const override {
+    return requested_robustness.empty() ? EmeConfigRule::SUPPORTED
+                                        : EmeConfigRule::NOT_SUPPORTED;
+  }
+
+  EmeSessionTypeSupport GetPersistentLicenseSessionSupport() const override {
 #if defined(OS_ANDROID)
-  info.persistent_license_support = EmeSessionTypeSupport::NOT_SUPPORTED;
+    return EmeSessionTypeSupport::NOT_SUPPORTED;
 #else
-  info.persistent_license_support = EmeSessionTypeSupport::SUPPORTED;
+    return EmeSessionTypeSupport::SUPPORTED;
 #endif
-  info.persistent_release_message_support =
-      EmeSessionTypeSupport::NOT_SUPPORTED;
-  info.persistent_state_support = EmeFeatureSupport::ALWAYS_ENABLED;
-  info.distinctive_identifier_support = EmeFeatureSupport::ALWAYS_ENABLED;
-  key_systems_info->push_back(info);
-}
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+  }
+
+  EmeSessionTypeSupport GetPersistentReleaseMessageSessionSupport()
+      const override {
+    return EmeSessionTypeSupport::NOT_SUPPORTED;
+  }
+
+  EmeFeatureSupport GetPersistentStateSupport() const override {
+    return EmeFeatureSupport::ALWAYS_ENABLED;
+  }
+  EmeFeatureSupport GetDistinctiveIdentifierSupport() const override {
+    return EmeFeatureSupport::ALWAYS_ENABLED;
+  }
+};
+#endif  // PLAYREADY_CDM_AVAILABLE
 
 }  // namespace
 
 void AddChromecastKeySystems(
     std::vector<std::unique_ptr<::media::KeySystemProperties>>*
         key_systems_properties) {
+#if defined(PLAYREADY_CDM_AVAILABLE)
+  key_systems_properties->emplace_back(new PlayReadyKeySystemProperties());
+#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+
 #if defined(WIDEVINE_CDM_AVAILABLE)
   ::media::SupportedCodecs codecs =
       ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1 |
@@ -71,13 +100,5 @@
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 }
 
-void AddChromecastKeySystemsInfo(
-    std::vector<::media::KeySystemInfo>* key_systems_info) {
-#if defined(PLAYREADY_CDM_AVAILABLE)
-  AddKeySystemWithCodecs(media::kChromecastPlayreadyKeySystem,
-                         key_systems_info);
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
-}
-
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/renderer/key_systems_cast.h b/chromecast/renderer/key_systems_cast.h
index 158518f8..351abef 100644
--- a/chromecast/renderer/key_systems_cast.h
+++ b/chromecast/renderer/key_systems_cast.h
@@ -8,8 +8,9 @@
 #include <memory>
 #include <vector>
 
-#include "media/base/key_system_info.h"
-#include "media/base/key_system_properties.h"
+namespace media {
+class KeySystemProperties;
+}
 
 namespace chromecast {
 namespace shell {
@@ -18,9 +19,6 @@
     std::vector<std::unique_ptr<::media::KeySystemProperties>>*
         key_systems_properties);
 
-void AddChromecastKeySystemsInfo(
-    std::vector<::media::KeySystemInfo>* key_systems_info);
-
 }  // namespace shell
 }  // namespace chromecast
 
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
index 0ce0b5430..59d9b32 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
@@ -17,23 +17,24 @@
  */
 @JNINamespace("cronet")
 class CronetLibraryLoader {
-    /**
-     * Synchronize access to sInitTaskPosted and initialization routine.
-     */
+    // Synchronize initialization.
     private static final Object sLoadLock = new Object();
     private static final String TAG = "CronetLibraryLoader";
-    private static boolean sInitTaskPosted = false;
+    // Has library loading commenced?  Setting guarded by sLoadLock.
+    private static volatile boolean sInitStarted = false;
+    // Has ensureMainThreadInitialized() completed?  Only accessed on main thread.
+    private static boolean sMainThreadInitDone = false;
 
     /**
      * Ensure that native library is loaded and initialized. Can be called from
      * any thread, the load and initialization is performed on main thread.
      */
-    public static void ensureInitialized(
-            final Context context, final CronetEngine.Builder builder) {
+    static void ensureInitialized(final Context context, final CronetEngine.Builder builder) {
         synchronized (sLoadLock) {
-            if (sInitTaskPosted) {
+            if (sInitStarted) {
                 return;
             }
+            sInitStarted = true;
             builder.loadLibrary();
             if (!Version.CRONET_VERSION.equals(nativeGetCronetVersion())) {
                 throw new RuntimeException(String.format(
@@ -48,7 +49,7 @@
             // Init native Chromium CronetEngine on Main UI thread.
             Runnable task = new Runnable() {
                 public void run() {
-                    initOnMainThread(context);
+                    ensureInitializedOnMainThread(context);
                 }
             };
             // Run task immediately or post it to the UI thread.
@@ -59,11 +60,20 @@
                 // to other tasks posted to the main thread.
                 new Handler(Looper.getMainLooper()).post(task);
             }
-            sInitTaskPosted = true;
         }
     }
 
-    private static void initOnMainThread(final Context context) {
+    /**
+     * Ensure that the main thread initialization has completed. Can only be called from
+     * the main thread. Ensures that the NetworkChangeNotifier is initialzied and the
+     * main thread native MessageLoop is initialized.
+     */
+    static void ensureInitializedOnMainThread(Context context) {
+        assert sInitStarted;
+        assert Looper.getMainLooper() == Looper.myLooper();
+        if (sMainThreadInitDone) {
+            return;
+        }
         NetworkChangeNotifier.init(context);
         // Registers to always receive network notifications. Note
         // that this call is fine for Cronet because Cronet
@@ -76,6 +86,7 @@
         // the undesired initial network change observer notification, which
         // will cause active requests to fail with ERR_NETWORK_CHANGED.
         nativeCronetInitOnMainThread();
+        sMainThreadInitDone = true;
     }
 
     // Native methods are implemented in cronet_library_loader.cc.
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 9fd00597..1c4140c 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
@@ -76,7 +76,7 @@
             new ObserverList<RequestFinishedListener>();
 
     @UsedByReflection("CronetEngine.java")
-    public CronetUrlRequestContext(CronetEngine.Builder builder) {
+    public CronetUrlRequestContext(final CronetEngine.Builder builder) {
         CronetLibraryLoader.ensureInitialized(builder.getContext(), builder);
         nativeSetMinLogLevel(getLoggingLevel());
         synchronized (mLock) {
@@ -91,6 +91,7 @@
         Runnable task = new Runnable() {
             @Override
             public void run() {
+                CronetLibraryLoader.ensureInitializedOnMainThread(builder.getContext());
                 synchronized (mLock) {
                     // mUrlRequestContextAdapter is guaranteed to exist until
                     // initialization on main and network threads completes and
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
index e73e820..f6e00533 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
@@ -23,12 +23,10 @@
 final class CronetFixedModeOutputStream extends CronetOutputStream {
     // CronetFixedModeOutputStream buffers up to this value and wait for UploadDataStream
     // to consume the data. This field is non-final, so it can be changed for tests.
-    // Using 2048 bytes is because the internal read buffer is 14520 for QUIC,
-    // 2852 for SPDY, and 16384 for normal stream. If a large value is used
-    // here, the buffer might not fit the internal buffer and compacting the buffer
-    // will be costly, see #read method below.
+    // Using 16384 bytes is because the internal read buffer is 14520 for QUIC,
+    // 16384 for SPDY, and 16384 for normal HTTP/1.1 stream.
     @VisibleForTesting
-    private static int sDefaultBufferLength = 2048;
+    private static int sDefaultBufferLength = 16384;
     private final CronetHttpURLConnection mConnection;
     private final MessageLoop mMessageLoop;
     private final long mContentLength;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 6807d80..0426d28 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -1126,4 +1126,30 @@
             assertTrue(loader.wasCalled());
         }
     }
+
+    // Creates a CronetEngine on another thread and then one on the main thread.  This shouldn't
+    // crash.
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testThreadedStartup() throws Exception {
+        final ConditionVariable otherThreadDone = new ConditionVariable();
+        final ConditionVariable uiThreadDone = new ConditionVariable();
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            public void run() {
+                final CronetEngine.Builder builder =
+                        new CronetEngine.Builder(getContext()).setLibraryName("cronet_tests");
+                new Thread() {
+                    public void run() {
+                        CronetEngine cronetEngine = builder.build();
+                        otherThreadDone.open();
+                        cronetEngine.shutdown();
+                    }
+                }.start();
+                otherThreadDone.block();
+                builder.build().shutdown();
+                uiThreadDone.open();
+            }
+        });
+        assertTrue(uiThreadDone.block(1000));
+    }
 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
index d90b4843..3dddc73 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
@@ -225,6 +225,7 @@
                 (HttpURLConnection) url.openConnection();
         connection.setDoOutput(true);
         connection.setRequestMethod("POST");
+        // largeData is 1.8 MB.
         byte[] largeData = TestUtil.getLargeData();
         connection.setFixedLengthStreamingMode(largeData.length);
         OutputStream out = connection.getOutputStream();
@@ -239,6 +240,7 @@
             }
             out.write(largeData, totalBytesWritten, bytesToWrite);
             totalBytesWritten += bytesToWrite;
+            // About 5th iteration of this loop, bytesToWrite will be bigger than 16384.
             bytesToWrite *= 2;
         }
         assertEquals(200, connection.getResponseCode());
@@ -277,7 +279,7 @@
             throws Exception {
         // Set an internal buffer of size larger than the buffer size used
         // in network stack internally.
-        // Normal stream uses 16384, QUIC uses 14520, and SPDY uses 2852.
+        // Normal stream uses 16384, QUIC uses 14520, and SPDY uses 16384.
         CronetFixedModeOutputStream.setDefaultBufferLengthForTesting(17384);
         testFixedLengthStreamingModeLargeDataWriteOneByte();
         testFixedLengthStreamingModeLargeData();
diff --git a/components/cronet/ios/cronet_bidirectional_stream.cc b/components/cronet/ios/cronet_bidirectional_stream.cc
index e229fcd1..c5e091d5 100644
--- a/components/cronet/ios/cronet_bidirectional_stream.cc
+++ b/components/cronet/ios/cronet_bidirectional_stream.cc
@@ -208,7 +208,9 @@
   DCHECK(!read_buffer_);
   if (read_state_ != WAITING_FOR_READ) {
     DLOG(ERROR) << "Unexpected Read Data in read_state " << WAITING_FOR_READ;
-    OnFailed(net::ERR_UNEXPECTED);
+    // Invoke OnFailed unless it is already invoked.
+    if (read_state_ != ERROR)
+      OnFailed(net::ERR_UNEXPECTED);
     return;
   }
   read_state_ = READING;
@@ -235,7 +237,9 @@
   DCHECK(!write_buffer_);
   if (write_state_ != WAITING_FOR_WRITE) {
     DLOG(ERROR) << "Unexpected Write Data in write_state " << write_state_;
-    OnFailed(net::ERR_UNEXPECTED);
+    // Invoke OnFailed unless it is already invoked.
+    if (write_state_ != ERROR)
+      OnFailed(net::ERR_UNEXPECTED);
     return;
   }
   write_state_ = WRITING;
diff --git a/components/cronet/ios/test/cronet_bidirectional_stream_test.mm b/components/cronet/ios/test/cronet_bidirectional_stream_test.mm
index 412b31b..6ddb2291 100644
--- a/components/cronet/ios/test/cronet_bidirectional_stream_test.mm
+++ b/components/cronet/ios/test/cronet_bidirectional_stream_test.mm
@@ -25,9 +25,6 @@
 #include "url/gurl.h"
 
 namespace {
-const char kTestServerHost[] = "test.example.com";
-const int kTestServerPort = 6121;
-const char kTestServerUrl[] = "https://test.example.com:6121";
 
 cronet_bidirectional_stream_header kTestHeaders[] = {
     {"header1", "foo"},
@@ -37,6 +34,8 @@
     2, 2, kTestHeaders};
 }  // namespace
 
+namespace cronet {
+
 class CronetBidirectionalStreamTest : public ::testing::Test {
  protected:
   CronetBidirectionalStreamTest() {}
@@ -49,11 +48,12 @@
       // Hack to work around issues with SetUp being called multiple times
       // during the test, and QuicTestServer not shutting down / restarting
       // gracefully.
-      cronet::CronetEnvironment::Initialize();
-      cronet::StartQuicTestServer();
+      CronetEnvironment::Initialize();
     }
 
-    cronet_environment_ = new cronet::CronetEnvironment("CronetTest/1.0.0.0");
+    StartQuicTestServer();
+
+    cronet_environment_ = new CronetEnvironment("CronetTest/1.0.0.0");
     cronet_environment_->set_http2_enabled(true);
     cronet_environment_->set_quic_enabled(true);
     cronet_environment_->set_ssl_key_log_file_name("SSLKEYLOGFILE");
@@ -66,7 +66,7 @@
     cronet_environment_->set_host_resolver_rules(
         "MAP test.example.com 127.0.0.1,"
         "MAP notfound.example.com ~NOTFOUND");
-    cronet_environment_->AddQuicHint(kTestServerHost, kTestServerPort,
+    cronet_environment_->AddQuicHint(kTestServerDomain, kTestServerPort,
                                      kTestServerPort);
 
     cronet_environment_->Start();
@@ -77,7 +77,7 @@
   }
 
   void TearDown() override {
-    // cronet::ShutdownQuicTestServer();
+    ShutdownQuicTestServer();
     cronet_environment_->StopNetLog();
     //[CronetEngine stopNetLog];
     //[CronetEngine uninstall];
@@ -86,18 +86,18 @@
   cronet_engine* engine() { return &cronet_engine_; }
 
  private:
-  static cronet::CronetEnvironment* cronet_environment_;
+  static CronetEnvironment* cronet_environment_;
   static cronet_engine cronet_engine_;
 };
 
-cronet::CronetEnvironment* CronetBidirectionalStreamTest::cronet_environment_;
+CronetEnvironment* CronetBidirectionalStreamTest::cronet_environment_;
 cronet_engine CronetBidirectionalStreamTest::cronet_engine_;
 
 class TestBidirectionalStreamCallback {
  public:
   enum ResponseStep {
     NOTHING,
-    ON_REQUEST_HEADERS_SENT,
+    ON_STREAM_READY,
     ON_RESPONSE_STARTED,
     ON_READ_COMPLETED,
     ON_WRITE_COMPLETED,
@@ -146,7 +146,8 @@
     return (TestBidirectionalStreamCallback*)stream->annotation;
   }
 
-  bool MaybeCancel(cronet_bidirectional_stream* stream, ResponseStep step) {
+  virtual bool MaybeCancel(cronet_bidirectional_stream* stream,
+                           ResponseStep step) {
     DCHECK_EQ(stream, this->stream);
     response_step = step;
     DLOG(WARNING) << "Step: " << step;
@@ -164,7 +165,7 @@
 
   void AddWriteData(const std::string& data) { write_data.push_back(data); }
 
-  void MaybeWriteNextData(cronet_bidirectional_stream* stream) {
+  virtual void MaybeWriteNextData(cronet_bidirectional_stream* stream) {
     DCHECK_EQ(stream, this->stream);
     if (write_data.empty())
       return;
@@ -177,10 +178,9 @@
 
  private:
   // C callbacks.
-  static void on_request_headers_sent_callback(
-      cronet_bidirectional_stream* stream) {
+  static void on_stream_ready_callback(cronet_bidirectional_stream* stream) {
     TestBidirectionalStreamCallback* test = FromStream(stream);
-    if (test->MaybeCancel(stream, ON_REQUEST_HEADERS_SENT))
+    if (test->MaybeCancel(stream, ON_STREAM_READY))
       return;
     test->MaybeWriteNextData(stream);
   }
@@ -230,6 +230,11 @@
       cronet_bidirectional_stream* stream,
       const cronet_bidirectional_stream_header_array* trailers) {
     TestBidirectionalStreamCallback* test = FromStream(stream);
+    for (size_t i = 0; i < trailers->count; ++i) {
+      test->response_trailers[trailers->headers[i].key] =
+          trailers->headers[i].value;
+    }
+
     if (test->MaybeCancel(stream, ON_TRAILERS))
       return;
   }
@@ -259,7 +264,7 @@
 
 cronet_bidirectional_stream_callback
     TestBidirectionalStreamCallback::s_callback = {
-        on_request_headers_sent_callback,
+        on_stream_ready_callback,
         on_response_headers_received_callback,
         on_read_completed_callback,
         on_write_completed_callback,
@@ -279,11 +284,14 @@
   cronet_bidirectional_stream_start(test.stream, kTestServerUrl, 0, "POST",
                                     &kTestHeadersArray, false);
   test.BlockForDone();
-  ASSERT_EQ(std::string("404"), test.response_headers[":status"]);
+  ASSERT_EQ(std::string(kHelloStatus), test.response_headers[kStatusHeader]);
+  ASSERT_EQ(std::string(kHelloHeaderValue),
+            test.response_headers[kHelloHeaderName]);
   ASSERT_EQ(TestBidirectionalStreamCallback::ON_SUCCEEDED, test.response_step);
-  ASSERT_EQ(std::string("fi"), test.read_data.front());
-  ASSERT_EQ(std::string("file not found"),
-            base::JoinString(test.read_data, ""));
+  ASSERT_EQ(std::string(kHelloBodyValue, 2), test.read_data.front());
+  ASSERT_EQ(std::string(kHelloBodyValue), base::JoinString(test.read_data, ""));
+  ASSERT_EQ(std::string(kHelloTrailerValue),
+            test.response_trailers[kHelloTrailerName]);
   cronet_bidirectional_stream_destroy(test.stream);
 }
 
@@ -296,8 +304,8 @@
   cronet_bidirectional_stream_start(test.stream, kTestServerUrl, 0, "POST",
                                     &kTestHeadersArray, true);
   test.BlockForDone();
-  ASSERT_EQ(std::string("404"), test.response_headers[":status"]);
-  ASSERT_EQ(std::string("file not found"), test.read_data.front());
+  ASSERT_EQ(std::string(kHelloStatus), test.response_headers[kStatusHeader]);
+  ASSERT_EQ(std::string(kHelloBodyValue), test.read_data.front());
   ASSERT_EQ(TestBidirectionalStreamCallback::ON_CANCELED, test.response_step);
   cronet_bidirectional_stream_destroy(test.stream);
 }
@@ -311,7 +319,7 @@
   cronet_bidirectional_stream_start(test.stream, kTestServerUrl, 0, "POST",
                                     &kTestHeadersArray, true);
   test.BlockForDone();
-  ASSERT_EQ(std::string("404"), test.response_headers[":status"]);
+  ASSERT_EQ(std::string(kHelloStatus), test.response_headers[kStatusHeader]);
   ASSERT_TRUE(test.read_data.empty());
   ASSERT_EQ(TestBidirectionalStreamCallback::ON_CANCELED, test.response_step);
   cronet_bidirectional_stream_destroy(test.stream);
@@ -326,8 +334,8 @@
   cronet_bidirectional_stream_start(test.stream, kTestServerUrl, 0, "POST",
                                     &kTestHeadersArray, true);
   test.BlockForDone();
-  ASSERT_EQ(std::string("404"), test.response_headers[":status"]);
-  ASSERT_EQ(std::string("file not found"), test.read_data.front());
+  ASSERT_EQ(std::string(kHelloStatus), test.response_headers[kStatusHeader]);
+  ASSERT_EQ(std::string(kHelloBodyValue), test.read_data.front());
   ASSERT_EQ(TestBidirectionalStreamCallback::ON_SUCCEEDED, test.response_step);
   cronet_bidirectional_stream_destroy(test.stream);
 }
@@ -347,6 +355,36 @@
   cronet_bidirectional_stream_destroy(test.stream);
 }
 
+TEST_F(CronetBidirectionalStreamTest,
+       StreamFailBeforeReadIsExecutedOnNetworkThread) {
+  class CustomTestBidirectionalStreamCallback
+      : public TestBidirectionalStreamCallback {
+    bool MaybeCancel(cronet_bidirectional_stream* stream,
+                     ResponseStep step) override {
+      if (step == ResponseStep::ON_READ_COMPLETED) {
+        // Shut down the server, and the stream should error out.
+        // The second call to ShutdownQuicTestServer is no-op.
+        ShutdownQuicTestServer();
+      }
+      return TestBidirectionalStreamCallback::MaybeCancel(stream, step);
+    }
+  };
+
+  CustomTestBidirectionalStreamCallback test;
+  test.AddWriteData("Hello, ");
+  test.AddWriteData("world!");
+  test.read_buffer_size = 2;
+  test.stream =
+      cronet_bidirectional_stream_create(engine(), &test, test.callback());
+  DCHECK(test.stream);
+  cronet_bidirectional_stream_start(test.stream, kTestServerUrl, 0, "POST",
+                                    &kTestHeadersArray, false);
+  test.BlockForDone();
+  ASSERT_EQ(TestBidirectionalStreamCallback::ON_FAILED, test.response_step);
+  ASSERT_EQ(net::ERR_QUIC_PROTOCOL_ERROR, test.net_error);
+  cronet_bidirectional_stream_destroy(test.stream);
+}
+
 TEST_F(CronetBidirectionalStreamTest, WriteFailsBeforeRequestStarted) {
   TestBidirectionalStreamCallback test;
   test.stream =
@@ -360,6 +398,36 @@
   cronet_bidirectional_stream_destroy(test.stream);
 }
 
+TEST_F(CronetBidirectionalStreamTest,
+       StreamFailBeforeWriteIsExecutedOnNetworkThread) {
+  class CustomTestBidirectionalStreamCallback
+      : public TestBidirectionalStreamCallback {
+    bool MaybeCancel(cronet_bidirectional_stream* stream,
+                     ResponseStep step) override {
+      if (step == ResponseStep::ON_WRITE_COMPLETED) {
+        // Shut down the server, and the stream should error out.
+        // The second call to ShutdownQuicTestServer is no-op.
+        ShutdownQuicTestServer();
+      }
+      return TestBidirectionalStreamCallback::MaybeCancel(stream, step);
+    }
+  };
+
+  CustomTestBidirectionalStreamCallback test;
+  test.AddWriteData("Test String");
+  test.AddWriteData("1234567890");
+  test.AddWriteData("woot!");
+  test.stream =
+      cronet_bidirectional_stream_create(engine(), &test, test.callback());
+  DCHECK(test.stream);
+  cronet_bidirectional_stream_start(test.stream, kTestServerUrl, 0, "POST",
+                                    &kTestHeadersArray, false);
+  test.BlockForDone();
+  ASSERT_EQ(TestBidirectionalStreamCallback::ON_FAILED, test.response_step);
+  ASSERT_EQ(net::ERR_QUIC_PROTOCOL_ERROR, test.net_error);
+  cronet_bidirectional_stream_destroy(test.stream);
+}
+
 TEST_F(CronetBidirectionalStreamTest, FailedResolution) {
   TestBidirectionalStreamCallback test;
   test.stream =
@@ -374,3 +442,5 @@
   ASSERT_EQ(net::ERR_NAME_NOT_RESOLVED, test.net_error);
   cronet_bidirectional_stream_destroy(test.stream);
 }
+
+}  // namespace cronet
diff --git a/components/cronet/ios/test/quic_test_server.cc b/components/cronet/ios/test/quic_test_server.cc
index 28f1b55..678da5f1 100644
--- a/components/cronet/ios/test/quic_test_server.cc
+++ b/components/cronet/ios/test/quic_test_server.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 "quic_test_server.h"
+#include "components/cronet/ios/test/quic_test_server.h"
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
@@ -14,34 +14,58 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/test_data_directory.h"
 #include "net/quic/crypto/proof_source_chromium.h"
+#include "net/spdy/spdy_header_block.h"
 #include "net/tools/quic/quic_in_memory_cache.h"
 #include "net/tools/quic/quic_simple_server.h"
 
 namespace cronet {
 
-static const int kServerPort = 6121;
+// This must match the certificate used (quic_test.example.com.crt and
+// quic_test.example.com.key.pkcs8).
+const char kTestServerDomain[] = "test.example.com";
+const int kTestServerPort = 6121;
+const char kTestServerHost[] = "test.example.com:6121";
+const char kTestServerUrl[] = "https://test.example.com:6121/hello.txt";
+
+const char kStatusHeader[] = ":status";
+
+const char kHelloPath[] = "/hello.txt";
+const char kHelloBodyValue[] = "Hello from QUIC Server";
+const char kHelloStatus[] = "200";
+
+const char kHelloHeaderName[] = "hello_header";
+const char kHelloHeaderValue[] = "hello header value";
+
+const char kHelloTrailerName[] = "hello_trailer";
+const char kHelloTrailerValue[] = "hello trailer value";
 
 base::Thread* g_quic_server_thread = nullptr;
 net::QuicSimpleServer* g_quic_server = nullptr;
 
+void SetupQuicInMemoryCache() {
+  static bool setup_done = false;
+  if (setup_done)
+    return;
+  setup_done = true;
+  net::SpdyHeaderBlock headers;
+  headers.ReplaceOrAppendHeader(kHelloHeaderName, kHelloHeaderValue);
+  headers.ReplaceOrAppendHeader(kStatusHeader, kHelloStatus);
+  net::SpdyHeaderBlock trailers;
+  trailers.ReplaceOrAppendHeader(kHelloTrailerName, kHelloTrailerValue);
+  net::QuicInMemoryCache::GetInstance()->AddResponse(
+      kTestServerHost, kHelloPath, headers, kHelloBodyValue, trailers);
+}
+
 void StartQuicServerOnServerThread(const base::FilePath& test_files_root,
                                    base::WaitableEvent* server_started_event) {
   DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread());
   DCHECK(!g_quic_server);
 
   // Set up in-memory cache.
-  /*
-  base::FilePath file_dir = test_files_root.Append("quic_data");
-  CHECK(base::PathExists(file_dir)) << "Quic data does not exist";
-  net::QuicInMemoryCache::GetInstance()->InitializeFromDirectory(
-      file_dir.value());
-   */
+  SetupQuicInMemoryCache();
   net::QuicConfig config;
-
   // Set up server certs.
   base::FilePath directory;
-  // CHECK(base::android::GetExternalStorageDirectory(&directory));
-  // directory = directory.Append("net/data/ssl/certificates");
   directory = test_files_root;
   // TODO(xunjieli): Use scoped_ptr when crbug.com/545474 is fixed.
   net::ProofSourceChromium* proof_source = new net::ProofSourceChromium();
@@ -54,7 +78,7 @@
 
   // Start listening.
   int rv = g_quic_server->Listen(
-      net::IPEndPoint(net::IPAddress::IPv4AllZeros(), kServerPort));
+      net::IPEndPoint(net::IPAddress::IPv4AllZeros(), kTestServerPort));
   CHECK_GE(rv, 0) << "Quic server fails to start";
   server_started_event->Signal();
 }
@@ -89,6 +113,8 @@
 }
 
 void ShutdownQuicTestServer() {
+  if (!g_quic_server_thread)
+    return;
   DCHECK(!g_quic_server_thread->task_runner()->BelongsToCurrentThread());
   base::WaitableEvent server_stopped_event(true, false);
   g_quic_server_thread->task_runner()->PostTask(
diff --git a/components/cronet/ios/test/quic_test_server.h b/components/cronet/ios/test/quic_test_server.h
index 2fae073..8b21cb2 100644
--- a/components/cronet/ios/test/quic_test_server.h
+++ b/components/cronet/ios/test/quic_test_server.h
@@ -11,6 +11,22 @@
 
 void ShutdownQuicTestServer();
 
+extern const char kTestServerDomain[];
+extern const int kTestServerPort;
+extern const char kTestServerHost[];
+extern const char kTestServerUrl[];
+
+extern const char kStatusHeader[];
+
+extern const char kHelloBodyValue[];
+extern const char kHelloStatus[];
+
+extern const char kHelloHeaderName[];
+extern const char kHelloHeaderValue[];
+
+extern const char kHelloTrailerName[];
+extern const char kHelloTrailerValue[];
+
 }  // namespace cronet
 
 #endif  // COMPONENTS_CRONET_IOS_TEST_QUIC_TEST_SERVER_H_
diff --git a/components/leveldb/leveldb_service_unittest.cc b/components/leveldb/leveldb_service_unittest.cc
index 06abe2a4..1f9b702 100644
--- a/components/leveldb/leveldb_service_unittest.cc
+++ b/components/leveldb/leveldb_service_unittest.cc
@@ -58,20 +58,10 @@
   DISALLOW_COPY_AND_ASSIGN(LevelDBServiceTest);
 };
 
-// TODO(crbug.com/602820) Test is flaky.
-#if defined(OS_LINUX) || defined(OS_WIN)
-#define MAYBE_Basic DISABLED_Basic
-#else
-#define MAYBE_Basic Basic
-#endif
-TEST_F(LevelDBServiceTest, MAYBE_Basic) {
-  filesystem::DirectoryPtr directory;
-  GetUserDataDir(&directory);
-
+TEST_F(LevelDBServiceTest, Basic) {
   DatabaseError error;
   LevelDBDatabasePtr database;
-  leveldb()->Open(std::move(directory), "test", GetProxy(&database),
-                  Capture(&error));
+  leveldb()->OpenInMemory(GetProxy(&database), Capture(&error));
   ASSERT_TRUE(leveldb().WaitForIncomingResponse());
   EXPECT_EQ(DatabaseError::OK, error);
 
@@ -109,13 +99,7 @@
   EXPECT_EQ("", value.To<std::string>());
 }
 
-// TODO(crbug.com/602820) Test is flaky.
-#if defined(OS_LINUX) || defined(OS_WIN)
-#define MAYBE_WriteBatch DISABLED_WriteBatch
-#else
-#define MAYBE_WriteBatch WriteBatch
-#endif
-TEST_F(LevelDBServiceTest, MAYBE_WriteBatch) {
+TEST_F(LevelDBServiceTest, WriteBatch) {
   DatabaseError error;
   LevelDBDatabasePtr database;
   leveldb()->OpenInMemory(GetProxy(&database), Capture(&error));
@@ -255,21 +239,10 @@
   }
 }
 
-// TODO(crbug.com/602820) Test is flaky.
-#if defined(OS_LINUX) || defined(OS_WIN)
-#define MAYBE_GetSnapshotSimple DISABLED_GetSnapshotSimple
-#else
-#define MAYBE_GetSnapshotSimple GetSnapshotSimple
-#endif
-TEST_F(LevelDBServiceTest, MAYBE_GetSnapshotSimple) {
+TEST_F(LevelDBServiceTest, GetSnapshotSimple) {
   DatabaseError error;
-
-  filesystem::DirectoryPtr directory;
-  GetUserDataDir(&directory);
-
   LevelDBDatabasePtr database;
-  leveldb()->Open(std::move(directory), "test", GetProxy(&database),
-                  Capture(&error));
+  leveldb()->OpenInMemory(GetProxy(&database), Capture(&error));
   ASSERT_TRUE(leveldb().WaitForIncomingResponse());
   EXPECT_EQ(DatabaseError::OK, error);
 
@@ -279,21 +252,10 @@
   EXPECT_NE(static_cast<uint64_t>(0), snapshot_id);
 }
 
-// TODO(crbug.com/602820) Test is flaky.
-#if defined(OS_LINUX) || defined(OS_WIN)
-#define MAYBE_GetFromSnapshots DISABLED_GetFromSnapshots
-#else
-#define MAYBE_GetFromSnapshots GetFromSnapshots
-#endif
-TEST_F(LevelDBServiceTest, MAYBE_GetFromSnapshots) {
+TEST_F(LevelDBServiceTest, GetFromSnapshots) {
   DatabaseError error;
-
-  filesystem::DirectoryPtr directory;
-  GetUserDataDir(&directory);
-
   LevelDBDatabasePtr database;
-  leveldb()->Open(std::move(directory), "test", GetProxy(&database),
-                  Capture(&error));
+  leveldb()->OpenInMemory(GetProxy(&database), Capture(&error));
   ASSERT_TRUE(leveldb().WaitForIncomingResponse());
   EXPECT_EQ(DatabaseError::OK, error);
 
@@ -339,20 +301,10 @@
   EXPECT_EQ("value", value.To<std::string>());
 }
 
-// TODO(crbug.com/602820) Test is flaky.
-#if defined(OS_LINUX) || defined(OS_WIN)
-#define MAYBE_InvalidArgumentOnInvalidSnapshot DISABLED_InvalidArgumentOnInvalidSnapshot
-#else
-#define MAYBE_InvalidArgumentOnInvalidSnapshot InvalidArgumentOnInvalidSnapshot
-#endif
-TEST_F(LevelDBServiceTest, MAYBE_InvalidArgumentOnInvalidSnapshot) {
-  filesystem::DirectoryPtr directory;
-  GetUserDataDir(&directory);
-
+TEST_F(LevelDBServiceTest, InvalidArgumentOnInvalidSnapshot) {
   LevelDBDatabasePtr database;
   DatabaseError error = DatabaseError::INVALID_ARGUMENT;
-  leveldb()->Open(std::move(directory), "test", GetProxy(&database),
-                  Capture(&error));
+  leveldb()->OpenInMemory(GetProxy(&database), Capture(&error));
   ASSERT_TRUE(leveldb().WaitForIncomingResponse());
   EXPECT_EQ(DatabaseError::OK, error);
 
diff --git a/components/mus/public/cpp/tests/BUILD.gn b/components/mus/public/cpp/tests/BUILD.gn
index 4c58e664..ccf65383 100644
--- a/components/mus/public/cpp/tests/BUILD.gn
+++ b/components/mus/public/cpp/tests/BUILD.gn
@@ -33,7 +33,6 @@
     "//components/mus/public/cpp",
     "//services/shell/public/cpp:shell_test_support",
     "//services/shell/public/cpp:sources",
-    "//services/shell/public/cpp:test_support",
     "//testing/gtest",
     "//ui/gl",
   ]
diff --git a/components/mus/ws/BUILD.gn b/components/mus/ws/BUILD.gn
index a524b21b..ef7393a1 100644
--- a/components/mus/ws/BUILD.gn
+++ b/components/mus/ws/BUILD.gn
@@ -206,7 +206,6 @@
     "//mojo/public/cpp/bindings:bindings",
     "//services/shell/public/cpp:shell_test_support",
     "//services/shell/public/cpp:sources",
-    "//services/shell/public/cpp:test_support",
     "//services/shell/public/cpp/test:run_all_shelltests",
     "//services/shell/public/interfaces",
     "//testing/gtest",
diff --git a/components/offline_pages/background/scheduler.h b/components/offline_pages/background/scheduler.h
index 76a185f..8aab4c3 100644
--- a/components/offline_pages/background/scheduler.h
+++ b/components/offline_pages/background/scheduler.h
@@ -20,13 +20,14 @@
   Scheduler() {}
   virtual ~Scheduler() {}
 
-  // Ensures that the system has a task scheduled for |trigger_conditions|.
+  // Schedules the triggering of a task subject to |trigger_conditions|.
   // This may overwrite any previous scheduled task with a new one for
-  // these conditions.
-  virtual void EnsureScheduled(const TriggerCondition& trigger_condition) = 0;
+  // these conditions. That is, only one set of triggering conditions
+  // is scheduled at a time.
+  virtual void Schedule(const TriggerCondition& trigger_condition) = 0;
 
-  // Clears the currently scheduled task, if any.
-  virtual void ClearScheduled() = 0;
+  // Unschedules the currently scheduled task, if any.
+  virtual void Unschedule() = 0;
 };
 
 }  // namespace offline_pages
diff --git a/components/onc/docs/onc_spec.html b/components/onc/docs/onc_spec.html
index c28053f..2ef2344 100644
--- a/components/onc/docs/onc_spec.html
+++ b/components/onc/docs/onc_spec.html
@@ -408,7 +408,7 @@
         <span class="rule_id"></span>
         Allowed values are <span class="value">Cellular</span>,
         <span class="value">Ethernet</span>, <span class="value">WiFi</span>,
-        <span class="value">Cellular</span> and <span class="value">VPN</span>.
+        <span class="value">WiMAX</span> and <span class="value">VPN</span>.
       </span>
       Indicates which kind of connection this is.
     </dd>
diff --git a/components/os_crypt.gypi b/components/os_crypt.gypi
index e5b38f1..88ba67c7 100644
--- a/components/os_crypt.gypi
+++ b/components/os_crypt.gypi
@@ -50,6 +50,18 @@
             },
           },
         }],
+        ['OS=="linux" and chromeos!=1', {
+          'sources': [ 
+            'os_crypt/libsecret_util_posix.cc',
+            'os_crypt/libsecret_util_posix.h',
+          ],
+          'defines': [
+            'USE_LIBSECRET',
+          ],
+          'include_dirs' : [
+            '../third_party/libsecret/'
+          ],
+        }],
       ],
       'target_conditions': [
         ['OS=="ios"', {
diff --git a/components/os_crypt/BUILD.gn b/components/os_crypt/BUILD.gn
index 37adbe9..33bab6e 100644
--- a/components/os_crypt/BUILD.gn
+++ b/components/os_crypt/BUILD.gn
@@ -40,6 +40,15 @@
   if (is_win) {
     libs = [ "crypt32.lib" ]
   }
+
+  if (is_desktop_linux) {
+    sources += [
+      "libsecret_util_posix.cc",
+      "libsecret_util_posix.h",
+    ]
+    deps += [ "//third_party/libsecret" ]
+    defines = [ "USE_LIBSECRET" ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/components/os_crypt/libsecret_util_posix.cc b/components/os_crypt/libsecret_util_posix.cc
new file mode 100644
index 0000000..58ba9d0
--- /dev/null
+++ b/components/os_crypt/libsecret_util_posix.cc
@@ -0,0 +1,134 @@
+// Copyright 2016 The Chromium 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/os_crypt/libsecret_util_posix.h"
+
+#include <dlfcn.h>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+
+decltype(
+    &::secret_password_store_sync) LibsecretLoader::secret_password_store_sync;
+decltype(
+    &::secret_service_search_sync) LibsecretLoader::secret_service_search_sync;
+decltype(
+    &::secret_password_clear_sync) LibsecretLoader::secret_password_clear_sync;
+decltype(&::secret_item_get_secret) LibsecretLoader::secret_item_get_secret;
+decltype(&::secret_value_get_text) LibsecretLoader::secret_value_get_text;
+decltype(
+    &::secret_item_get_attributes) LibsecretLoader::secret_item_get_attributes;
+decltype(&::secret_item_load_secret_sync)
+    LibsecretLoader::secret_item_load_secret_sync;
+decltype(&::secret_value_unref) LibsecretLoader::secret_value_unref;
+
+bool LibsecretLoader::libsecret_loaded_ = false;
+
+const LibsecretLoader::FunctionInfo LibsecretLoader::kFunctions[] = {
+    {"secret_password_store_sync",
+     reinterpret_cast<void**>(&secret_password_store_sync)},
+    {"secret_service_search_sync",
+     reinterpret_cast<void**>(&secret_service_search_sync)},
+    {"secret_password_clear_sync",
+     reinterpret_cast<void**>(&secret_password_clear_sync)},
+    {"secret_item_get_secret",
+     reinterpret_cast<void**>(&secret_item_get_secret)},
+    {"secret_value_get_text", reinterpret_cast<void**>(&secret_value_get_text)},
+    {"secret_item_get_attributes",
+     reinterpret_cast<void**>(&secret_item_get_attributes)},
+    {"secret_item_load_secret_sync",
+     reinterpret_cast<void**>(&secret_item_load_secret_sync)},
+    {"secret_value_unref", reinterpret_cast<void**>(&secret_value_unref)}};
+
+// static
+bool LibsecretLoader::EnsureLibsecretLoaded() {
+  return LoadLibsecret() && LibsecretIsAvailable();
+}
+
+// static
+bool LibsecretLoader::LoadLibsecret() {
+  if (libsecret_loaded_)
+    return true;
+
+  void* handle = dlopen("libsecret-1.so.0", RTLD_NOW | RTLD_GLOBAL);
+  if (!handle) {
+    // We wanted to use libsecret, but we couldn't load it. Warn, because
+    // either the user asked for this, or we autodetected it incorrectly. (Or
+    // the system has broken libraries, which is also good to warn about.)
+    // TODO(crbug.com/607435): Channel this message to the user-facing log
+    VLOG(1) << "Could not load libsecret-1.so.0: " << dlerror();
+    return false;
+  }
+
+  for (const auto& function : kFunctions) {
+    dlerror();
+    *function.pointer = dlsym(handle, function.name);
+    const char* error = dlerror();
+    if (error) {
+      VLOG(1) << "Unable to load symbol " << function.name << ": " << error;
+      dlclose(handle);
+      return false;
+    }
+  }
+
+  libsecret_loaded_ = true;
+  // We leak the library handle. That's OK: this function is called only once.
+  return true;
+}
+
+// static
+bool LibsecretLoader::LibsecretIsAvailable() {
+  if (!libsecret_loaded_)
+    return false;
+  // A dummy query is made to check for availability, because libsecret doesn't
+  // have a dedicated availability function. For performance reasons, the query
+  // is meant to return an empty result.
+  LibsecretAttributesBuilder attrs;
+  attrs.Append("application", "chrome-string_to_get_empty_result");
+  const SecretSchema kDummySchema = {
+      "_chrome_dummy_schema",
+      SECRET_SCHEMA_DONT_MATCH_NAME,
+      {{"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
+       {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}};
+
+  GError* error = nullptr;
+  GList* found =
+      secret_service_search_sync(nullptr,  // default secret service
+                                 &kDummySchema, attrs.Get(), SECRET_SEARCH_ALL,
+                                 nullptr,  // no cancellable ojbect
+                                 &error);
+  bool success = (error == nullptr);
+  if (error)
+    g_error_free(error);
+  if (found)
+    g_list_free(found);
+
+  return success;
+}
+
+LibsecretAttributesBuilder::LibsecretAttributesBuilder() {
+  attrs_ = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                 nullptr,   // no deleter for keys
+                                 nullptr);  // no deleter for values
+}
+
+LibsecretAttributesBuilder::~LibsecretAttributesBuilder() {
+  g_hash_table_destroy(attrs_);
+}
+
+void LibsecretAttributesBuilder::Append(const std::string& name,
+                                        const std::string& value) {
+  name_values_.push_back(name);
+  gpointer name_str =
+      static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
+  name_values_.push_back(value);
+  gpointer value_str =
+      static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
+  g_hash_table_insert(attrs_, name_str, value_str);
+}
+
+void LibsecretAttributesBuilder::Append(const std::string& name,
+                                        int64_t value) {
+  Append(name, base::Int64ToString(value));
+}
diff --git a/components/os_crypt/libsecret_util_posix.h b/components/os_crypt/libsecret_util_posix.h
new file mode 100644
index 0000000..0d265be
--- /dev/null
+++ b/components/os_crypt/libsecret_util_posix.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium 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_OS_CRYPT_LIBSECRET_UTIL_POSIX_H_
+#define COMPONENTS_OS_CRYPT_LIBSECRET_UTIL_POSIX_H_
+
+#include <libsecret/secret.h>
+
+#include <list>
+#include <string>
+
+#include "base/macros.h"
+
+// Utility for dynamically loading libsecret.
+class LibsecretLoader {
+ public:
+  static decltype(&::secret_password_store_sync) secret_password_store_sync;
+  static decltype(&::secret_service_search_sync) secret_service_search_sync;
+  static decltype(&::secret_password_clear_sync) secret_password_clear_sync;
+  static decltype(&::secret_item_get_secret) secret_item_get_secret;
+  static decltype(&::secret_value_get_text) secret_value_get_text;
+  static decltype(&::secret_item_get_attributes) secret_item_get_attributes;
+  static decltype(&::secret_item_load_secret_sync) secret_item_load_secret_sync;
+  static decltype(&::secret_value_unref) secret_value_unref;
+
+  // Loads the libsecret library and checks that it responds to queries.
+  // Returns false if either step fails.
+  // Repeated calls check the responsiveness every time, but do not load the
+  // the library again if already successful.
+  static bool EnsureLibsecretLoaded();
+
+ protected:
+  static bool libsecret_loaded_;
+
+ private:
+  struct FunctionInfo {
+    const char* name;
+    void** pointer;
+  };
+
+  static const FunctionInfo kFunctions[];
+
+  // Load the libsecret binaries. Returns true on success.
+  // If successful, the result is cached and the function can be safely called
+  // multiple times.
+  // Checking |LibsecretIsAvailable| is necessary after this to verify that the
+  // service responds to queries.
+  static bool LoadLibsecret();
+
+  // True if the libsecret binaries have been loaded and the library responds
+  // to queries.
+  static bool LibsecretIsAvailable();
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(LibsecretLoader);
+};
+
+class LibsecretAttributesBuilder {
+ public:
+  LibsecretAttributesBuilder();
+  ~LibsecretAttributesBuilder();
+
+  void Append(const std::string& name, const std::string& value);
+
+  void Append(const std::string& name, int64_t value);
+
+  // GHashTable, its keys and values returned from Get() are destroyed in
+  // |LibsecretAttributesBuilder| destructor.
+  GHashTable* Get() { return attrs_; }
+
+ private:
+  // |name_values_| is a storage for strings referenced in |attrs_|.
+  // TODO(crbug.com/607950): Make implementation more robust by not depending on
+  // the implementation details of containers. External objects keep references
+  // to the objects stored in this container. Using a vector here will fail the
+  // ASan tests, because it may move the objects and break the references.
+  std::list<std::string> name_values_;
+  GHashTable* attrs_;
+
+  DISALLOW_COPY_AND_ASSIGN(LibsecretAttributesBuilder);
+};
+
+#endif  // COMPONENTS_OS_CRYPT_LIBSECRET_UTIL_POSIX_H_
diff --git a/components/rappor/rappor_parameters.h b/components/rappor/rappor_parameters.h
index 2a8bae7..1696269f 100644
--- a/components/rappor/rappor_parameters.h
+++ b/components/rappor/rappor_parameters.h
@@ -13,17 +13,28 @@
 enum NoiseLevel {
   NO_NOISE = 0,
   NORMAL_NOISE,
+  SPARSE_NOISE,
   NUM_NOISE_LEVELS,
 };
 
 // The type of data stored in a metric.
+// Any use of the LOW_FREQUENCY types must be approved by Chrome Privacy and
+// the rappor-dev team.
 enum RapporType {
   // Generic metrics from UMA opt-in users.
   UMA_RAPPOR_TYPE = 0,
-  // Generic metrics for SafeBrowsing users.
+  // Generic metrics for SafeBrowsing users. Deprecated, replaced by
+  // LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE.
   SAFEBROWSING_RAPPOR_TYPE,
   // Deprecated: Use UMA_RAPPOR_TYPE for new metrics
   ETLD_PLUS_ONE_RAPPOR_TYPE,
+  // Type for low-frequency metrics from UMA opt-in users.
+  LOW_FREQUENCY_UMA_RAPPOR_TYPE,
+  // Type for low-frequency metrics from SafeBrowsing users.
+  LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE,
+  // Type for low-frequency metrics from UMA opt-in users. Do not use for new
+  // metrics.
+  LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
   NUM_RAPPOR_TYPES,
   COARSE_RAPPOR_TYPE = SAFEBROWSING_RAPPOR_TYPE,
 };
@@ -49,13 +60,16 @@
 
 // An object describing noise probabilities for a noise level
 struct NoiseParameters {
-  // The probability that a bit will be redacted with fake data.
+  // The probability that a bit will be redacted with fake data. This
+  // corresponds to the F privacy parameter.
   Probability fake_prob;
   // The probability that a fake bit will be a one.
   Probability fake_one_prob;
-  // The probability that a one bit in the redacted data reports as one.
+  // The probability that a one bit in the redacted data reports as one. This
+  // corresponds to the Q privacy parameter
   Probability one_coin_prob;
-  // The probability that a zero bit in the redacted data reports as one.
+  // The probability that a zero bit in the redacted data reports as one. This
+  // corresponds to the P privacy parameter.
   Probability zero_coin_prob;
 };
 
@@ -104,6 +118,13 @@
      rappor::PROBABILITY_75 /* One coin probability */,
      rappor::PROBABILITY_25 /* Zero coin probability */,
     },
+    // SPARSE_NOISE
+    {
+     rappor::PROBABILITY_25 /* Fake data probability */,
+     rappor::PROBABILITY_50 /* Fake one probability */,
+     rappor::PROBABILITY_75 /* One coin probability */,
+     rappor::PROBABILITY_25 /* Zero coin probability */,
+    },
 };
 
 const RapporParameters kRapporParametersForType[NUM_RAPPOR_TYPES] = {
@@ -125,6 +146,24 @@
      2 /* Bloom filter hash count */,
      rappor::NORMAL_NOISE /* Noise level */,
      UMA_RAPPOR_GROUP /* Recording group */},
+    // LOW_FREQUENCY_UMA_RAPPOR_TYPE
+    {128 /* Num cohorts */,
+     4 /* Bloom filter size bytes */,
+     2 /* Bloom filter hash count */,
+     rappor::SPARSE_NOISE /* Noise level */,
+     UMA_RAPPOR_GROUP /* Recording group */},
+    // LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE
+    {128 /* Num cohorts */,
+     1 /* Bloom filter size bytes */,
+     2 /* Bloom filter hash count */,
+     rappor::SPARSE_NOISE /* Noise level */,
+     SAFEBROWSING_RAPPOR_GROUP /* Recording group */},
+    // LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE
+    {128 /* Num cohorts */,
+     16 /* Bloom filter size bytes */,
+     2 /* Bloom filter hash count */,
+     rappor::SPARSE_NOISE /* Noise level */,
+     UMA_RAPPOR_GROUP /* Recording group */},
 };
 
 }  // namespace internal
diff --git a/components/resources/autofill_scaled_resources.grdp b/components/resources/autofill_scaled_resources.grdp
index bc1d884c..2d688c6 100644
--- a/components/resources/autofill_scaled_resources.grdp
+++ b/components/resources/autofill_scaled_resources.grdp
@@ -13,6 +13,13 @@
     <structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_SCAN_NEW" file="autofill/cc-generic.png" />
     <structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_SCAN_NEW_KEYBOARD_ACCESSORY" file="autofill/cc-generic.png" />
     <structure type="chrome_scaled_image" name="IDR_AUTOFILL_SETTINGS" file="autofill/cc-generic.png" />
+
+    <!-- PaymentRequest image variants -->
+    <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_AMEX" file="autofill/cc-generic.png" />
+    <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_DISCOVER" file="autofill/cc-generic.png" />
+    <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_GENERIC" file="autofill/cc-generic.png" />
+    <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_MASTERCARD" file="autofill/cc-generic.png" />
+    <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_VISA" file="autofill/cc-generic.png" />
   </if>
 
   <structure type="chrome_scaled_image" name="IDR_CREDIT_CARD_CVC_HINT" file="autofill/credit_card_cvc_hint.png" />
diff --git a/components/security_interstitials/core/metrics_helper.cc b/components/security_interstitials/core/metrics_helper.cc
index ee1724c2..17073b3c 100644
--- a/components/security_interstitials/core/metrics_helper.cc
+++ b/components/security_interstitials/core/metrics_helper.cc
@@ -114,6 +114,8 @@
 MetricsHelper::ReportDetails::ReportDetails(const ReportDetails& other) =
     default;
 
+MetricsHelper::ReportDetails::~ReportDetails() {}
+
 MetricsHelper::MetricsHelper(
   const GURL& request_url,
   const ReportDetails settings,
@@ -148,7 +150,10 @@
   }
 
   MaybeRecordDecisionAsAction(decision, settings_.metric_prefix);
-  RecordUserDecisionToRappor(decision);
+  RecordUserDecisionToRappor(decision, settings_.rappor_report_type,
+                             settings_.rappor_prefix);
+  RecordUserDecisionToRappor(decision, settings_.deprecated_rappor_report_type,
+                             settings_.deprecated_rappor_prefix);
   RecordExtraUserDecisionMetrics(decision);
 }
 
@@ -163,18 +168,20 @@
   }
 }
 
-void MetricsHelper::RecordUserDecisionToRappor(Decision decision) {
+void MetricsHelper::RecordUserDecisionToRappor(
+    Decision decision,
+    const rappor::RapporType rappor_report_type,
+    const std::string& rappor_prefix) {
   if (!rappor_service_ || (decision != PROCEED && decision != DONT_PROCEED))
     return;
 
   std::unique_ptr<rappor::Sample> sample =
-      rappor_service_->CreateSample(settings_.rappor_report_type);
+      rappor_service_->CreateSample(rappor_report_type);
 
-  // This will populate, for example, "intersitial.malware.domain" or
-  // "interstitial.ssl2.domain".  |domain| will be empty for hosts w/o TLDs.
-  const std::string domain =
-      rappor::GetDomainAndRegistrySampleFromGURL(request_url_);
-  sample->SetStringField("domain", domain);
+  // This will populate, for example, "intersitial.malware2.domain" or
+  // "interstitial.ssl3.domain". The domain will be empty for hosts w/o TLDs.
+  sample->SetStringField(
+      "domain", rappor::GetDomainAndRegistrySampleFromGURL(request_url_));
 
   // Only report history and decision if we have history data.
   if (num_visits_ >= 0) {
@@ -187,7 +194,7 @@
     sample->SetFlagsField("flags", flags,
                           InterstitialFlagBits::HIGHEST_USED_BIT + 1);
   }
-  rappor_service_->RecordSampleObj("interstitial." + settings_.rappor_prefix,
+  rappor_service_->RecordSampleObj("interstitial." + rappor_prefix,
                                    std::move(sample));
 }
 
diff --git a/components/security_interstitials/core/metrics_helper.h b/components/security_interstitials/core/metrics_helper.h
index f265f1b9..7f41376 100644
--- a/components/security_interstitials/core/metrics_helper.h
+++ b/components/security_interstitials/core/metrics_helper.h
@@ -21,11 +21,12 @@
 namespace security_interstitials {
 
 // MetricsHelper records user warning interactions in a common way via METRICS
-// histograms and, optionally, RAPPOR metrics. The  class will generate the
+// histograms and, optionally, RAPPOR metrics. The class will generate the
 // following histograms:
 //   METRICS: interstitial.<metric_prefix>.decision[.repeat_visit]
 //   METRICS: interstitial.<metric_prefix>.interaction[.repeat_visi]
-//   RAPPOR:  interstitial.<rappor_prefix>
+//   RAPPOR:  interstitial.<rappor_prefix> (SafeBrowsing parameters)
+//   RAPPOR:  interstitial.<rappor_prefix>2 (Low frequency parameters)
 // wherein |metric_prefix| and |rappor_prefix| are specified via ReportDetails.
 // repeat_visit is also generated if the user has seen the page before.
 //
@@ -63,16 +64,25 @@
   //               placing at the end of the metric name.  Examples:
   //               "from_datasaver", "from_device"
   // rappor_prefix: Metric prefix for Rappor.
-  //                examples: "phishing", "ssl2"
-  // rappor_report_type: Used to differentiate UMA and Safe Browsing statistics.
+  //                examples: "phishing2", "ssl3"
+  // rappor_report_type: Specifies the low-frequency RAPPOR configuration to use
+  //                     (i.e. UMA or Safe Browsing).
+  // deprecated_rappor_report_type: Specifies the deprecated RAPPOR
+  //                                configuration to use for comparison with the
+  //                                low-frequency metric.
   // The rappor preferences can be left blank if rappor_service is not set.
+  // TODO(dominickn): remove deprecated_rappor_report_type once sufficient
+  // comparison data has been collected and analysed - crbug.com/605836.
   struct ReportDetails {
     ReportDetails();
     ReportDetails(const ReportDetails& other);
+    ~ReportDetails();
     std::string metric_prefix;
     std::string extra_suffix;
     std::string rappor_prefix;
+    std::string deprecated_rappor_prefix;
     rappor::RapporType rappor_report_type;
+    rappor::RapporType deprecated_rappor_report_type;
   };
 
   // Args:
@@ -113,7 +123,9 @@
 
   void RecordUserDecisionToMetrics(Decision decision,
                                    const std::string& histogram_name);
-  void RecordUserDecisionToRappor(Decision decision);
+  void RecordUserDecisionToRappor(Decision decision,
+                                  const rappor::RapporType rappor_report_type,
+                                  const std::string& rappor_prefix);
   const GURL request_url_;
   const ReportDetails settings_;
   base::WeakPtr<rappor::RapporService> rappor_service_;
diff --git a/components/test_runner/layout_test_runtime_flags.cc b/components/test_runner/layout_test_runtime_flags.cc
index c9d530f..0488e55 100644
--- a/components/test_runner/layout_test_runtime_flags.cc
+++ b/components/test_runner/layout_test_runtime_flags.cc
@@ -42,6 +42,7 @@
   set_plugins_allowed(true);
   set_displaying_insecure_content_allowed(false);
   set_running_insecure_content_allowed(false);
+  set_autoplay_allowed(true);
 
   set_dump_editting_callbacks(false);
   set_dump_frame_load_callbacks(false);
diff --git a/components/test_runner/layout_test_runtime_flags.h b/components/test_runner/layout_test_runtime_flags.h
index 78c8cbf..42b7000 100644
--- a/components/test_runner/layout_test_runtime_flags.h
+++ b/components/test_runner/layout_test_runtime_flags.h
@@ -114,6 +114,7 @@
   DEFINE_BOOL_LAYOUT_TEST_RUNTIME_FLAG(running_insecure_content_allowed)
   DEFINE_BOOL_LAYOUT_TEST_RUNTIME_FLAG(
       dump_web_content_settings_client_callbacks)
+  DEFINE_BOOL_LAYOUT_TEST_RUNTIME_FLAG(autoplay_allowed)
 
   // If true, the test_shell will write a descriptive line for each editing
   // command.
diff --git a/components/test_runner/mock_content_settings_client.cc b/components/test_runner/mock_content_settings_client.cc
index 6bcee66..4aa8c00 100644
--- a/components/test_runner/mock_content_settings_client.cc
+++ b/components/test_runner/mock_content_settings_client.cc
@@ -21,9 +21,10 @@
                                            const blink::WebURL& image_url) {
   bool allowed = enabled_per_settings && flags_->images_allowed();
   if (flags_->dump_web_content_settings_client_callbacks() && delegate_) {
-    delegate_->PrintMessage(std::string("PERMISSION CLIENT: allowImage(") +
-                            NormalizeLayoutTestURL(image_url.string().utf8()) +
-                            "): " + (allowed ? "true" : "false") + "\n");
+    delegate_->PrintMessage(
+        std::string("MockContentSettingsClient: allowImage(") +
+        NormalizeLayoutTestURL(image_url.string().utf8()) +
+        "): " + (allowed ? "true" : "false") + "\n");
   }
   return allowed;
 }
@@ -31,9 +32,10 @@
 bool MockContentSettingsClient::allowMedia(const blink::WebURL& image_url) {
   bool allowed = flags_->media_allowed();
   if (flags_->dump_web_content_settings_client_callbacks() && delegate_)
-    delegate_->PrintMessage(std::string("PERMISSION CLIENT: allowMedia(") +
-                            NormalizeLayoutTestURL(image_url.string().utf8()) +
-                            "): " + (allowed ? "true" : "false") + "\n");
+    delegate_->PrintMessage(
+        std::string("MockContentSettingsClient: allowMedia(") +
+        NormalizeLayoutTestURL(image_url.string().utf8()) +
+        "): " + (allowed ? "true" : "false") + "\n");
   return allowed;
 }
 
@@ -43,7 +45,7 @@
   bool allowed = enabled_per_settings && flags_->scripts_allowed();
   if (flags_->dump_web_content_settings_client_callbacks() && delegate_) {
     delegate_->PrintMessage(
-        std::string("PERMISSION CLIENT: allowScriptFromSource(") +
+        std::string("MockContentSettingsClient: allowScriptFromSource(") +
         NormalizeLayoutTestURL(scriptURL.string().utf8()) + "): " +
         (allowed ? "true" : "false") + "\n");
   }
@@ -71,6 +73,10 @@
   return enabled_per_settings || flags_->running_insecure_content_allowed();
 }
 
+bool MockContentSettingsClient::allowAutoplay(bool defaultValue) {
+  return defaultValue || flags_->autoplay_allowed();
+}
+
 void MockContentSettingsClient::SetDelegate(WebTestDelegate* delegate) {
   delegate_ = delegate;
 }
diff --git a/components/test_runner/mock_content_settings_client.h b/components/test_runner/mock_content_settings_client.h
index 4d7337e..523888c 100644
--- a/components/test_runner/mock_content_settings_client.h
+++ b/components/test_runner/mock_content_settings_client.h
@@ -34,6 +34,7 @@
   bool allowRunningInsecureContent(bool enabledPerSettings,
                                    const blink::WebSecurityOrigin&,
                                    const blink::WebURL&) override;
+  bool allowAutoplay(bool defaultValue) override;
 
   void SetDelegate(WebTestDelegate*);
 
diff --git a/components/test_runner/test_plugin.cc b/components/test_runner/test_plugin.cc
index 5a83df49..61e12204 100644
--- a/components/test_runner/test_plugin.cc
+++ b/components/test_runner/test_plugin.cc
@@ -21,7 +21,6 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #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/WebGraphicsContext3DProvider.h"
 #include "third_party/WebKit/public/platform/WebTaskRunner.h"
 #include "third_party/WebKit/public/platform/WebThread.h"
@@ -125,7 +124,6 @@
     : frame_(frame),
       delegate_(delegate),
       container_(nullptr),
-      context_(nullptr),
       gl_(nullptr),
       color_texture_(0),
       mailbox_changed_(false),
@@ -189,7 +187,6 @@
   context_provider_ = base::WrapUnique(
       blink::Platform::current()->createOffscreenGraphicsContext3DProvider(
           attrs, url, nullptr, &gl_info));
-  context_ = context_provider_ ? context_provider_->context3d() : nullptr;
   gl_ = context_provider_ ? context_provider_->contextGL() : nullptr;
 
   if (!InitScene())
diff --git a/components/test_runner/test_plugin.h b/components/test_runner/test_plugin.h
index 6299b153..eca9a36 100644
--- a/components/test_runner/test_plugin.h
+++ b/components/test_runner/test_plugin.h
@@ -23,7 +23,6 @@
 
 namespace blink {
 class WebFrame;
-class WebGraphicsContext3D;
 class WebGraphicsContext3DProvider;
 class WebLayer;
 struct WebPluginParams;
@@ -45,9 +44,9 @@
 
 // A fake implemention of blink::WebPlugin for testing purposes.
 //
-// It uses WebGraphicsContext3D to paint a scene consisiting of a primitive
-// over a background. The primitive and background can be customized using
-// the following plugin parameters:
+// It uses GL to paint a scene consisiting of a primitive over a background. The
+// primitive and background can be customized using the following plugin
+// parameters.
 // primitive: none (default), triangle.
 // background-color: black (default), red, green, blue.
 // primitive-color: black (default), red, green, blue.
@@ -159,7 +158,6 @@
 
   blink::WebRect rect_;
   std::unique_ptr<blink::WebGraphicsContext3DProvider> context_provider_;
-  blink::WebGraphicsContext3D* context_;
   gpu::gles2::GLES2Interface* gl_;
   GLuint color_texture_;
   cc::TextureMailbox texture_mailbox_;
diff --git a/components/test_runner/test_runner.cc b/components/test_runner/test_runner.cc
index 2d0597ab..429bf34 100644
--- a/components/test_runner/test_runner.cc
+++ b/components/test_runner/test_runner.cc
@@ -200,6 +200,7 @@
   void SetAllowDisplayOfInsecureContent(bool allowed);
   void SetAllowFileAccessFromFileURLs(bool allow);
   void SetAllowRunningOfInsecureContent(bool allowed);
+  void SetAutoplayAllowed(bool allowed);
   void SetAllowUniversalAccessFromFileURLs(bool allow);
   void SetAlwaysAcceptCookies(bool accept);
   void SetAudioData(const gin::ArrayBufferView& view);
@@ -499,6 +500,8 @@
                  &TestRunnerBindings::SetAllowFileAccessFromFileURLs)
       .SetMethod("setAllowRunningOfInsecureContent",
                  &TestRunnerBindings::SetAllowRunningOfInsecureContent)
+      .SetMethod("setAutoplayAllowed",
+                 &TestRunnerBindings::SetAutoplayAllowed)
       .SetMethod("setAllowUniversalAccessFromFileURLs",
                  &TestRunnerBindings::SetAllowUniversalAccessFromFileURLs)
       .SetMethod("setAlwaysAcceptCookies",
@@ -1143,6 +1146,11 @@
     runner_->SetAllowRunningOfInsecureContent(allowed);
 }
 
+void TestRunnerBindings::SetAutoplayAllowed(bool allowed) {
+  if (runner_)
+    runner_->SetAutoplayAllowed(allowed);
+}
+
 void TestRunnerBindings::DumpPermissionClientCallbacks() {
   if (runner_)
     runner_->DumpPermissionClientCallbacks();
@@ -2811,6 +2819,11 @@
   OnLayoutTestRuntimeFlagsChanged();
 }
 
+void TestRunner::SetAutoplayAllowed(bool allowed) {
+  layout_test_runtime_flags_.set_autoplay_allowed(allowed);
+  OnLayoutTestRuntimeFlagsChanged();
+}
+
 void TestRunner::DumpPermissionClientCallbacks() {
   layout_test_runtime_flags_.set_dump_web_content_settings_client_callbacks(
       true);
diff --git a/components/test_runner/test_runner.h b/components/test_runner/test_runner.h
index 57c0f0ce..941906d5 100644
--- a/components/test_runner/test_runner.h
+++ b/components/test_runner/test_runner.h
@@ -405,6 +405,7 @@
   void SetPluginsAllowed(bool allowed);
   void SetAllowDisplayOfInsecureContent(bool allowed);
   void SetAllowRunningOfInsecureContent(bool allowed);
+  void SetAutoplayAllowed(bool allowed);
   void DumpPermissionClientCallbacks();
 
   // This function sets a flag that tells the test_shell to dump all calls
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 72dd3db..3b979f40 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -62,7 +62,6 @@
   "+third_party/WebKit/public/platform/WebGamepad.h",
   "+third_party/WebKit/public/platform/WebGamepads.h",
   "+third_party/WebKit/public/platform/WebGeofencingEventType.h",
-  "+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
   "+third_party/WebKit/public/platform/WebReferrerPolicy.h",
   "+third_party/WebKit/public/platform/WebScreenInfo.h",
   "+third_party/WebKit/public/platform/WebString.h",
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index e77833c..57cccab 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -191,22 +191,19 @@
   if (!previous_sibling)
     return false;
 
-  const BrowserAccessibility* text_object = this;
-  while (!text_object->IsTextOnlyObject()) {
-    // If we don't find a text object, then sibling cannot be on the same line.
-    if (!text_object->InternalChildCount())
-      return false;
-    text_object = text_object->InternalGetChild(0);
-  }
+  // Line linkage information might not be provided on non-leaf objects.
+  const BrowserAccessibility* leaf_object = PlatformDeepestFirstChild();
+  if (!leaf_object)
+    leaf_object = this;
 
   int32_t previous_on_line_id;
-  if (text_object->GetIntAttribute(ui::AX_ATTR_PREVIOUS_ON_LINE_ID,
+  if (leaf_object->GetIntAttribute(ui::AX_ATTR_PREVIOUS_ON_LINE_ID,
                                    &previous_on_line_id)) {
     const BrowserAccessibility* previous_on_line =
         manager()->GetFromID(previous_on_line_id);
-    // In the case of static text objects, the object designated to be the
-    // previous object on this line might be a child of the previous sibling,
-    // i.e. the last inline text box of the previous static text object.
+    // In the case of a static text sibling, the object designated to be the
+    // previous object on this line might be one of its children, i.e. the last
+    // inline text box.
     return previous_on_line &&
            previous_on_line->IsDescendantOf(previous_sibling);
   }
@@ -218,22 +215,19 @@
   if (!next_sibling)
     return false;
 
-  const BrowserAccessibility* text_object = this;
-  while (!text_object->IsTextOnlyObject()) {
-    // If we don't find a text object, then sibling cannot be on the same line.
-    if (!text_object->InternalChildCount())
-      return false;
-    text_object = text_object->InternalGetChild(0);
-  }
+  // Line linkage information might not be provided on non-leaf objects.
+  const BrowserAccessibility* leaf_object = PlatformDeepestLastChild();
+  if (!leaf_object)
+    leaf_object = this;
 
   int32_t next_on_line_id;
-  if (text_object->GetIntAttribute(ui::AX_ATTR_NEXT_ON_LINE_ID,
+  if (leaf_object->GetIntAttribute(ui::AX_ATTR_NEXT_ON_LINE_ID,
                                    &next_on_line_id)) {
     const BrowserAccessibility* next_on_line =
         manager()->GetFromID(next_on_line_id);
-    // In the case of static text objects, the object designated to be the next
-    // object on this line might be a child of the next sibling, i.e. the first
-    // inline text box of the next static text object.
+    // In the case of a static text sibling, the object designated to be the
+    // next object on this line might be one of its children, i.e. the last
+    // inline text box.
     return next_on_line && next_on_line->IsDescendantOf(next_sibling);
   }
   return false;
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index e734b6cf..df9c931 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -189,7 +189,7 @@
   // Don't fire focus events if the window itself doesn't have focus.
   // Bypass this check if a global focus listener was set up for testing
   // so that the test passes whether the window is active or not.
-  if (!g_focus_change_callback_for_testing.Pointer()) {
+  if (g_focus_change_callback_for_testing.Get().is_null()) {
     if (delegate_ && !delegate_->AccessibilityViewHasFocus())
       focus = nullptr;
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 610621ff..1522967 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -451,6 +451,7 @@
   Java_BrowserAccessibilityManager_setAccessibilityNodeInfoKitKatAttributes(
       env, obj, info,
       is_root,
+      node->IsEditableText(),
       base::android::ConvertUTF16ToJavaString(
           env, node->GetRoleDescription()).obj());
 
diff --git a/content/browser/blob_storage/OWNERS b/content/browser/blob_storage/OWNERS
new file mode 100644
index 0000000..471c08e
--- /dev/null
+++ b/content/browser/blob_storage/OWNERS
@@ -0,0 +1 @@
+dmurph@chromium.org
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
index b6b83d6..209a351 100644
--- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc
+++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
@@ -179,23 +179,6 @@
                           base::Bind(&base::DoNothing));
 }
 
-// TODO(ortuno): This should really be a BluetoothDevice method.
-// Replace when implemented. http://crbug.com/552022
-std::vector<BluetoothRemoteGattService*> GetPrimaryServicesByUUID(
-    device::BluetoothDevice* device,
-    const std::string& service_uuid) {
-  std::vector<BluetoothRemoteGattService*> services;
-  VLOG(1) << "Looking for service: " << service_uuid;
-  for (BluetoothRemoteGattService* service : device->GetGattServices()) {
-    VLOG(1) << "Service in cache: " << service->GetUUID().canonical_value();
-    if (service->GetUUID().canonical_value() == service_uuid &&
-        service->IsPrimary()) {
-      services.push_back(service);
-    }
-  }
-  return services;
-}
-
 UMARequestDeviceOutcome OutcomeFromChooserEvent(BluetoothChooser::Event event) {
   switch (event) {
     case BluetoothChooser::Event::DENIED_PERMISSION:
@@ -270,7 +253,6 @@
   IPC_MESSAGE_HANDLER(BluetoothHostMsg_GATTServerConnect, OnGATTServerConnect)
   IPC_MESSAGE_HANDLER(BluetoothHostMsg_GATTServerDisconnect,
                       OnGATTServerDisconnect)
-  IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetPrimaryService, OnGetPrimaryService)
   IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -293,13 +275,11 @@
     // The following data structures are used to store pending operations.
     // They should never contain elements at the end of a test.
     DCHECK(request_device_sessions_.IsEmpty());
-    DCHECK(pending_primary_services_requests_.empty());
 
     // The following data structures are cleaned up when a
     // device/service/characteristic is removed.
     // Since this can happen after the test is done and the cleanup function is
     // called, we clean them here.
-    service_to_device_.clear();
     connected_devices_map_.reset(new ConnectedDevicesMap(render_process_id_));
     allowed_devices_map_ = BluetoothAllowedDevicesMap();
   }
@@ -357,25 +337,6 @@
   std::unique_ptr<device::BluetoothDiscoverySession> discovery_session;
 };
 
-struct BluetoothDispatcherHost::PrimaryServicesRequest {
-  enum CallingFunction { GET_PRIMARY_SERVICE, GET_PRIMARY_SERVICES };
-
-  PrimaryServicesRequest(int thread_id,
-                         int request_id,
-                         const std::string& service_uuid,
-                         PrimaryServicesRequest::CallingFunction func)
-      : thread_id(thread_id),
-        request_id(request_id),
-        service_uuid(service_uuid),
-        func(func) {}
-  ~PrimaryServicesRequest() {}
-
-  int thread_id;
-  int request_id;
-  std::string service_uuid;
-  CallingFunction func;
-};
-
 BluetoothDispatcherHost::ConnectedDevicesMap::ConnectedDevicesMap(
     int render_process_id)
     : render_process_id_(render_process_id) {}
@@ -554,47 +515,6 @@
   }
 }
 
-void BluetoothDispatcherHost::GattServicesDiscovered(
-    device::BluetoothAdapter* adapter,
-    device::BluetoothDevice* device) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const std::string& device_address = device->GetAddress();
-  VLOG(1) << "Services discovered for device: " << device_address;
-
-  auto iter = pending_primary_services_requests_.find(device_address);
-  if (iter == pending_primary_services_requests_.end()) {
-    return;
-  }
-  std::vector<PrimaryServicesRequest> requests;
-  requests.swap(iter->second);
-  pending_primary_services_requests_.erase(iter);
-
-  for (const PrimaryServicesRequest& request : requests) {
-    std::vector<BluetoothRemoteGattService*> services =
-        GetPrimaryServicesByUUID(device, request.service_uuid);
-    switch (request.func) {
-      case PrimaryServicesRequest::GET_PRIMARY_SERVICE:
-        if (!services.empty()) {
-          AddToServicesMapAndSendGetPrimaryServiceSuccess(
-              *services[0], request.thread_id, request.request_id);
-        } else {
-          VLOG(1) << "No service found";
-          RecordGetPrimaryServiceOutcome(
-              UMAGetPrimaryServiceOutcome::NOT_FOUND);
-          Send(new BluetoothMsg_GetPrimaryServiceError(
-              request.thread_id, request.request_id,
-              WebBluetoothError::SERVICE_NOT_FOUND));
-        }
-        break;
-      case PrimaryServicesRequest::GET_PRIMARY_SERVICES:
-        NOTIMPLEMENTED();
-        break;
-    }
-  }
-  DCHECK(!ContainsKey(pending_primary_services_requests_, device_address))
-      << "Sending get-service responses unexpectedly queued another request.";
-}
-
 void BluetoothDispatcherHost::OnRequestDevice(
     int thread_id,
     int request_id,
@@ -684,74 +604,6 @@
   connected_devices_map_->Remove(frame_routing_id, device_id);
 }
 
-void BluetoothDispatcherHost::OnGetPrimaryService(
-    int thread_id,
-    int request_id,
-    int frame_routing_id,
-    const std::string& device_id,
-    const std::string& service_uuid) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_PRIMARY_SERVICE);
-  RecordGetPrimaryServiceService(BluetoothUUID(service_uuid));
-
-  if (!allowed_devices_map_.IsOriginAllowedToAccessService(
-          GetOrigin(frame_routing_id), device_id, service_uuid)) {
-    Send(new BluetoothMsg_GetPrimaryServiceError(
-        thread_id, request_id,
-        WebBluetoothError::NOT_ALLOWED_TO_ACCESS_SERVICE));
-    return;
-  }
-
-  const CacheQueryResult query_result =
-      QueryCacheForDevice(GetOrigin(frame_routing_id), device_id);
-
-  if (query_result.outcome != CacheQueryOutcome::SUCCESS) {
-    RecordGetPrimaryServiceOutcome(query_result.outcome);
-    Send(new BluetoothMsg_GetPrimaryServiceError(thread_id, request_id,
-                                                 query_result.GetWebError()));
-    return;
-  }
-
-  // There are four possibilities here:
-  // 1. Services not discovered and service present in |device|: Send back the
-  //    service to the renderer.
-  // 2. Services discovered and service present in |device|: Send back the
-  //    service to the renderer.
-  // 3. Services discovered and service not present in |device|: Send back not
-  //    found error.
-  // 4. Services not discovered and service not present in |device|: Add request
-  //    to map of pending getPrimaryService requests.
-
-  std::vector<BluetoothRemoteGattService*> services =
-      GetPrimaryServicesByUUID(query_result.device, service_uuid);
-
-  // 1. & 2.
-  if (!services.empty()) {
-    VLOG(1) << "Service found in device.";
-    const BluetoothRemoteGattService& service = *services[0];
-    DCHECK(service.IsPrimary());
-    AddToServicesMapAndSendGetPrimaryServiceSuccess(service, thread_id,
-                                                    request_id);
-    return;
-  }
-
-  // 3.
-  if (query_result.device->IsGattServicesDiscoveryComplete()) {
-    VLOG(1) << "Service not found in device.";
-    RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NOT_FOUND);
-    Send(new BluetoothMsg_GetPrimaryServiceError(
-        thread_id, request_id, WebBluetoothError::SERVICE_NOT_FOUND));
-    return;
-  }
-
-  VLOG(1) << "Adding service request to pending requests.";
-  // 4.
-  AddToPendingPrimaryServicesRequest(
-      query_result.device->GetAddress(),
-      PrimaryServicesRequest(thread_id, request_id, service_uuid,
-                             PrimaryServicesRequest::GET_PRIMARY_SERVICE));
-}
-
 void BluetoothDispatcherHost::OnGetAdapter(
     base::Closure continuation,
     scoped_refptr<device::BluetoothAdapter> adapter) {
@@ -1116,24 +968,6 @@
       thread_id, request_id, TranslateConnectError(error_code)));
 }
 
-void BluetoothDispatcherHost::AddToServicesMapAndSendGetPrimaryServiceSuccess(
-    const device::BluetoothRemoteGattService& service,
-    int thread_id,
-    int request_id) {
-  const std::string& service_identifier = service.GetIdentifier();
-  const std::string& device_address = service.GetDevice()->GetAddress();
-  auto insert_result =
-      service_to_device_.insert(make_pair(service_identifier, device_address));
-
-  // If a value is already in map, DCHECK it's valid.
-  if (!insert_result.second)
-    DCHECK_EQ(insert_result.first->second, device_address);
-
-  RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::SUCCESS);
-  Send(new BluetoothMsg_GetPrimaryServiceSuccess(thread_id, request_id,
-                                                 service_identifier));
-}
-
 CacheQueryResult BluetoothDispatcherHost::QueryCacheForDevice(
     const url::Origin& origin,
     const std::string& device_id) {
@@ -1158,45 +992,6 @@
   return result;
 }
 
-CacheQueryResult BluetoothDispatcherHost::QueryCacheForService(
-    const url::Origin& origin,
-    const std::string& service_instance_id) {
-  auto device_iter = service_to_device_.find(service_instance_id);
-
-  // Kill the renderer, see "ID Not In Map Note" above.
-  if (device_iter == service_to_device_.end()) {
-    bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_SERVICE_ID);
-    return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
-  }
-
-  const std::string& device_id =
-      allowed_devices_map_.GetDeviceId(origin, device_iter->second);
-  // Kill the renderer if the origin is not allowed to access the device.
-  if (device_id.empty()) {
-    bad_message::ReceivedBadMessage(
-        this, bad_message::BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN);
-    return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
-  }
-
-  CacheQueryResult result = QueryCacheForDevice(origin, device_id);
-
-  if (result.outcome != CacheQueryOutcome::SUCCESS) {
-    return result;
-  }
-
-  result.service = result.device->GetGattService(service_instance_id);
-  if (result.service == nullptr) {
-    result.outcome = CacheQueryOutcome::NO_SERVICE;
-  } else if (!allowed_devices_map_.IsOriginAllowedToAccessService(
-                 origin, device_id,
-                 result.service->GetUUID().canonical_value())) {
-    bad_message::ReceivedBadMessage(
-        this, bad_message::BDH_SERVICE_NOT_ALLOWED_FOR_ORIGIN);
-    return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
-  }
-  return result;
-}
-
 void BluetoothDispatcherHost::AddAdapterObserver(
     device::BluetoothAdapter::Observer* observer) {
   adapter_observers_.insert(observer);
@@ -1213,12 +1008,6 @@
   }
 }
 
-void BluetoothDispatcherHost::AddToPendingPrimaryServicesRequest(
-    const std::string& device_address,
-    const PrimaryServicesRequest& request) {
-  pending_primary_services_requests_[device_address].push_back(request);
-}
-
 url::Origin BluetoothDispatcherHost::GetOrigin(int frame_routing_id) {
   return RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id)
       ->GetLastCommittedOrigin();
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.h b/content/browser/bluetooth/bluetooth_dispatcher_host.h
index 1ed8440..3893e39 100644
--- a/content/browser/bluetooth/bluetooth_dispatcher_host.h
+++ b/content/browser/bluetooth/bluetooth_dispatcher_host.h
@@ -73,7 +73,6 @@
   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
 
   struct RequestDeviceSession;
-  struct PrimaryServicesRequest;
 
   // Map to keep track of connections. Inserting and removing connections
   // will update the Web Contents for the frame. Upon destruction
@@ -124,8 +123,6 @@
                    device::BluetoothDevice* device) override;
   void DeviceRemoved(device::BluetoothAdapter* adapter,
                      device::BluetoothDevice* device) override;
-  void GattServicesDiscovered(device::BluetoothAdapter* adapter,
-                              device::BluetoothDevice* device) override;
 
   // IPC Handlers, see definitions in bluetooth_messages.h.
   void OnRequestDevice(
@@ -141,11 +138,6 @@
   void OnGATTServerDisconnect(int thread_id,
                               int frame_routing_id,
                               const std::string& device_id);
-  void OnGetPrimaryService(int thread_id,
-                           int request_id,
-                           int frame_routing_id,
-                           const std::string& device_id,
-                           const std::string& service_uuid);
 
   // Callbacks for BluetoothDevice::OnRequestDevice.
   // If necessary, the adapter must be obtained before continuing to Impl.
@@ -191,13 +183,6 @@
       base::TimeTicks start_time,
       device::BluetoothDevice::ConnectErrorCode error_code);
 
-  // Adds the service to the map of services' instance ids to devices' instance
-  // ids and sends the service to the renderer.
-  void AddToServicesMapAndSendGetPrimaryServiceSuccess(
-      const device::BluetoothRemoteGattService& service,
-      int thread_id,
-      int request_id);
-
   // Functions to query the platform cache for the bluetooth object.
   // result.outcome == CacheQueryOutcome::SUCCESS if the object was found in the
   // cache. Otherwise result.outcome that can used to record the outcome and
@@ -211,17 +196,6 @@
   CacheQueryResult QueryCacheForDevice(const url::Origin& origin,
                                        const std::string& device_id);
 
-  // Queries the platform cache for a Service with |service_instance_id|. Fills
-  // in the |outcome| field, and |device| and |service| fields if successful.
-  CacheQueryResult QueryCacheForService(const url::Origin& origin,
-                                        const std::string& service_instance_id);
-
-  // Adds the PrimaryServicesRequest to the vector of pending services requests
-  // for that device.
-  void AddToPendingPrimaryServicesRequest(
-      const std::string& device_address,
-      const PrimaryServicesRequest& request);
-
   // Returns the origin for the frame with "frame_routing_id" in
   // render_process_id_.
   url::Origin GetOrigin(int frame_routing_id);
@@ -236,10 +210,6 @@
 
   BluetoothAllowedDevicesMap allowed_devices_map_;
 
-  // Maps to get the object's parent based on it's instanceID
-  // Map of service_instance_id to device_address.
-  std::map<std::string, std::string> service_to_device_;
-
   // Defines how long to scan for and how long to discover services for.
   int current_delay_time_;
 
@@ -258,11 +228,6 @@
   // Retains BluetoothGattConnection objects to keep connections open.
   std::unique_ptr<ConnectedDevicesMap> connected_devices_map_;
 
-  // Map of device_address's to primary-services requests that need responses
-  // when that device's service discovery completes.
-  std::map<std::string, std::vector<PrimaryServicesRequest>>
-      pending_primary_services_requests_;
-
   // |weak_ptr_on_ui_thread_| provides weak pointers, e.g. for callbacks, and
   // because it exists and has been bound to the UI thread enforces that all
   // copies verify they are also used on the UI thread.
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index a9536ae7..081b1be 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -4,13 +4,15 @@
 
 // ID Not In Map Note:
 // A service, characteristic, or descriptor ID not in the corresponding
-// BluetoothDispatcherHost map [service_to_device_,
+// BluetoothDispatcherHost map [service_id_to_device_address_,
 // characteristic_id_to_service_id_, descriptor_to_characteristic_] implies a
 // hostile renderer because a renderer obtains the corresponding ID from this
 // class and it will be added to the map at that time.
 
 #include "content/browser/bluetooth/web_bluetooth_service_impl.h"
 
+#include <algorithm>
+
 #include "base/thread_task_runner_handle.h"
 #include "content/browser/bluetooth/bluetooth_blacklist.h"
 #include "content/browser/bluetooth/bluetooth_dispatcher_host.h"
@@ -81,6 +83,24 @@
   return characteristics;
 }
 
+// TODO(ortuno): This should really be a BluetoothDevice method.
+// Replace when implemented. http://crbug.com/552022
+std::vector<device::BluetoothRemoteGattService*> GetPrimaryServicesByUUID(
+    device::BluetoothDevice* device,
+    const std::string& service_uuid) {
+  std::vector<device::BluetoothRemoteGattService*> services;
+  VLOG(1) << "Looking for service: " << service_uuid;
+  for (device::BluetoothRemoteGattService* service :
+       device->GetGattServices()) {
+    VLOG(1) << "Service in cache: " << service->GetUUID().canonical_value();
+    if (service->GetUUID().canonical_value() == service_uuid &&
+        service->IsPrimary()) {
+      services.push_back(service);
+    }
+  }
+  return services;
+}
+
 }  // namespace
 
 WebBluetoothServiceImpl::WebBluetoothServiceImpl(
@@ -123,6 +143,29 @@
   }
 }
 
+void WebBluetoothServiceImpl::GattServicesDiscovered(
+    device::BluetoothAdapter* adapter,
+    device::BluetoothDevice* device) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  const std::string& device_address = device->GetAddress();
+  VLOG(1) << "Services discovered for device: " << device_address;
+
+  auto iter = pending_primary_services_requests_.find(device_address);
+  if (iter == pending_primary_services_requests_.end()) {
+    return;
+  }
+  std::vector<PrimaryServicesRequestCallback> requests =
+      std::move(iter->second);
+  pending_primary_services_requests_.erase(iter);
+
+  for (const PrimaryServicesRequestCallback& request : requests) {
+    request.Run(device);
+  }
+
+  // Sending get-service responses unexpectedly queued another request.
+  DCHECK(!ContainsKey(pending_primary_services_requests_, device_address));
+}
+
 void WebBluetoothServiceImpl::GattCharacteristicValueChanged(
     device::BluetoothAdapter* adapter,
     device::BluetoothRemoteGattCharacteristic* characteristic,
@@ -160,6 +203,52 @@
   client_.Bind(std::move(client));
 }
 
+void WebBluetoothServiceImpl::RemoteServerGetPrimaryService(
+    const mojo::String& device_id,
+    const mojo::String& service_uuid,
+    const RemoteServerGetPrimaryServiceCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_PRIMARY_SERVICE);
+  RecordGetPrimaryServiceService(device::BluetoothUUID(service_uuid));
+
+  if (!GetBluetoothDispatcherHost()
+           ->allowed_devices_map_.IsOriginAllowedToAccessService(
+               GetOrigin(), device_id, service_uuid)) {
+    callback.Run(blink::mojom::WebBluetoothError::NOT_ALLOWED_TO_ACCESS_SERVICE,
+                 nullptr /* service */);
+    return;
+  }
+
+  const CacheQueryResult query_result =
+      GetBluetoothDispatcherHost()->QueryCacheForDevice(GetOrigin(), device_id);
+
+  if (query_result.outcome == CacheQueryOutcome::BAD_RENDERER) {
+    binding_.Close();
+    return;
+  }
+
+  if (query_result.outcome != CacheQueryOutcome::SUCCESS) {
+    RecordGetPrimaryServiceOutcome(query_result.outcome);
+    callback.Run(query_result.GetWebError(), nullptr /* service */);
+    return;
+  }
+
+  const std::string& device_address = query_result.device->GetAddress();
+
+  // We can't know if a service is present or not until GATT service discovery
+  // is complete for the device.
+  if (query_result.device->IsGattServicesDiscoveryComplete()) {
+    RemoteServerGetPrimaryServiceImpl(service_uuid, callback,
+                                      query_result.device);
+    return;
+  }
+
+  VLOG(1) << "Services not yet discovered.";
+  pending_primary_services_requests_[device_address].push_back(
+      base::Bind(&WebBluetoothServiceImpl::RemoteServerGetPrimaryServiceImpl,
+                 base::Unretained(this), service_uuid, callback));
+}
+
 void WebBluetoothServiceImpl::RemoteServiceGetCharacteristics(
     const mojo::String& service_instance_id,
     blink::mojom::WebBluetoothGATTQueryQuantity quantity,
@@ -184,8 +273,7 @@
   }
 
   const CacheQueryResult query_result =
-      GetBluetoothDispatcherHost()->QueryCacheForService(GetOrigin(),
-                                                         service_instance_id);
+      QueryCacheForService(service_instance_id);
 
   if (query_result.outcome == CacheQueryOutcome::BAD_RENDERER) {
     binding_.Close();
@@ -403,6 +491,41 @@
       weak_ptr_factory_.GetWeakPtr(), characteristic_instance_id, callback));
 }
 
+void WebBluetoothServiceImpl::RemoteServerGetPrimaryServiceImpl(
+    const std::string& service_uuid,
+    const RemoteServerGetPrimaryServiceCallback& callback,
+    device::BluetoothDevice* device) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(device->IsGattServicesDiscoveryComplete());
+
+  std::vector<device::BluetoothRemoteGattService*> services =
+      GetPrimaryServicesByUUID(device, service_uuid);
+
+  if (services.empty()) {
+    VLOG(1) << "Service not found in device.";
+    RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NOT_FOUND);
+    callback.Run(blink::mojom::WebBluetoothError::SERVICE_NOT_FOUND,
+                 nullptr /* service */);
+    return;
+  }
+
+  VLOG(1) << "Service found in device.";
+  std::string service_instance_id = services[0]->GetIdentifier();
+  std::string device_address = device->GetAddress();
+  auto insert_result = service_id_to_device_address_.insert(
+      make_pair(service_instance_id, device_address));
+  if (!insert_result.second)
+    DCHECK_EQ(insert_result.first->second, device_address);
+
+  RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::SUCCESS);
+  blink::mojom::WebBluetoothRemoteGATTServicePtr service_ptr =
+      blink::mojom::WebBluetoothRemoteGATTService::New();
+  service_ptr->instance_id = services[0]->GetIdentifier();
+  service_ptr->uuid = services[0]->GetUUID().canonical_value();
+  callback.Run(blink::mojom::WebBluetoothError::SUCCESS,
+               std::move(service_ptr));
+}
+
 void WebBluetoothServiceImpl::OnReadValueSuccess(
     const RemoteCharacteristicReadValueCallback& callback,
     const std::vector<uint8_t>& value) {
@@ -465,6 +588,51 @@
   callback.Run();
 }
 
+CacheQueryResult WebBluetoothServiceImpl::QueryCacheForService(
+    const std::string& service_instance_id) {
+  auto device_iter = service_id_to_device_address_.find(service_instance_id);
+
+  // Kill the render, see "ID Not in Map Note" above.
+  if (device_iter == service_id_to_device_address_.end()) {
+    CrashRendererAndClosePipe(bad_message::BDH_INVALID_SERVICE_ID);
+    return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
+  }
+
+  const std::string& device_id =
+      GetBluetoothDispatcherHost()->allowed_devices_map_.GetDeviceId(
+          GetOrigin(), device_iter->second);
+  // Kill the renderer if origin is not allowed to access the device.
+  if (device_id.empty()) {
+    CrashRendererAndClosePipe(bad_message::BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN);
+    return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
+  }
+
+  CacheQueryResult result =
+      GetBluetoothDispatcherHost()->QueryCacheForDevice(GetOrigin(), device_id);
+
+  // TODO(ortuno): Remove once QueryCacheForDevice closes binding_.
+  // http://crbug.com/508771
+  if (result.outcome == CacheQueryOutcome::BAD_RENDERER) {
+    binding_.Close();
+  }
+
+  if (result.outcome != CacheQueryOutcome::SUCCESS) {
+    return result;
+  }
+
+  result.service = result.device->GetGattService(service_instance_id);
+  if (result.service == nullptr) {
+    result.outcome = CacheQueryOutcome::NO_SERVICE;
+  } else if (!GetBluetoothDispatcherHost()
+                  ->allowed_devices_map_.IsOriginAllowedToAccessService(
+                      GetOrigin(), device_id,
+                      result.service->GetUUID().canonical_value())) {
+    CrashRendererAndClosePipe(bad_message::BDH_SERVICE_NOT_ALLOWED_FOR_ORIGIN);
+    return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
+  }
+  return result;
+}
+
 CacheQueryResult WebBluetoothServiceImpl::QueryCacheForCharacteristic(
     const std::string& characteristic_instance_id) {
   auto characteristic_iter =
@@ -476,14 +644,7 @@
     return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
   }
 
-  CacheQueryResult result = GetBluetoothDispatcherHost()->QueryCacheForService(
-      GetOrigin(), characteristic_iter->second);
-
-  // TODO(ortuno): Remove once QueryCacheForService closes binding_.
-  // http://crbug.com/508771
-  if (result.outcome == CacheQueryOutcome::BAD_RENDERER) {
-    binding_.Close();
-  }
+  CacheQueryResult result = QueryCacheForService(characteristic_iter->second);
 
   if (result.outcome != CacheQueryOutcome::SUCCESS) {
     return result;
@@ -521,7 +682,9 @@
 
 void WebBluetoothServiceImpl::ClearState() {
   characteristic_id_to_notify_session_.clear();
+  pending_primary_services_requests_.clear();
   characteristic_id_to_service_id_.clear();
+  service_id_to_device_address_.clear();
 }
 
 }  // namespace content
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.h b/content/browser/bluetooth/web_bluetooth_service_impl.h
index 2ada19d..2c6dc940 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.h
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -55,6 +55,9 @@
   void SetClientConnectionErrorHandler(base::Closure closure);
 
  private:
+  typedef base::Callback<void(device::BluetoothDevice*)>
+      PrimaryServicesRequestCallback;
+
   // WebContentsObserver:
   // These functions should always check that the affected RenderFrameHost
   // is this->render_frame_host_ and not some other frame in the same tab.
@@ -63,6 +66,8 @@
   // BluetoothAdapter::Observer:
   void AdapterPresentChanged(device::BluetoothAdapter* adapter,
                              bool present) override;
+  void GattServicesDiscovered(device::BluetoothAdapter* adapter,
+                              device::BluetoothDevice* device) override;
   void GattCharacteristicValueChanged(
       device::BluetoothAdapter* adapter,
       device::BluetoothRemoteGattCharacteristic* characteristic,
@@ -78,6 +83,10 @@
   // WebBluetoothService methods:
   void SetClient(
       blink::mojom::WebBluetoothServiceClientAssociatedPtrInfo client) override;
+  void RemoteServerGetPrimaryService(
+      const mojo::String& device_id,
+      const mojo::String& service_uuid,
+      const RemoteServerGetPrimaryServiceCallback& callback) override;
   void RemoteServiceGetCharacteristics(
       const mojo::String& service_instance_id,
       blink::mojom::WebBluetoothGATTQueryQuantity quantity,
@@ -97,6 +106,13 @@
       const mojo::String& characteristic_instance_id,
       const RemoteCharacteristicStopNotificationsCallback& callback) override;
 
+  // Should only be run after the services have been discovered for
+  // |device_address|.
+  void RemoteServerGetPrimaryServiceImpl(
+      const std::string& service_uuid,
+      const RemoteServerGetPrimaryServiceCallback& callback,
+      device::BluetoothDevice* device);
+
   // Callbacks for BluetoothRemoteGattCharacteristic::ReadRemoteCharacteristic.
   void OnReadValueSuccess(const RemoteCharacteristicReadValueCallback& callback,
                           const std::vector<uint8_t>& value);
@@ -132,6 +148,10 @@
   // renderer, record the reason and close the pipe, so it's safe to drop
   // any callbacks.
 
+  // Queries the platform cache for a Service with |service_instance_id|. Fills
+  // in the |outcome| field, and |device| and |service| fields if successful.
+  CacheQueryResult QueryCacheForService(const std::string& service_instance_id);
+
   // Queries the platform cache for a characteristic with
   // |characteristic_instance_id|. Fills in the |outcome| field, and |device|,
   // |service| and |characteristic| fields if successful.
@@ -147,10 +167,14 @@
   void ClearState();
 
   // Maps to get the object's parent based on its instanceID.
-
-  // Map of characteristic_instance_id to service_instance_id.
+  std::unordered_map<std::string, std::string> service_id_to_device_address_;
   std::unordered_map<std::string, std::string> characteristic_id_to_service_id_;
 
+  // Maps a device address to callbacks that are waiting for services to
+  // be discovered for that device.
+  std::unordered_map<std::string, std::vector<PrimaryServicesRequestCallback>>
+      pending_primary_services_requests_;
+
   // Map to keep track of the characteristics' notify sessions.
   std::unordered_map<std::string,
                      std::unique_ptr<device::BluetoothGattNotifySession>>
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index e615006ed..e0e0531 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -97,8 +97,7 @@
 
 std::unique_ptr<content::WebGraphicsContext3DCommandBufferImpl>
 CreateContextCommon(scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
-                    gpu::SurfaceHandle surface_handle,
-                    bool share_resources) {
+                    gpu::SurfaceHandle surface_handle) {
   DCHECK(
       content::GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor());
   DCHECK(gpu_channel_host);
@@ -129,8 +128,7 @@
   GURL url("chrome://gpu/GpuProcessTransportFactory::CreateContextCommon");
   return base::WrapUnique(new content::WebGraphicsContext3DCommandBufferImpl(
       surface_handle, url, gpu_channel_host.get(), attributes,
-      gfx::PreferIntegratedGpu, share_resources, automatic_flushes,
-      nullptr));
+      gfx::PreferIntegratedGpu, automatic_flushes));
 }
 
 #if defined(OS_MACOSX)
@@ -359,26 +357,28 @@
     if (!gpu_channel_host) {
       shared_worker_context_provider_ = nullptr;
     } else {
-      // This context is used for both the browser compositor and the display
-      // compositor.
-      constexpr bool share_resources = true;
-      context_provider = new ContextProviderCommandBuffer(
-          CreateContextCommon(gpu_channel_host, data->surface_handle,
-                              share_resources),
-          gpu::SharedMemoryLimits(), DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT);
-      if (!context_provider->BindToCurrentThread())
-        context_provider = nullptr;
-
       if (!shared_worker_context_provider_) {
         shared_worker_context_provider_ = new ContextProviderCommandBuffer(
-            CreateContextCommon(std::move(gpu_channel_host),
-                                gpu::kNullSurfaceHandle, share_resources),
-            gpu::SharedMemoryLimits(), BROWSER_WORKER_CONTEXT);
+            CreateContextCommon(gpu_channel_host, gpu::kNullSurfaceHandle),
+            gpu::SharedMemoryLimits(), nullptr, BROWSER_WORKER_CONTEXT);
         if (shared_worker_context_provider_->BindToCurrentThread())
           shared_worker_context_provider_->SetupLock();
         else
           shared_worker_context_provider_ = nullptr;
       }
+
+      // The |context_provider| is used for both the browser compositor and the
+      // display compositor. It shares resources with the worker context, so if
+      // we failed to make a worker context, just start over and try again.
+      if (shared_worker_context_provider_) {
+        context_provider = new ContextProviderCommandBuffer(
+            CreateContextCommon(std::move(gpu_channel_host),
+                                data->surface_handle),
+            gpu::SharedMemoryLimits(), shared_worker_context_provider_.get(),
+            DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT);
+        if (!context_provider->BindToCurrentThread())
+          context_provider = nullptr;
+      }
     }
 
     bool created_gpu_browser_compositor =
@@ -683,11 +683,9 @@
 
   // We need a separate context from the compositor's so that skia and gl_helper
   // don't step on each other.
-  bool share_resources = false;
   shared_main_thread_contexts_ = new ContextProviderCommandBuffer(
-      CreateContextCommon(std::move(gpu_channel_host), gpu::kNullSurfaceHandle,
-                          share_resources),
-      gpu::SharedMemoryLimits(), BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT);
+      CreateContextCommon(std::move(gpu_channel_host), gpu::kNullSurfaceHandle),
+      gpu::SharedMemoryLimits(), nullptr, BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT);
   shared_main_thread_contexts_->SetLostContextCallback(base::Bind(
       &GpuProcessTransportFactory::OnLostMainThreadSharedContextInsideCallback,
       callback_factory_.GetWeakPtr()));
diff --git a/content/browser/compositor/image_transport_factory.h b/content/browser/compositor/image_transport_factory.h
index e272c4d..d16b14b 100644
--- a/content/browser/compositor/image_transport_factory.h
+++ b/content/browser/compositor/image_transport_factory.h
@@ -31,10 +31,6 @@
 class Texture;
 }
 
-namespace blink {
-class WebGraphicsContext3D;
-}
-
 namespace display_compositor {
 class GLHelper;
 }
diff --git a/content/browser/compositor/software_output_device_mac.mm b/content/browser/compositor/software_output_device_mac.mm
index 9c9c393..da190699d 100644
--- a/content/browser/compositor/software_output_device_mac.mm
+++ b/content/browser/compositor/software_output_device_mac.mm
@@ -145,8 +145,8 @@
   base::TimeTicks vsync_timebase;
   base::TimeDelta vsync_interval;
   ui::AcceleratedWidgetMacGotFrame(
-      compositor_->widget(), 0, io_surfaces_[current_index_], pixel_size_,
-      scale_factor_, &vsync_timebase, &vsync_interval);
+      compositor_->widget(), 0, false, 0, io_surfaces_[current_index_],
+      pixel_size_, scale_factor_, &vsync_timebase, &vsync_interval);
   if (!update_vsync_callback_.is_null())
     update_vsync_callback_.Run(vsync_timebase, vsync_interval);
 
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 1d22345..bb12f90 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -195,6 +195,10 @@
 
 void CrossProcessFrameConnector::OnFrameRectChanged(
     const gfx::Rect& frame_rect) {
+  // TODO(wjmaclean) When changing the zoom of a WebView child without also
+  // changing the zoom of the embedder (e.g. using WebView.setZoom()), we
+  // shouldn't propagate this change in the frame rect. We need to find a way
+  // to detect when this happens. http://crbug.com/607978
   if (!frame_rect.size().IsEmpty())
     SetRect(frame_rect);
 }
diff --git a/content/browser/frame_host/frame_tree_browsertest.cc b/content/browser/frame_host/frame_tree_browsertest.cc
index 782234f..396ab05a 100644
--- a/content/browser/frame_host/frame_tree_browsertest.cc
+++ b/content/browser/frame_host/frame_tree_browsertest.cc
@@ -450,6 +450,68 @@
   EXPECT_EQ("Hi from a.com", document_body);
 }
 
+// Ensures that iframe with srcdoc is always put in the same origin as its
+// parent frame.
+IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  WebContentsImpl* contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  FrameTreeNode* root = contents->GetFrameTree()->root();
+  EXPECT_EQ(1U, root->child_count());
+
+  FrameTreeNode* child = root->child_at(0);
+  std::string frame_origin;
+  EXPECT_TRUE(ExecuteScriptAndExtractString(
+      child->current_frame_host(),
+      "domAutomationController.send(document.origin);", &frame_origin));
+  EXPECT_TRUE(
+      child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
+          url::Origin(GURL(frame_origin))));
+  EXPECT_FALSE(
+      root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
+          url::Origin(GURL(frame_origin))));
+
+  // Create a new iframe with srcdoc and add it to the main frame. It should
+  // be created in the same SiteInstance as the parent.
+  {
+    std::string script("var f = document.createElement('iframe');"
+                       "f.srcdoc = 'some content';"
+                       "document.body.appendChild(f)");
+    TestNavigationObserver observer(shell()->web_contents());
+    EXPECT_TRUE(ExecuteScript(root->current_frame_host(), script));
+    EXPECT_EQ(2U, root->child_count());
+    observer.Wait();
+
+    EXPECT_EQ(GURL(url::kAboutBlankURL), root->child_at(1)->current_url());
+    EXPECT_TRUE(ExecuteScriptAndExtractString(
+        root->child_at(1)->current_frame_host(),
+        "domAutomationController.send(document.origin);", &frame_origin));
+    EXPECT_EQ(root->current_frame_host()->GetLastCommittedURL().GetOrigin(),
+              GURL(frame_origin));
+    EXPECT_NE(child->current_frame_host()->GetLastCommittedURL().GetOrigin(),
+              GURL(frame_origin));
+  }
+
+  // Set srcdoc on the existing cross-site frame. It should navigate the frame
+  // back to the origin of the parent.
+  {
+    std::string script("var f = document.getElementById('child-0');"
+                       "f.srcdoc = 'some content';");
+    TestNavigationObserver observer(shell()->web_contents());
+    EXPECT_TRUE(ExecuteScript(root->current_frame_host(), script));
+    observer.Wait();
+
+    EXPECT_EQ(GURL(url::kAboutBlankURL), child->current_url());
+    EXPECT_TRUE(ExecuteScriptAndExtractString(
+        child->current_frame_host(),
+        "domAutomationController.send(document.origin);", &frame_origin));
+    EXPECT_EQ(root->current_frame_host()->GetLastCommittedURL().GetOrigin(),
+              GURL(frame_origin));
+  }
+}
+
 // Ensure that sandbox flags are correctly set when child frames are created.
 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) {
   GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
diff --git a/content/browser/gpu/gpu_ipc_browsertests.cc b/content/browser/gpu/gpu_ipc_browsertests.cc
index c4d7fdd..03a7a1f 100644
--- a/content/browser/gpu/gpu_ipc_browsertests.cc
+++ b/content/browser/gpu/gpu_ipc_browsertests.cc
@@ -39,11 +39,10 @@
   attributes.samples = 0;
   attributes.sample_buffers = 0;
   attributes.bind_generates_resource = false;
-  bool share_resources = false;
   bool automatic_flushes = false;
   return base::WrapUnique(new WebGraphicsContext3DCommandBufferImpl(
       gpu::kNullSurfaceHandle, GURL(), gpu_channel_host, attributes,
-      gfx::PreferIntegratedGpu, share_resources, automatic_flushes, nullptr));
+      gfx::PreferIntegratedGpu, automatic_flushes));
 }
 
 class ContextTestBase : public content::ContentBrowserTest {
@@ -69,7 +68,7 @@
 
     provider_ = new content::ContextProviderCommandBuffer(
         CreateContext(gpu_channel_host.get()), gpu::SharedMemoryLimits(),
-        content::OFFSCREEN_CONTEXT_FOR_TESTING);
+        nullptr, content::OFFSCREEN_CONTEXT_FOR_TESTING);
     bool bound = provider_->BindToCurrentThread();
     CHECK(bound);
     gl_ = provider_->ContextGL();
@@ -227,7 +226,7 @@
   // retain the host after provider is destroyed.
   scoped_refptr<ContextProviderCommandBuffer> provider =
       new ContextProviderCommandBuffer(CreateContext(GetGpuChannel()),
-                                       gpu::SharedMemoryLimits(),
+                                       gpu::SharedMemoryLimits(), nullptr,
                                        OFFSCREEN_CONTEXT_FOR_TESTING);
   EXPECT_TRUE(provider->BindToCurrentThread());
 
@@ -276,7 +275,7 @@
 
   scoped_refptr<ContextProviderCommandBuffer> provider =
       new ContextProviderCommandBuffer(CreateContext(GetGpuChannel()),
-                                       gpu::SharedMemoryLimits(),
+                                       gpu::SharedMemoryLimits(), nullptr,
                                        OFFSCREEN_CONTEXT_FOR_TESTING);
   base::RunLoop run_loop;
   int counter = 0;
diff --git a/content/browser/gpu/gpu_process_host_ui_shim.cc b/content/browser/gpu/gpu_process_host_ui_shim.cc
index 217d21f..bc81d2c 100644
--- a/content/browser/gpu/gpu_process_host_ui_shim.cc
+++ b/content/browser/gpu/gpu_process_host_ui_shim.cc
@@ -256,10 +256,12 @@
       io_surface.reset(IOSurfaceLookupFromMachPort(params.io_surface));
     }
 
-    ui::AcceleratedWidgetMacGotFrame(native_widget, ca_context_id, io_surface,
-                                     params.size, params.scale_factor,
-                                     &ack_params.vsync_timebase,
-                                     &ack_params.vsync_interval);
+    ui::AcceleratedWidgetMacGotFrame(
+        native_widget, ca_context_id,
+        params.fullscreen_low_power_ca_context_valid,
+        params.fullscreen_low_power_ca_context_id, io_surface, params.size,
+        params.scale_factor, &ack_params.vsync_timebase,
+        &ack_params.vsync_interval);
   } else {
     TRACE_EVENT0("browser", "Skipping recycled surface frame");
   }
diff --git a/content/browser/host_zoom_map_impl.cc b/content/browser/host_zoom_map_impl.cc
index cda61f9..916cec4 100644
--- a/content/browser/host_zoom_map_impl.cc
+++ b/content/browser/host_zoom_map_impl.cc
@@ -278,7 +278,66 @@
 
 void HostZoomMapImpl::SetDefaultZoomLevel(double level) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (ZoomValuesEqual(level, default_zoom_level_))
+      return;
+
   default_zoom_level_ = level;
+
+  // First, remove all entries that match the new default zoom level.
+  {
+    base::AutoLock auto_lock(lock_);
+    for (auto it = host_zoom_levels_.begin(); it != host_zoom_levels_.end(); ) {
+      if (ZoomValuesEqual(it->second, default_zoom_level_))
+        it = host_zoom_levels_.erase(it);
+      else
+        it++;
+    }
+  }
+
+  // Second, update zoom levels for all pages that do not have an overriding
+  // entry.
+  for (auto web_contents : WebContentsImpl::GetAllWebContents()) {
+    // Only change zoom for WebContents tied to the StoragePartition this
+    // HostZoomMap serves.
+    if (GetForWebContents(web_contents) != this)
+      continue;
+
+    int render_process_id = web_contents->GetRenderProcessHost()->GetID();
+    int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
+
+    // Get the url from the navigation controller directly, as calling
+    // WebContentsImpl::GetLastCommittedURL() may give us a virtual url that
+    // is different than the one stored in the map.
+    GURL url;
+    std::string host;
+    std::string scheme;
+
+    NavigationEntry* entry =
+        web_contents->GetController().GetLastCommittedEntry();
+    // It is possible for a WebContent's zoom level to be queried before
+    // a navigation has occurred.
+    if (entry) {
+      url = GetURLFromEntry(entry);
+      scheme = url.scheme();
+      host = net::GetHostOrSpecFromURL(url);
+    }
+
+    bool uses_default_zoom =
+        !HasZoomLevel(scheme, host) &&
+        !UsesTemporaryZoomLevel(render_process_id, render_view_id);
+
+    if (uses_default_zoom) {
+      web_contents->UpdateZoom(level);
+
+      HostZoomMap::ZoomLevelChange change;
+      change.mode = HostZoomMap::ZOOM_CHANGED_FOR_HOST;
+      change.host = host;
+      change.zoom_level = level;
+
+      zoom_level_changed_callbacks_.Notify(change);
+    }
+  }
 }
 
 std::unique_ptr<HostZoomMap::Subscription>
@@ -405,9 +464,10 @@
     temporary_zoom_levels_[key] = level;
   }
 
-  RenderViewHost* host =
-      RenderViewHost::FromID(render_process_id, render_view_id);
-  host->Send(new ViewMsg_SetZoomLevelForView(render_view_id, true, level));
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(
+          RenderViewHost::FromID(render_process_id, render_view_id)));
+  web_contents->SetTemporaryZoomLevel(level, true);
 
   HostZoomMap::ZoomLevelChange change;
   change.mode = HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM;
@@ -457,30 +517,31 @@
       return;
     temporary_zoom_levels_.erase(it);
   }
-  RenderViewHost* host =
-      RenderViewHost::FromID(render_process_id, render_view_id);
-  DCHECK(host);
-  // Send a new zoom level, host-specific if one exists.
-  host->Send(new ViewMsg_SetZoomLevelForView(
-      render_view_id,
-      false,
-      GetZoomLevelForHost(
-          GetHostFromProcessView(render_process_id, render_view_id))));
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(
+          RenderViewHost::FromID(render_process_id, render_view_id)));
+  web_contents->SetTemporaryZoomLevel(GetZoomLevelForHost(
+          GetHostFromProcessView(render_process_id, render_view_id)), false);
 }
 
 void HostZoomMapImpl::SendZoomLevelChange(const std::string& scheme,
                                           const std::string& host,
                                           double level) {
-  for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
-       !i.IsAtEnd(); i.Advance()) {
-    RenderProcessHost* render_process_host = i.GetCurrentValue();
-    // TODO(wjmaclean) This will need to be cleaned up when
-    // RenderProcessHost::GetStoragePartition() goes away. Perhaps have
-    // RenderProcessHost expose a GetHostZoomMap() function?
-    if (render_process_host->GetStoragePartition()->GetHostZoomMap() == this) {
-      render_process_host->Send(
-          new ViewMsg_SetZoomLevelForCurrentURL(scheme, host, level));
-    }
+  // We'll only send to WebContents not using temporary zoom levels. The one
+  // other case of interest is where the renderer is hosting a plugin document;
+  // that should be reflected in our temporary zoom level map, but we will
+  // double check on the renderer side to avoid the possibility of any races.
+  for (auto web_contents : WebContentsImpl::GetAllWebContents()) {
+    // Only send zoom level changes to WebContents that are using this
+    // HostZoomMap.
+    if (GetForWebContents(web_contents) != this)
+      continue;
+
+    int render_process_id = web_contents->GetRenderProcessHost()->GetID();
+    int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
+
+    if (!UsesTemporaryZoomLevel(render_process_id, render_view_id))
+      web_contents->UpdateZoomIfNecessary(scheme, host, level);
   }
 }
 
diff --git a/content/browser/host_zoom_map_impl_browsertest.cc b/content/browser/host_zoom_map_impl_browsertest.cc
index fbfdc3c..c4f6120f8 100644
--- a/content/browser/host_zoom_map_impl_browsertest.cc
+++ b/content/browser/host_zoom_map_impl_browsertest.cc
@@ -8,19 +8,25 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.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 "net/dns/mock_host_resolver.h"
 #include "url/gurl.h"
 
 namespace content {
 
 class HostZoomMapImplBrowserTest : public ContentBrowserTest {
+ protected:
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
 };
 
 void RunTestForURL(const GURL& url,
                    Shell* shell,
                    double host_zoom_level,
                    double temp_zoom_level) {
-  shell->LoadURL(url);
   WebContents* web_contents = shell->web_contents();
 
   HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
@@ -49,7 +55,10 @@
 // stored by host value, and can distinguish temporary zoom levels from
 // these.
 IN_PROC_BROWSER_TEST_F(HostZoomMapImplBrowserTest, GetZoomForView_Host) {
-  GURL url("http://abc.com");
+  GURL url(embedded_test_server()->GetURL("abc.com", "/"));
+
+  // We must navigate so the WebContents has a committed entry.
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   HostZoomMap* host_zoom_map =
       HostZoomMap::GetForWebContents(shell()->web_contents());
@@ -68,7 +77,10 @@
 // from these.
 IN_PROC_BROWSER_TEST_F(HostZoomMapImplBrowserTest,
                        GetZoomForView_HostAndScheme) {
-  GURL url("http://abc.com");
+  GURL url(embedded_test_server()->GetURL("abc.com", "/"));
+
+  // We must navigate so the WebContents has a committed entry.
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   HostZoomMap* host_zoom_map =
       HostZoomMap::GetForWebContents(shell()->web_contents());
diff --git a/content/browser/iframe_zoom_browsertest.cc b/content/browser/iframe_zoom_browsertest.cc
new file mode 100644
index 0000000..ade69ee0
--- /dev/null
+++ b/content/browser/iframe_zoom_browsertest.cc
@@ -0,0 +1,430 @@
+// Copyright 2016 The Chromium 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 <vector>
+
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/common/page_zoom.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/content_browser_test_utils_internal.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace content {
+
+// This file contains tests to make sure that subframes zoom in a manner
+// consistent with the top-level frame, even when the subframes are cross-site.
+// Particular things we want to make sure of:
+//
+// * Subframes should always have the same zoom level as their main frame, even
+// if the subframe's domain has a different zoom level stored in HostZoomMap.
+//
+// * The condition above should continue to hold after a navigation of the
+// subframe.
+//
+// * Zoom changes applied to the mainframe should propagate to all subframes,
+// regardless of whether they are same site or cross-site to the frame they are
+// children of.
+//
+// The tests in this file rely on the notion that, when a page zooms, that
+// subframes have both (1) a change in their frame rect, and (2) a change in
+// their frame's scale. Since the page should scale as a unit, this means the
+// innerWidth value of any subframe should be the same before and after the
+// zoom (though it may transiently take on a different value). The
+// FrameSizeObserver serves to watch for onresize events, and observes when
+// the innerWidth is correctly set.
+class IFrameZoomBrowserTest : public ContentBrowserTest {
+ public:
+  IFrameZoomBrowserTest() {}
+
+ protected:
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+    SetupCrossSiteRedirector(embedded_test_server());
+  }
+
+  WebContentsImpl* web_contents() {
+    return static_cast<WebContentsImpl*>(shell()->web_contents());
+  }
+};
+
+namespace {
+
+const double kTolerance = 0.1;  // In CSS pixels.
+
+double GetMainframeWindowBorder(const ToRenderFrameHost& adapter) {
+  double border;
+  const char kGetMainframeBorder[] = "window.domAutomationController.send("
+      "window.outerWidth - window.innerWidth"
+      ");";
+  EXPECT_TRUE(
+      ExecuteScriptAndExtractDouble(adapter, kGetMainframeBorder, &border));
+  return border;
+}
+
+double GetMainFrameZoomFactor(const ToRenderFrameHost& adapter, double border) {
+  const char kGetMainFrameZoomLevel[] =
+      "window.domAutomationController.send("
+      "(window.outerWidth - %f)/window.innerWidth"
+      ");";
+  double zoom_factor;
+  EXPECT_TRUE(ExecuteScriptAndExtractDouble(
+      adapter, base::StringPrintf(kGetMainFrameZoomLevel, border),
+      &zoom_factor));
+  return zoom_factor;
+}
+
+double GetSubframeWidth(const ToRenderFrameHost& adapter) {
+  double width;
+  EXPECT_TRUE(ExecuteScriptAndExtractDouble(
+      adapter, "window.domAutomationController.send(window.innerWidth);",
+      &width));
+  return width;
+}
+
+// This struct is used to track changes to subframes after a main frame zoom
+// change, so that we can test subframe inner widths with assurance that all the
+// changes have finished propagating.
+struct FrameResizeObserver {
+  FrameResizeObserver(RenderFrameHost* host,
+                      std::string label,
+                      double inner_width,
+                      double tolerance)
+      : frame_host(host),
+        msg_label(std::move(label)),
+        zoomed_correctly(false),
+        expected_inner_width(inner_width),
+        tolerance(tolerance) {
+    SetupOnResizeCallback(host, msg_label);
+  }
+
+  void SetupOnResizeCallback(const ToRenderFrameHost& adapter,
+                             const std::string& label) {
+    const char kOnResizeCallbackSetup[] =
+        "document.body.onresize = function(){"
+        "  window.domAutomationController.setAutomationId(0);"
+        "  window.domAutomationController.send('%s ' + window.innerWidth);"
+        "};";
+    EXPECT_TRUE(ExecuteScript(
+        adapter, base::StringPrintf(kOnResizeCallbackSetup, label.c_str())));
+  }
+
+  void Check(const std::string& status_msg) {
+    if (status_msg.find(msg_label) != 0)
+      return;
+
+    double inner_width = std::stod(status_msg.substr(msg_label.length() + 1));
+    zoomed_correctly = std::abs(expected_inner_width - inner_width) < tolerance;
+  }
+
+  FrameResizeObserver* toThis() {return this;}
+
+  RenderFrameHost* frame_host;
+  std::string msg_label;
+  bool zoomed_correctly;
+  double expected_inner_width;
+  double tolerance;
+};
+
+void WaitAndCheckFrameZoom(
+    DOMMessageQueue& msg_queue,
+    std::vector<FrameResizeObserver>& frame_observers) {
+  std::string status;
+  while (msg_queue.WaitForMessage(&status)) {
+    // Strip the double quotes from the message.
+    status = status.substr(1, status.length() -2);
+
+    bool all_zoomed_correctly = true;
+
+    // Use auto& to operate on a reference, and not a copy.
+    for (auto& observer : frame_observers) {
+      observer.Check(status);
+      all_zoomed_correctly = all_zoomed_correctly && observer.zoomed_correctly;
+    }
+
+    if (all_zoomed_correctly)
+      break;
+  }
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesZoomProperly) {
+  std::string top_level_host("a.com");
+  GURL main_url(embedded_test_server()->GetURL(
+      top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  NavigationEntry* entry =
+      web_contents()->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
+  EXPECT_EQ(top_level_host, loaded_url.host());
+
+  FrameTreeNode* root =
+      static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
+  RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* grandchild =
+      root->child_at(0)->child_at(0)->current_frame_host();
+
+  // The following calls must be made when the page's scale factor = 1.0.
+  double scale_one_child_width = GetSubframeWidth(child);
+  double scale_one_grandchild_width = GetSubframeWidth(grandchild);
+  double main_frame_window_border = GetMainframeWindowBorder(web_contents());
+
+  HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
+  double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
+  EXPECT_EQ(0.0, default_zoom_level);
+
+  EXPECT_DOUBLE_EQ(
+      1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+
+  const double new_zoom_factor = 2.5;
+  {
+    DOMMessageQueue msg_queue;
+
+    std::vector<FrameResizeObserver> frame_observers;
+    frame_observers.emplace_back(child, "child",
+                                 scale_one_child_width, kTolerance);
+    frame_observers.emplace_back(grandchild, "grandchild",
+                                 scale_one_grandchild_width, kTolerance);
+
+    const double new_zoom_level =
+        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+    host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
+
+    WaitAndCheckFrameZoom(msg_queue, frame_observers);
+  }
+
+  EXPECT_DOUBLE_EQ(
+      new_zoom_factor,
+      GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+}
+
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesDontZoomIndependently) {
+  std::string top_level_host("a.com");
+  GURL main_url(embedded_test_server()->GetURL(
+      top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  NavigationEntry* entry =
+      web_contents()->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
+  EXPECT_EQ(top_level_host, loaded_url.host());
+
+  FrameTreeNode* root =
+      static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
+  RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* grandchild =
+      root->child_at(0)->child_at(0)->current_frame_host();
+
+  // The following calls must be made when the page's scale factor = 1.0.
+  double scale_one_child_width = GetSubframeWidth(child);
+  double scale_one_grandchild_width = GetSubframeWidth(grandchild);
+  double main_frame_window_border = GetMainframeWindowBorder(web_contents());
+
+  HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
+  double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
+  EXPECT_EQ(0.0, default_zoom_level);
+
+  EXPECT_DOUBLE_EQ(
+      1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+
+  const double new_zoom_factor = 2.0;
+  const double new_zoom_level =
+      default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+
+  // This should not cause the nested iframe to change its zoom.
+  host_zoom_map->SetZoomLevelForHost("b.com", new_zoom_level);
+
+  EXPECT_DOUBLE_EQ(
+      1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+  EXPECT_EQ(scale_one_child_width, GetSubframeWidth(child));
+  EXPECT_EQ(scale_one_grandchild_width, GetSubframeWidth(grandchild));
+
+  // We exclude the remainder of this test on Android since Android does not
+  // set page zoom levels for loading pages.
+  // See RenderViewImpl::OnSetZoomLevelForLoadingURL().
+#if !defined(OS_ANDROID)
+  // When we navigate so that b.com is the top-level site, then it has the
+  // expected zoom.
+  GURL new_url = embedded_test_server()->GetURL("b.com", "/title1.html");
+  EXPECT_TRUE(NavigateToURL(shell(), new_url));
+  EXPECT_DOUBLE_EQ(
+      new_zoom_factor,
+      GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+#endif
+}
+
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, AllFramesGetDefaultZoom) {
+  std::string top_level_host("a.com");
+  GURL main_url(embedded_test_server()->GetURL(
+      top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  NavigationEntry* entry =
+      web_contents()->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
+  EXPECT_EQ(top_level_host, loaded_url.host());
+
+  FrameTreeNode* root =
+      static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
+  RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* grandchild =
+      root->child_at(0)->child_at(0)->current_frame_host();
+
+  // The following calls must be made when the page's scale factor = 1.0.
+  double scale_one_child_width = GetSubframeWidth(child);
+  double scale_one_grandchild_width = GetSubframeWidth(grandchild);
+  double main_frame_window_border = GetMainframeWindowBorder(web_contents());
+
+  HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
+  double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
+  EXPECT_EQ(0.0, default_zoom_level);
+
+  EXPECT_DOUBLE_EQ(
+      1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+
+  const double new_default_zoom_factor = 2.0;
+  {
+    DOMMessageQueue msg_queue;
+
+    std::vector<FrameResizeObserver> frame_observers;
+    frame_observers.emplace_back(child, "child",
+                                 scale_one_child_width, kTolerance);
+    frame_observers.emplace_back(grandchild, "grandchild",
+                                 scale_one_grandchild_width, kTolerance);
+
+    const double new_default_zoom_level =
+        default_zoom_level + ZoomFactorToZoomLevel(new_default_zoom_factor);
+
+    host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0);
+    host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level);
+
+    WaitAndCheckFrameZoom(msg_queue, frame_observers);
+  }
+  EXPECT_DOUBLE_EQ(
+      new_default_zoom_factor,
+      GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+}
+
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SiblingFramesZoom) {
+  std::string top_level_host("a.com");
+  GURL main_url(embedded_test_server()->GetURL(
+      top_level_host, "/cross_site_iframe_factory.html?a(b,b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  NavigationEntry* entry =
+      web_contents()->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
+  EXPECT_EQ(top_level_host, loaded_url.host());
+
+  FrameTreeNode* root =
+      static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
+  RenderFrameHostImpl* child1 = root->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* child2 = root->child_at(1)->current_frame_host();
+
+  // The following calls must be made when the page's scale factor = 1.0.
+  double scale_one_child1_width = GetSubframeWidth(child1);
+  double scale_one_child2_width = GetSubframeWidth(child2);
+  double main_frame_window_border = GetMainframeWindowBorder(web_contents());
+
+  HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
+  double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
+  EXPECT_EQ(0.0, default_zoom_level);
+
+  EXPECT_DOUBLE_EQ(
+      1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+
+  const double new_zoom_factor = 2.5;
+  {
+    DOMMessageQueue msg_queue;
+
+    std::vector<FrameResizeObserver> frame_observers;
+    frame_observers.emplace_back(child1, "child1",
+                                 scale_one_child1_width, kTolerance);
+    frame_observers.emplace_back(child2, "child2",
+                                 scale_one_child2_width, kTolerance);
+
+    const double new_zoom_level =
+        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+    host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
+
+    WaitAndCheckFrameZoom(msg_queue, frame_observers);
+  }
+
+  EXPECT_DOUBLE_EQ(
+      new_zoom_factor,
+      GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+}
+
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframeRetainsZoomOnNavigation) {
+  std::string top_level_host("a.com");
+  GURL main_url(embedded_test_server()->GetURL(
+      top_level_host, "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  NavigationEntry* entry =
+      web_contents()->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
+  EXPECT_EQ(top_level_host, loaded_url.host());
+
+  FrameTreeNode* root =
+      static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
+  RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
+
+  // The following calls must be made when the page's scale factor = 1.0.
+  double scale_one_child_width = GetSubframeWidth(child);
+  double main_frame_window_border = GetMainframeWindowBorder(web_contents());
+
+  HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
+  double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
+  EXPECT_EQ(0.0, default_zoom_level);
+
+  EXPECT_DOUBLE_EQ(
+      1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+
+  const double new_zoom_factor = 0.5;
+  {
+    DOMMessageQueue msg_queue;
+
+    std::vector<FrameResizeObserver> frame_observers;
+    frame_observers.emplace_back(child, "child",
+                                 scale_one_child_width, kTolerance);
+
+    const double new_zoom_level =
+        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+    host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
+
+    WaitAndCheckFrameZoom(msg_queue, frame_observers);
+  }
+
+  EXPECT_DOUBLE_EQ(
+      new_zoom_factor,
+      GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
+
+  // Navigate child frame cross site, and make sure zoom is the same.
+  TestNavigationObserver observer(web_contents());
+  GURL url = embedded_test_server()->GetURL("c.com", "/title1.html");
+  NavigateFrameToURL(root->child_at(0), url);
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+  EXPECT_EQ(url, observer.last_navigation_url());
+
+  // Check that the child frame maintained the same scale after navigating
+  // cross-site.
+  double new_child_width =
+      GetSubframeWidth(root->child_at(0)->current_frame_host());
+  EXPECT_EQ(scale_one_child_width, new_child_width);
+}
+
+}  // namespace content
diff --git a/content/browser/manifest/manifest_browsertest.cc b/content/browser/manifest/manifest_browsertest.cc
index 880a0f40..bcac1071 100644
--- a/content/browser/manifest/manifest_browsertest.cc
+++ b/content/browser/manifest/manifest_browsertest.cc
@@ -136,7 +136,7 @@
     const base::string16& source_id) {
   DCHECK(source == web_contents_);
 
-  if (level == logging::LOG_ERROR)
+  if (level == logging::LOG_ERROR || level == logging::LOG_WARNING)
     test_->OnReceivedConsoleError();
   return false;
 }
diff --git a/content/browser/media/media_source_browsertest.cc b/content/browser/media/media_source_browsertest.cc
index 90d11ba5..f6f86b2a 100644
--- a/content/browser/media/media_source_browsertest.cc
+++ b/content/browser/media/media_source_browsertest.cc
@@ -107,6 +107,9 @@
 }
 
 #if defined(USE_PROPRIETARY_CODECS)
+
+// TODO(chcunningham): Figure out why this is flaky on android. crbug/607841
+#if !defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(MediaSourceTest, Playback_Video_MP4_Audio_WEBM) {
   if (!IsMSESupported()) {
     VLOG(0) << "Skipping test - MSE not supported.";
@@ -117,6 +120,7 @@
   query_params.push_back(std::make_pair("audioFormat", "CLEAR_WEBM"));
   RunMediaTestPage("mse_different_containers.html", query_params, kEnded, true);
 }
+#endif  // !defined(OS_ANDROID)
 
 IN_PROC_BROWSER_TEST_F(MediaSourceTest, Playback_Video_WEBM_Audio_MP4) {
   if (!IsMSESupported()) {
diff --git a/content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc b/content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc
index 00f8454..0d1fcb66 100644
--- a/content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc
+++ b/content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc
@@ -11,140 +11,135 @@
 #include "content/test/webrtc_content_browsertest_base.h"
 #include "media/base/media_switches.h"
 
-#if defined(OS_ANDROID)
-// TODO(cpaulin): when crbug.com/561068 is fixed, enable this test
-// on android platform.
-#define MAYBE_WebRtcMediaRecorderTest DISABLED_WebRtcMediaRecorderTest
-#else
-#define MAYBE_WebRtcMediaRecorderTest WebRtcMediaRecorderTest
-#endif
-
 namespace {
 
-static const char kBlinkFeaturesNeeded[] = "GetUserMedia";
-
 static const char kMediaRecorderHtmlFile[] = "/media/mediarecorder_test.html";
 
 }  // namespace
 
 namespace content {
+
 // This class tests the recording of a media stream.
-class MAYBE_WebRtcMediaRecorderTest : public WebRtcContentBrowserTest {
+class WebRtcMediaRecorderTest : public WebRtcContentBrowserTest {
  public:
-  MAYBE_WebRtcMediaRecorderTest() {}
-  ~MAYBE_WebRtcMediaRecorderTest() override {}
+  WebRtcMediaRecorderTest() {}
+  ~WebRtcMediaRecorderTest() override {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     WebRtcContentBrowserTest::SetUpCommandLine(command_line);
 
-    // Turn on the flags we need.
     AppendUseFakeUIForMediaStreamFlag();
 
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseFakeDeviceForMediaStream);
 
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kEnableBlinkFeatures, kBlinkFeaturesNeeded);
+        switches::kEnableBlinkFeatures, "GetUserMedia");
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(MAYBE_WebRtcMediaRecorderTest);
+  DISALLOW_COPY_AND_ASSIGN(WebRtcMediaRecorderTest);
 };
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest, MediaRecorderStart) {
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, MediaRecorderStart) {
   MakeTypicalCall("testStartAndRecorderState();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
-                       MediaRecorderStartAndStop) {
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, MediaRecorderStartAndStop) {
   MakeTypicalCall("testStartStopAndRecorderState();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderStartAndDataAvailable) {
   MakeTypicalCall("testStartAndDataAvailable();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderStartWithTimeSlice) {
   MakeTypicalCall("testStartWithTimeSlice();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest, MediaRecorderResume) {
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, MediaRecorderResume) {
   MakeTypicalCall("testResumeAndRecorderState();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderNoResumeWhenRecorderInactive) {
   MakeTypicalCall("testIllegalResumeThrowsDOMError();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderResumeAndDataAvailable) {
   MakeTypicalCall("testResumeAndDataAvailable();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
-                       MediaRecorderPause) {
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, MediaRecorderPause) {
   MakeTypicalCall("testPauseAndRecorderState();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
-                       MediaRecorderPauseStop) {
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, MediaRecorderPauseStop) {
   MakeTypicalCall("testPauseStopAndRecorderState();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderPausePreventsDataavailableFromBeingFired) {
   MakeTypicalCall("testPausePreventsDataavailableFromBeingFired();",
                   kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderIllegalPauseThrowsDOMError) {
   MakeTypicalCall("testIllegalPauseThrowsDOMError();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderTwoChannelAudioRecording) {
   MakeTypicalCall("testTwoChannelAudio();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderIllegalStopThrowsDOMError) {
   MakeTypicalCall("testIllegalStopThrowsDOMError();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderIllegalStartWhileRecordingThrowsDOMError) {
   MakeTypicalCall("testIllegalStartInRecordingStateThrowsDOMError();",
                   kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderIllegalStartWhilePausedThrowsDOMError) {
   MakeTypicalCall("testIllegalStartInPausedStateThrowsDOMError();",
                   kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        MediaRecorderIllegalRequestDataThrowsDOMError) {
   MakeTypicalCall("testIllegalRequestDataThrowsDOMError();",
                   kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
-                       MediaRecorderPeerConnection) {
+#if defined(OS_ANDROID)
+// TODO(cpaulin): when http://crbug.com/585242 is fixed, enable peer connection
+// recording test on android platform.
+#define MAYBE_MediaRecorderPeerConnection DISABLED_MediaRecorderPeerConnection
+#else
+#define MAYBE_MediaRecorderPeerConnection MediaRecorderPeerConnection
+#endif
+
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
+                       MAYBE_MediaRecorderPeerConnection) {
   MakeTypicalCall("testRecordRemotePeerConnection();", kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        AddingTrackToMediaStreamFiresErrorEvent) {
   MakeTypicalCall("testAddingTrackToMediaStreamFiresErrorEvent();",
                   kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcMediaRecorderTest,
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
                        RemovingTrackFromMediaStreamFiresErrorEvent) {
   MakeTypicalCall("testRemovingTrackFromMediaStreamFiresErrorEvent();",
                   kMediaRecorderHtmlFile);
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index cee1d49..c193c042 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -549,7 +549,6 @@
   scoped_refptr<gpu::GpuChannelHost> gpu_channel_host(factory->GetGpuChannel());
 
   GURL url("chrome://gpu/CompositorImpl::CreateOutputSurface");
-  constexpr bool share_resources = false;
   constexpr bool automatic_flushes = false;
 
   constexpr size_t kBytesPerPixel = 4;
@@ -574,9 +573,8 @@
       new ContextProviderCommandBuffer(
           base::WrapUnique(new WebGraphicsContext3DCommandBufferImpl(
               surface_handle_, url, gpu_channel_host.get(), attributes,
-              gfx::PreferIntegratedGpu, share_resources, automatic_flushes,
-              nullptr)),
-          limits, DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT));
+              gfx::PreferIntegratedGpu, automatic_flushes)),
+          limits, nullptr, DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT));
   DCHECK(context_provider.get());
 
   std::unique_ptr<cc::OutputSurface> real_output_surface(
diff --git a/content/browser/renderer_host/render_view_host_delegate.cc b/content/browser/renderer_host/render_view_host_delegate.cc
index 2367681..2ce7f96 100644
--- a/content/browser/renderer_host/render_view_host_delegate.cc
+++ b/content/browser/renderer_host/render_view_host_delegate.cc
@@ -36,6 +36,10 @@
   return NULL;
 }
 
+double RenderViewHostDelegate::GetPendingPageZoomLevel() {
+  return 0.0;
+}
+
 bool RenderViewHostDelegate::IsNeverVisible() {
   return false;
 }
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index 70b916f..984f854 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -220,6 +220,11 @@
   // to this view.
   virtual SessionStorageNamespaceMap GetSessionStorageNamespaceMap();
 
+  // Returns the zoom level for the pending navigation for the page. If there
+  // is no pending navigation, this returns the zoom level for the current
+  // page.
+  virtual double GetPendingPageZoomLevel();
+
   // Returns true if the RenderViewHost will never be visible.
   virtual bool IsNeverVisible();
 
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 6896481..9d7b00a 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -337,6 +337,7 @@
   params.enable_auto_resize = GetWidget()->auto_resize_enabled();
   params.min_size = GetWidget()->min_size_for_auto_resize();
   params.max_size = GetWidget()->max_size_for_auto_resize();
+  params.page_zoom_level = delegate_->GetPendingPageZoomLevel();
   GetWidget()->GetResizeParams(&params.initial_size);
 
   if (!Send(new ViewMsg_New(params)))
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 5e05335..0868cb8 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -183,7 +183,6 @@
   // screen readback to be mapped.
   limits.mapped_memory_reclaim_limit = full_screen_texture_size_in_bytes;
 
-  bool share_resources = false;
   bool automatic_flushes = false;
   GURL url("chrome://gpu/RenderWidgetHostViewAndroid");
 
@@ -191,9 +190,10 @@
       new WebGraphicsContext3DCommandBufferImpl(
           gpu::kNullSurfaceHandle,  // offscreen
           url, gpu_channel_host.get(), attributes, gfx::PreferIntegratedGpu,
-          share_resources, automatic_flushes, nullptr));
-  provider_ = new ContextProviderCommandBuffer(
-      std::move(context), limits, BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT);
+          automatic_flushes));
+  provider_ =
+      new ContextProviderCommandBuffer(std::move(context), limits, nullptr,
+                                       BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT);
   if (!provider_->BindToCurrentThread())
     return;
   provider_->ContextGL()->TraceBeginCHROMIUM(
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index 18c04483..34faa32 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -30,7 +30,6 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/readback_types.h"
 #include "gpu/command_buffer/common/mailbox.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/android/window_android_observer.h"
 #include "ui/events/gesture_detection/filtered_gesture_provider.h"
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 e2815c2..51d18a0 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -668,17 +668,20 @@
   }
 }
 
-bool RenderWidgetHostViewAura::OnBeginFrameDerivedImpl(
+void RenderWidgetHostViewAura::OnBeginFrame(
     const cc::BeginFrameArgs& args) {
   delegated_frame_host_->SetVSyncParameters(args.frame_time, args.interval);
   host_->Send(new ViewMsg_BeginFrame(host_->GetRoutingID(), args));
-  return true;
+  last_begin_frame_args_ = args;
+}
+
+const cc::BeginFrameArgs& RenderWidgetHostViewAura::LastUsedBeginFrameArgs()
+    const {
+  return last_begin_frame_args_;
 }
 
 void RenderWidgetHostViewAura::OnBeginFrameSourcePausedChanged(bool paused) {
-  // Ignored for now.  If the begin frame source is paused, the renderer
-  // doesn't need to be informed about it and will just not receive more
-  // begin frames.
+  // Only used on Android WebView.
 }
 
 void RenderWidgetHostViewAura::SetKeyboardFocus() {
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 bccb444..16749dd48 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -98,7 +98,7 @@
       public aura::client::ActivationDelegate,
       public aura::client::FocusChangeObserver,
       public aura::client::CursorClientObserver,
-      public cc::BeginFrameObserverBase {
+      public cc::BeginFrameObserver {
  public:
   // When |is_guest_view_hack| is true, this view isn't really the view for
   // the |widget|, a RenderWidgetHostViewGuest is.
@@ -462,8 +462,9 @@
       const base::TimeDelta& interval) override;
   void SetBeginFrameSource(cc::BeginFrameSource* source) override;
 
-  // cc::BeginFrameObserverBase implementation.
-  bool OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) override;
+  // cc::BeginFrameObserver implementation.
+  void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+  const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
   void OnBeginFrameSourcePausedChanged(bool paused) override;
 
   // Detaches |this| from the input method object.
@@ -596,6 +597,7 @@
 
   // The begin frame source being observed.  Null if none.
   cc::BeginFrameSource* begin_frame_source_;
+  cc::BeginFrameArgs last_begin_frame_args_;
   bool needs_begin_frames_;
 
   // Used to record the last position of the mouse.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index fd4e494..b6c3f14 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -230,7 +230,7 @@
       public ui::AcceleratedWidgetMacNSView,
       public IPC::Sender,
       public display::DisplayObserver,
-      public cc::BeginFrameObserverBase {
+      public cc::BeginFrameObserver {
  public:
   // The view will associate itself with the given widget. The native view must
   // be hooked up immediately to the view hierarchy, or else when it is
@@ -509,8 +509,9 @@
       const base::TimeDelta& interval) override;
   void SetBeginFrameSource(cc::BeginFrameSource* source) override;
 
-  // cc::BeginFrameObserverBase implementation.
-  bool OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) override;
+  // cc::BeginFrameObserver implementation.
+  void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+  const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
   void OnBeginFrameSourcePausedChanged(bool paused) override;
 
   // AcceleratedWidgetMacNSView implementation.
@@ -601,6 +602,7 @@
 
   // The begin frame source being observed.  Null if none.
   cc::BeginFrameSource* begin_frame_source_;
+  cc::BeginFrameArgs last_begin_frame_args_;
   bool needs_begin_frames_;
 
   // The current composition character range and its bounds.
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 c6ab667b..e658157 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -79,15 +79,15 @@
 #include "ui/base/layout.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/layer.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/color_profile.h"
-#include "ui/gfx/display.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
-#include "ui/gfx/screen.h"
 #include "ui/gl/gl_switches.h"
 
 using content::BrowserAccessibility;
@@ -379,8 +379,8 @@
 }
 
 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
-  gfx::Display display =
-      gfx::Screen::GetScreen()->GetDisplayNearestWindow(view);
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(view);
 
   NSScreen* screen = [NSScreen deepestScreen];
 
@@ -484,17 +484,22 @@
   }
 }
 
-bool RenderWidgetHostViewMac::OnBeginFrameDerivedImpl(
+void RenderWidgetHostViewMac::OnBeginFrame(
     const cc::BeginFrameArgs& args) {
   delegated_frame_host_->SetVSyncParameters(args.frame_time, args.interval);
   render_widget_host_->Send(
       new ViewMsg_BeginFrame(render_widget_host_->GetRoutingID(), args));
-  return true;
+  last_begin_frame_args_ = args;
+}
+
+const cc::BeginFrameArgs& RenderWidgetHostViewMac::LastUsedBeginFrameArgs()
+    const {
+  return last_begin_frame_args_;
 }
 
 void RenderWidgetHostViewMac::OnBeginFrameSourcePausedChanged(
     bool paused) {
-  // Nothing to do here.
+  // Only used on Android WebView.
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -564,7 +569,7 @@
   root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
   delegated_frame_host_.reset(new DelegatedFrameHost(this));
 
-  gfx::Screen::GetScreen()->AddObserver(this);
+  display::Screen::GetScreen()->AddObserver(this);
 
   if (!is_guest_view_hack_)
     render_widget_host_->SetView(this);
@@ -580,7 +585,7 @@
 }
 
 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
-  gfx::Screen::GetScreen()->RemoveObserver(this);
+  display::Screen::GetScreen()->RemoveObserver(this);
 
   // This is being called from |cocoa_view_|'s destructor, so invalidate the
   // pointer.
@@ -1584,7 +1589,7 @@
     gfx::Point* transformed_point) {
   // The surface hittest happens in device pixels, so we need to convert the
   // |point| from DIPs to pixels before hittesting.
-  float scale_factor = gfx::Screen::GetScreen()
+  float scale_factor = display::Screen::GetScreen()
                            ->GetDisplayNearestWindow(cocoa_view_)
                            .device_scale_factor();
   gfx::Point point_in_pixels = gfx::ConvertPointToPixel(scale_factor, point);
@@ -1638,7 +1643,7 @@
     gfx::Point* transformed_point) {
   // Transformations use physical pixels rather than DIP, so conversion
   // is necessary.
-  float scale_factor = gfx::Screen::GetScreen()
+  float scale_factor = display::Screen::GetScreen()
                            ->GetDisplayNearestWindow(cocoa_view_)
                            .device_scale_factor();
   gfx::Point point_in_pixels = gfx::ConvertPointToPixel(scale_factor, point);
@@ -1760,17 +1765,17 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// gfx::DisplayObserver, public:
+// display::DisplayObserver, public:
 
-void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
-}
+void RenderWidgetHostViewMac::OnDisplayAdded(const display::Display& display) {}
 
-void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
-}
+void RenderWidgetHostViewMac::OnDisplayRemoved(
+    const display::Display& display) {}
 
 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
-    const gfx::Display& display, uint32_t metrics) {
-  gfx::Screen* screen = gfx::Screen::GetScreen();
+    const display::Display& display,
+    uint32_t metrics) {
+  display::Screen* screen = display::Screen::GetScreen();
   if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
     return;
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c4d3cd3..45028e9c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -115,6 +115,7 @@
 #include "content/public/common/web_preferences.h"
 #include "mojo/common/url_type_converters.h"
 #include "mojo/converters/geometry/geometry_type_converters.h"
+#include "net/base/url_util.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_transaction_factory.h"
 #include "net/url_request/url_request_context.h"
@@ -924,6 +925,39 @@
   }
 }
 
+void WebContentsImpl::SetTemporaryZoomLevel(double level,
+                                            bool temporary_zoom_enabled) {
+  SendPageMessage(new PageMsg_SetZoomLevel(
+      MSG_ROUTING_NONE,
+      temporary_zoom_enabled ? PageMsg_SetZoomLevel_Command::SET_TEMPORARY
+                             : PageMsg_SetZoomLevel_Command::CLEAR_TEMPORARY,
+      level));
+}
+
+void WebContentsImpl::UpdateZoom(double level) {
+  // Individual frames may still ignore the new zoom level if their RenderView
+  // contains a plugin document or if it uses a temporary zoom level.
+  SendPageMessage(new PageMsg_SetZoomLevel(
+      MSG_ROUTING_NONE,
+      PageMsg_SetZoomLevel_Command::USE_CURRENT_TEMPORARY_MODE, level));
+}
+
+void WebContentsImpl::UpdateZoomIfNecessary(const std::string& scheme,
+                                            const std::string& host,
+                                            double level) {
+  NavigationEntry* entry = GetController().GetLastCommittedEntry();
+  if (!entry)
+    return;
+
+  GURL url = HostZoomMap::GetURLFromEntry(entry);
+  if (host != net::GetHostOrSpecFromURL(url) ||
+      (!scheme.empty() && !url.SchemeIs(scheme))) {
+    return;
+  }
+
+  UpdateZoom(level);
+}
+
 WebUI* WebContentsImpl::CreateSubframeWebUI(const GURL& url,
                                             const std::string& frame_name) {
   DCHECK(!frame_name.empty());
@@ -4013,6 +4047,16 @@
   return this;
 }
 
+double WebContentsImpl::GetPendingPageZoomLevel() {
+  NavigationEntry* pending_entry = GetController().GetPendingEntry();
+  if (!pending_entry)
+    return HostZoomMap::GetZoomLevel(this);
+
+  GURL url = pending_entry->GetURL();
+  return HostZoomMap::GetForWebContents(this)->GetZoomLevelForHostAndScheme(
+      url.scheme(), net::GetHostOrSpecFromURL(url));
+}
+
 bool WebContentsImpl::IsNeverVisible() {
   if (!delegate_)
     return false;
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 0c72b84..f15c174 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -225,6 +225,21 @@
           const ui::AXTreeUpdate&)>;
   void RequestAXTreeSnapshot(AXTreeSnapshotCallback callback);
 
+  // Set a temporary zoom level for the frames associated with this WebContents.
+  // If |is_temporary| is true, we are setting a new temporary zoom level,
+  // otherwise we are clearing a previously set temporary zoom level.
+  void SetTemporaryZoomLevel(double level, bool temporary_zoom_enabled);
+
+  // Sets the zoom level for frames associated with this WebContents.
+  void UpdateZoom(double level);
+
+  // Sets the zoom level for frames associated with this WebContents if it
+  // matches |host| and (if non-empty) |scheme|. Matching is done on the
+  // last committed entry.
+  void UpdateZoomIfNecessary(const std::string& scheme,
+                             const std::string& host,
+                             double level);
+
   // WebContents ------------------------------------------------------
   WebContentsDelegate* GetDelegate() override;
   void SetDelegate(WebContentsDelegate* delegate) override;
@@ -520,6 +535,7 @@
   void SetIsVirtualKeyboardRequested(bool requested) override;
   bool IsVirtualKeyboardRequested() override;
   bool IsOverridingUserAgent() override;
+  double GetPendingPageZoomLevel() override;
 
   // NavigatorDelegate ---------------------------------------------------------
 
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index d30e03f..aa1289c 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -7,19 +7,22 @@
 #include <stddef.h>
 
 #include "base/containers/hash_tables.h"
+#include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
-#include "base/threading/thread_restrictions.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/url_constants.h"
-#include "net/base/mime_util.h"
 #include "ui/base/layout.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/resources/grit/webui_resources.h"
 #include "ui/resources/grit/webui_resources_map.h"
 
+#if defined(OS_WIN)
+#include "base/strings/utf_string_conversions.h"
+#endif
+
 namespace content {
 
 namespace {
@@ -104,13 +107,45 @@
 
 std::string SharedResourcesDataSource::GetMimeType(
     const std::string& path) const {
-  // Requests should not block on the disk!  On POSIX this goes to disk.
-  // http://code.google.com/p/chromium/issues/detail?id=59849
+  if (path.empty())
+    return "text/html";
 
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  std::string mime_type;
-  net::GetMimeTypeFromFile(base::FilePath().AppendASCII(path), &mime_type);
-  return mime_type;
+#if defined(OS_WIN)
+  base::FilePath file(base::UTF8ToWide(path));
+  std::string extension = base::WideToUTF8(file.FinalExtension()).substr(1);
+#else
+  base::FilePath file(path);
+  std::string extension = file.FinalExtension().substr(1);
+#endif
+
+  if (extension == "html")
+    return "text/html";
+
+  if (extension == "css")
+    return "text/css";
+
+  if (extension == "js")
+    return "application/javascript";
+
+  if (extension == "png")
+    return "image/png";
+
+  if (extension == "gif")
+    return "image/gif";
+
+  if (extension == "svg")
+    return "image/svg+xml";
+
+  if (extension == "woff2")
+    return "application/font-woff2";
+
+  CHECK(false) << path;
+  return "text/plain";
+}
+
+base::MessageLoop* SharedResourcesDataSource::MessageLoopForRequestPath(
+    const std::string& path) const {
+  return nullptr;
 }
 
 std::string
diff --git a/content/browser/webui/shared_resources_data_source.h b/content/browser/webui/shared_resources_data_source.h
index 53bf513b..002bbd24 100644
--- a/content/browser/webui/shared_resources_data_source.h
+++ b/content/browser/webui/shared_resources_data_source.h
@@ -23,7 +23,9 @@
       int render_process_id,
       int render_frame_id,
       const URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override;
+  std::string GetMimeType(const std::string& path) const override;
+  base::MessageLoop* MessageLoopForRequestPath(
+        const std::string& path) const override;
   std::string GetAccessControlAllowOriginForOrigin(
       const std::string& origin) const override;
 
diff --git a/content/child/blob_storage/OWNERS b/content/child/blob_storage/OWNERS
new file mode 100644
index 0000000..471c08e
--- /dev/null
+++ b/content/child/blob_storage/OWNERS
@@ -0,0 +1 @@
+dmurph@chromium.org
diff --git a/content/common/DEPS b/content/common/DEPS
index b7a79fc..8bc0e489 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -17,7 +17,6 @@
   "+third_party/WebKit/public/platform/WebGamepad.h",
   "+third_party/WebKit/public/platform/WebGamepads.h",
   "+third_party/WebKit/public/platform/WebGeofencingEventType.h",
-  "+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
   "+third_party/WebKit/public/platform/WebHTTPBody.h",
   "+third_party/WebKit/public/platform/WebHistoryScrollRestorationType.h",
   "+third_party/WebKit/public/platform/WebPageVisibilityState.h",
diff --git a/content/common/accelerated_surface_buffers_swapped_params_mac.h b/content/common/accelerated_surface_buffers_swapped_params_mac.h
index acd56aa2..2c35651 100644
--- a/content/common/accelerated_surface_buffers_swapped_params_mac.h
+++ b/content/common/accelerated_surface_buffers_swapped_params_mac.h
@@ -18,6 +18,8 @@
 
   gpu::SurfaceHandle surface_handle;
   CAContextID ca_context_id;
+  bool fullscreen_low_power_ca_context_valid;
+  CAContextID fullscreen_low_power_ca_context_id;
   gfx::ScopedRefCountedIOSurfaceMachPort io_surface;
   gfx::Size size;
   float scale_factor;
diff --git a/content/common/bluetooth/bluetooth_messages.h b/content/common/bluetooth/bluetooth_messages.h
index 88cbb5cb..7613523 100644
--- a/content/common/bluetooth/bluetooth_messages.h
+++ b/content/common/bluetooth/bluetooth_messages.h
@@ -139,18 +139,6 @@
                      int /* request_id */,
                      blink::WebBluetoothError /* result */)
 
-// Informs the renderer that primary service request |request_id| succeeded.
-IPC_MESSAGE_CONTROL3(BluetoothMsg_GetPrimaryServiceSuccess,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     std::string /* service_instance_id */)
-
-// Informs the renderer that the primary service request |request_id| failed.
-IPC_MESSAGE_CONTROL3(BluetoothMsg_GetPrimaryServiceError,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     blink::WebBluetoothError /* result */)
-
 // Messages sent from the renderer to the browser.
 
 // Requests a bluetooth device from the browser.
@@ -173,11 +161,3 @@
                      int /* thread_id */,
                      int /* frame_routing_id */,
                      std::string /* device_id */)
-
-// Gets primary service from bluetooth device.
-IPC_MESSAGE_CONTROL5(BluetoothHostMsg_GetPrimaryService,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     int /* frame_routing_id */,
-                     std::string /* device_id */,
-                     std::string /* service_uuid */)
diff --git a/content/common/gpu/client/context_provider_command_buffer.cc b/content/common/gpu/client/context_provider_command_buffer.cc
index 0379135..9636ffe 100644
--- a/content/common/gpu/client/context_provider_command_buffer.cc
+++ b/content/common/gpu/client/context_provider_command_buffer.cc
@@ -23,6 +23,9 @@
 
 namespace content {
 
+ContextProviderCommandBuffer::SharedProviders::SharedProviders() = default;
+ContextProviderCommandBuffer::SharedProviders::~SharedProviders() = default;
+
 class ContextProviderCommandBuffer::LostContextCallbackProxy
     : public WebGraphicsContext3DCommandBufferImpl::
           WebGraphicsContextLostCallback {
@@ -45,8 +48,12 @@
 ContextProviderCommandBuffer::ContextProviderCommandBuffer(
     std::unique_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
     const gpu::SharedMemoryLimits& memory_limits,
+    ContextProviderCommandBuffer* shared_context_provider,
     CommandBufferContextType type)
-    : context3d_(std::move(context3d)),
+    : shared_providers_(shared_context_provider
+                            ? shared_context_provider->shared_providers_
+                            : new SharedProviders),
+      context3d_(std::move(context3d)),
       memory_limits_(memory_limits),
       context_type_(type),
       debug_name_(CommandBufferContextTypeToString(type)) {
@@ -59,6 +66,14 @@
   DCHECK(main_thread_checker_.CalledOnValidThread() ||
          context_thread_checker_.CalledOnValidThread());
 
+  {
+    base::AutoLock hold(shared_providers_->lock);
+    auto it = std::find(shared_providers_->list.begin(),
+                        shared_providers_->list.end(), this);
+    if (it != shared_providers_->list.end())
+      shared_providers_->list.erase(it);
+  }
+
   // Destroy references to the context3d_ before leaking it.
   // TODO(danakj): Delete this.
   if (context3d_->GetCommandBufferProxy())
@@ -75,14 +90,6 @@
   return context3d_->GetCommandBufferProxy();
 }
 
-WebGraphicsContext3DCommandBufferImpl*
-ContextProviderCommandBuffer::WebContext3D() {
-  DCHECK(context3d_);
-  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
-  DCHECK(context_thread_checker_.CalledOnValidThread());
-  return context3d_.get();
-}
-
 bool ContextProviderCommandBuffer::BindToCurrentThread() {
   // This is called on the thread the context will be used.
   DCHECK(context_thread_checker_.CalledOnValidThread());
@@ -91,8 +98,50 @@
     return true;
 
   context3d_->SetContextType(context_type_);
-  if (!context3d_->InitializeOnCurrentThread(memory_limits_))
-    return false;
+
+  // It's possible to be running BindToCurrentThread on two contexts
+  // on different threads at the same time, but which will be in the same share
+  // group. To ensure they end up in the same group, hold the lock on the
+  // shared_providers_ (which they will share) after querying the group, until
+  // this context has been added to the list.
+  {
+    ContextProviderCommandBuffer* shared_context_provider = nullptr;
+    gpu::CommandBufferProxyImpl* shared_command_buffer = nullptr;
+    scoped_refptr<gpu::gles2::ShareGroup> share_group;
+
+    base::AutoLock hold(shared_providers_->lock);
+
+    if (!shared_providers_->list.empty()) {
+      shared_context_provider = shared_providers_->list.front();
+      shared_command_buffer =
+          shared_context_provider->context3d_->GetCommandBufferProxy();
+      share_group = shared_context_provider->context3d_->GetImplementation()
+                        ->share_group();
+    }
+
+    if (!context3d_->InitializeOnCurrentThread(
+            memory_limits_, shared_command_buffer, std::move(share_group)))
+      return false;
+
+    // If any context in the share group has been lost, then abort and don't
+    // continue since we need to go back to the caller of the constructor to
+    // find the correct share group.
+    // This may happen in between the share group being chosen at the
+    // constructor, and getting to run this BindToCurrentThread method which
+    // can be on some other thread.
+    // We intentionally call this *after* creating the command buffer via the
+    // GpuChannelHost. Once that has happened, the service knows we are in the
+    // share group and if a shared context is lost, our context will be informed
+    // also, and the lost context callback will occur for the owner of the
+    // context provider. If we check sooner, the shared context may be lost in
+    // between these two states and our context here would be left in an orphan
+    // share group.
+    if (share_group && share_group->IsLost())
+      return false;
+
+    shared_providers_->list.push_back(this);
+  }
+
   lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this));
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/content/common/gpu/client/context_provider_command_buffer.h b/content/common/gpu/client/context_provider_command_buffer.h
index dc490a3..0ed79f7 100644
--- a/content/common/gpu/client/context_provider_command_buffer.h
+++ b/content/common/gpu/client/context_provider_command_buffer.h
@@ -8,11 +8,11 @@
 #include <stdint.h>
 
 #include <memory>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
-#include "cc/blink/context_provider_web_context.h"
 #include "cc/output/context_provider.h"
 #include "content/common/content_export.h"
 #include "content/common/gpu/client/command_buffer_metrics.h"
@@ -35,18 +35,16 @@
 // Implementation of cc::ContextProvider that provides a
 // WebGraphicsContext3DCommandBufferImpl context and a GrContext.
 class CONTENT_EXPORT ContextProviderCommandBuffer
-    : NON_EXPORTED_BASE(public cc_blink::ContextProviderWebContext) {
+    : NON_EXPORTED_BASE(public cc::ContextProvider) {
  public:
   ContextProviderCommandBuffer(
       std::unique_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
       const gpu::SharedMemoryLimits& memory_limits,
+      ContextProviderCommandBuffer* shared_context_provider,
       CommandBufferContextType type);
 
   gpu::CommandBufferProxyImpl* GetCommandBufferProxy();
 
-  // cc_blink::ContextProviderWebContext implementation.
-  WebGraphicsContext3DCommandBufferImpl* WebContext3D() override;
-
   // cc::ContextProvider implementation.
   bool BindToCurrentThread() override;
   void DetachFromThread() override;
@@ -67,9 +65,22 @@
   void OnLostContext();
 
  private:
+  struct SharedProviders : public base::RefCountedThreadSafe<SharedProviders> {
+    base::Lock lock;
+    std::vector<ContextProviderCommandBuffer*> list;
+
+    SharedProviders();
+
+   private:
+    friend class base::RefCountedThreadSafe<SharedProviders>;
+    ~SharedProviders();
+  };
+
   base::ThreadChecker main_thread_checker_;
   base::ThreadChecker context_thread_checker_;
 
+  scoped_refptr<SharedProviders> shared_providers_;
+
   std::unique_ptr<WebGraphicsContext3DCommandBufferImpl> context3d_;
   std::unique_ptr<gpu::gles2::GLES2TraceImplementation> trace_impl_;
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc
index 0135776..8364236 100644
--- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc
+++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc
@@ -39,51 +39,13 @@
 
 namespace content {
 
-namespace {
-
-static base::LazyInstance<base::Lock>::Leaky
-    g_default_share_groups_lock = LAZY_INSTANCE_INITIALIZER;
-
-typedef std::map<
-    gpu::GpuChannelHost*,
-    scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>>
-    ShareGroupMap;
-static base::LazyInstance<ShareGroupMap> g_default_share_groups =
-    LAZY_INSTANCE_INITIALIZER;
-
-scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>
-GetDefaultShareGroupForHost(gpu::GpuChannelHost* host) {
-  base::AutoLock lock(g_default_share_groups_lock.Get());
-
-  ShareGroupMap& share_groups = g_default_share_groups.Get();
-  ShareGroupMap::iterator it = share_groups.find(host);
-  if (it == share_groups.end()) {
-    scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> group =
-        new WebGraphicsContext3DCommandBufferImpl::ShareGroup();
-    share_groups[host] = group;
-    return group;
-  }
-  return it->second;
-}
-
-} // namespace anonymous
-
-WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() {
-}
-
-WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() {
-  DCHECK(contexts_.empty());
-}
-
 WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
     gpu::SurfaceHandle surface_handle,
     const GURL& active_url,
     gpu::GpuChannelHost* host,
     const gpu::gles2::ContextCreationAttribHelper& attributes,
     gfx::GpuPreference gpu_preference,
-    bool share_resources,
-    bool automatic_flushes,
-    WebGraphicsContext3DCommandBufferImpl* share_context)
+    bool automatic_flushes)
     : automatic_flushes_(automatic_flushes),
       attributes_(attributes),
       host_(host),
@@ -100,14 +62,6 @@
     case gpu::gles2::CONTEXT_TYPE_WEBGL2:
       context_type_ = OFFSCREEN_CONTEXT_FOR_WEBGL;
   }
-  if (share_context) {
-    DCHECK(!share_resources);
-    share_group_ = share_context->share_group_;
-  } else if (share_resources) {
-    share_group_ = GetDefaultShareGroupForHost(host);
-  } else {
-    share_group_ = make_scoped_refptr(new ShareGroup);
-  }
 }
 
 WebGraphicsContext3DCommandBufferImpl::
@@ -119,7 +73,9 @@
 }
 
 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL(
-    const gpu::SharedMemoryLimits& memory_limits) {
+    const gpu::SharedMemoryLimits& memory_limits,
+    gpu::CommandBufferProxyImpl* shared_command_buffer,
+    scoped_refptr<gpu::gles2::ShareGroup> share_group) {
   if (initialized_)
     return true;
 
@@ -133,7 +89,8 @@
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "125248 WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL"));
 
-  if (!CreateContext(memory_limits)) {
+  if (!CreateContext(memory_limits, shared_command_buffer,
+                     std::move(share_group))) {
     Destroy();
 
     initialize_failed_ = true;
@@ -153,23 +110,17 @@
 }
 
 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
-    WebGraphicsContext3DCommandBufferImpl* share_context) {
+    gpu::CommandBufferProxyImpl* shared_command_buffer) {
   if (!host_.get())
     return false;
 
-  gpu::CommandBufferProxyImpl* share_group_command_buffer = NULL;
-
-  if (share_context) {
-    share_group_command_buffer = share_context->GetCommandBufferProxy();
-  }
-
   DCHECK(attributes_.buffer_preserved);
   std::vector<int32_t> serialized_attributes;
   attributes_.Serialize(&serialized_attributes);
 
   // Create a proxy to a command buffer in the GPU process.
   command_buffer_ = host_->CreateCommandBuffer(
-      surface_handle_, gfx::Size(), share_group_command_buffer,
+      surface_handle_, gfx::Size(), shared_command_buffer,
       gpu::GpuChannelHost::kDefaultStreamId,
       gpu::GpuChannelHost::kDefaultStreamPriority, serialized_attributes,
       active_url_, gpu_preference_);
@@ -192,27 +143,15 @@
 }
 
 bool WebGraphicsContext3DCommandBufferImpl::CreateContext(
-    const gpu::SharedMemoryLimits& memory_limits) {
+    const gpu::SharedMemoryLimits& memory_limits,
+    gpu::CommandBufferProxyImpl* shared_command_buffer,
+    scoped_refptr<gpu::gles2::ShareGroup> share_group) {
   TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext");
-  scoped_refptr<gpu::gles2::ShareGroup> gles2_share_group;
+  DCHECK_EQ(!!shared_command_buffer, !!share_group);
 
-  std::unique_ptr<base::AutoLock> share_group_lock;
-  bool add_to_share_group = false;
-  if (!command_buffer_) {
-    WebGraphicsContext3DCommandBufferImpl* share_context = NULL;
-
-    share_group_lock.reset(new base::AutoLock(share_group_->lock()));
-    share_context = share_group_->GetAnyContextLocked();
-
-    if (!InitializeCommandBuffer(share_context)) {
-      LOG(ERROR) << "Failed to initialize command buffer.";
-      return false;
-    }
-
-    if (share_context)
-      gles2_share_group = share_context->GetImplementation()->share_group();
-
-    add_to_share_group = true;
+  if (!InitializeCommandBuffer(shared_command_buffer)) {
+    LOG(ERROR) << "Failed to initialize command buffer.";
+    return false;
   }
 
   // Create the GLES2 helper, which writes the command buffer protocol.
@@ -237,7 +176,7 @@
 
   // Create the object exposing the OpenGL API.
   real_gl_.reset(new gpu::gles2::GLES2Implementation(
-      gles2_helper_.get(), gles2_share_group.get(), transfer_buffer_.get(),
+      gles2_helper_.get(), std::move(share_group), transfer_buffer_.get(),
       bind_generates_resource, lose_context_when_out_of_memory,
       support_client_side_arrays, command_buffer_.get()));
   if (!real_gl_->Initialize(memory_limits.start_transfer_buffer_size,
@@ -248,15 +187,15 @@
     return false;
   }
 
-  if (add_to_share_group)
-    share_group_->AddContextLocked(this);
-
   return true;
 }
 
 bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread(
-    const gpu::SharedMemoryLimits& memory_limits) {
-  if (!MaybeInitializeGL(memory_limits)) {
+    const gpu::SharedMemoryLimits& memory_limits,
+    gpu::CommandBufferProxyImpl* shared_command_buffer,
+    scoped_refptr<gpu::gles2::ShareGroup> share_group) {
+  if (!MaybeInitializeGL(memory_limits, shared_command_buffer,
+                         std::move(share_group))) {
     DLOG(ERROR) << "Failed to initialize context.";
     return false;
   }
@@ -270,8 +209,6 @@
 }
 
 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
-  share_group_->RemoveContext(this);
-
   trace_gl_.reset();
   real_gl_.reset();
   transfer_buffer_.reset();
@@ -291,14 +228,6 @@
   if (context_lost_callback_)
     context_lost_callback_->onContextLost();
 
-  share_group_->RemoveAllContexts();
-
-  DCHECK(host_.get());
-  {
-    base::AutoLock lock(g_default_share_groups_lock.Get());
-    g_default_share_groups.Get().erase(host_.get());
-  }
-
   gpu::CommandBuffer::State state = command_buffer_->GetLastState();
   UmaRecordContextLost(context_type_, state.error, state.context_lost_reason);
 }
diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h
index 57350b21..fcfc7d94 100644
--- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h
+++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h
@@ -14,6 +14,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "content/common/content_export.h"
@@ -21,7 +22,6 @@
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/common/surface_handle.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "ui/gl/gpu_preference.h"
 #include "url/gurl.h"
@@ -37,62 +37,18 @@
 class GLES2CmdHelper;
 class GLES2Implementation;
 class GLES2Interface;
+class ShareGroup;
 }
 }
 
 namespace content {
 
-class WebGraphicsContext3DCommandBufferImpl
-    : public NON_EXPORTED_BASE(blink::WebGraphicsContext3D) {
+class WebGraphicsContext3DCommandBufferImpl {
  public:
   enum MappedMemoryReclaimLimit {
     kNoLimit = 0,
   };
 
-  class ShareGroup : public base::RefCountedThreadSafe<ShareGroup> {
-   public:
-    ShareGroup();
-
-    WebGraphicsContext3DCommandBufferImpl* GetAnyContextLocked() {
-      // In order to ensure that the context returned is not removed while
-      // in use, the share group's lock should be aquired before calling this
-      // function.
-      lock_.AssertAcquired();
-      if (contexts_.empty())
-        return NULL;
-      return contexts_.front();
-    }
-
-    void AddContextLocked(WebGraphicsContext3DCommandBufferImpl* context) {
-      lock_.AssertAcquired();
-      contexts_.push_back(context);
-    }
-
-    void RemoveContext(WebGraphicsContext3DCommandBufferImpl* context) {
-      base::AutoLock auto_lock(lock_);
-      contexts_.erase(std::remove(contexts_.begin(), contexts_.end(), context),
-          contexts_.end());
-    }
-
-    void RemoveAllContexts() {
-      base::AutoLock auto_lock(lock_);
-      contexts_.clear();
-    }
-
-    base::Lock& lock() {
-      return lock_;
-    }
-
-   private:
-    friend class base::RefCountedThreadSafe<ShareGroup>;
-    virtual ~ShareGroup();
-
-    std::vector<WebGraphicsContext3DCommandBufferImpl*> contexts_;
-    base::Lock lock_;
-
-    DISALLOW_COPY_AND_ASSIGN(ShareGroup);
-  };
-
   class WebGraphicsContextLostCallback {
    public:
     virtual void onContextLost() = 0;
@@ -112,11 +68,9 @@
       gpu::GpuChannelHost* host,
       const gpu::gles2::ContextCreationAttribHelper& attributes,
       gfx::GpuPreference gpu_preference,
-      bool share_resources,
-      bool automatic_flushes,
-      WebGraphicsContext3DCommandBufferImpl* share_context);
+      bool automatic_flushes);
 
-  ~WebGraphicsContext3DCommandBufferImpl() override;
+  CONTENT_EXPORT ~WebGraphicsContext3DCommandBufferImpl();
 
   gpu::CommandBufferProxyImpl* GetCommandBufferProxy() {
     return command_buffer_.get();
@@ -133,7 +87,9 @@
   }
 
   CONTENT_EXPORT bool InitializeOnCurrentThread(
-      const gpu::SharedMemoryLimits& memory_limits);
+      const gpu::SharedMemoryLimits& memory_limits,
+      gpu::CommandBufferProxyImpl* shared_command_buffer,
+      scoped_refptr<gpu::gles2::ShareGroup> share_group);
 
   void SetContextType(CommandBufferContextType type) {
     context_type_ = type;
@@ -150,14 +106,18 @@
   // and subsequent calls are ignored. Must be called from the thread that is
   // going to use this object to issue GL commands (which might not be the main
   // thread).
-  bool MaybeInitializeGL(const gpu::SharedMemoryLimits& memory_limits);
+  bool MaybeInitializeGL(const gpu::SharedMemoryLimits& memory_limits,
+                         gpu::CommandBufferProxyImpl* shared_command_buffer,
+                         scoped_refptr<gpu::gles2::ShareGroup> share_group);
 
   bool InitializeCommandBuffer(
-      WebGraphicsContext3DCommandBufferImpl* share_context);
+      gpu::CommandBufferProxyImpl* shared_command_buffer);
 
   void Destroy();
 
-  bool CreateContext(const gpu::SharedMemoryLimits& memory_limits);
+  bool CreateContext(const gpu::SharedMemoryLimits& memory_limits,
+                     gpu::CommandBufferProxyImpl* shared_command_buffer,
+                     scoped_refptr<gpu::gles2::ShareGroup> share_group);
 
   void OnContextLost();
 
@@ -181,7 +141,6 @@
   std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
   std::unique_ptr<gpu::gles2::GLES2Implementation> real_gl_;
   std::unique_ptr<gpu::gles2::GLES2Interface> trace_gl_;
-  scoped_refptr<ShareGroup> share_group_;
 
   // Member variables should appear before the WeakPtrFactory, to ensure
   // that any WeakPtrs to Controller are invalidated before its members
diff --git a/content/common/gpu_host_messages.h b/content/common/gpu_host_messages.h
index 28140fc7..b4751d06 100644
--- a/content/common/gpu_host_messages.h
+++ b/content/common/gpu_host_messages.h
@@ -84,6 +84,8 @@
   IPC_STRUCT_TRAITS_MEMBER(surface_handle)
   // Only one of ca_context_id or io_surface may be non-0.
   IPC_STRUCT_TRAITS_MEMBER(ca_context_id)
+  IPC_STRUCT_TRAITS_MEMBER(fullscreen_low_power_ca_context_valid)
+  IPC_STRUCT_TRAITS_MEMBER(fullscreen_low_power_ca_context_id)
   IPC_STRUCT_TRAITS_MEMBER(io_surface)
   IPC_STRUCT_TRAITS_MEMBER(size)
   IPC_STRUCT_TRAITS_MEMBER(scale_factor)
diff --git a/content/common/page_message_enums.h b/content/common/page_message_enums.h
new file mode 100644
index 0000000..c4e0ce6a
--- /dev/null
+++ b/content/common/page_message_enums.h
@@ -0,0 +1,15 @@
+// Copyright 2016 The Chromium 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_COMMON_PAGE_MESSAGE_ENUMS_H_
+#define CONTENT_COMMON_PAGE_MESSAGE_ENUMS_H_
+
+enum class PageMsg_SetZoomLevel_Command {
+  SET_TEMPORARY,
+  CLEAR_TEMPORARY,
+  USE_CURRENT_TEMPORARY_MODE,
+  LAST = USE_CURRENT_TEMPORARY_MODE
+};
+
+#endif  // CONTENT_COMMON_PAGE_MESSAGE_ENUMS_H_
diff --git a/content/common/page_messages.h b/content/common/page_messages.h
index 284c1345..a7f56bb8 100644
--- a/content/common/page_messages.h
+++ b/content/common/page_messages.h
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "content/common/page_message_enums.h"
 #include "ipc/ipc_message_macros.h"
+#include "ui/gfx/geometry/rect.h"
 
 // IPC messages for page-level actions.
 // Multiply-included message file, hence no include guard.
@@ -12,11 +14,19 @@
 
 #define IPC_MESSAGE_START PageMsgStart
 
+IPC_ENUM_TRAITS_MAX_VALUE(
+    PageMsg_SetZoomLevel_Command,
+    PageMsg_SetZoomLevel_Command::LAST);
+
 // Messages sent from the browser to the renderer.
 
 IPC_MESSAGE_ROUTED1(PageMsg_UpdateWindowScreenRect,
                     gfx::Rect /* window_screen_rect */)
 
+IPC_MESSAGE_ROUTED2(PageMsg_SetZoomLevel,
+                    PageMsg_SetZoomLevel_Command /* command */,
+                    double /* zoom_level */)
+
 // -----------------------------------------------------------------------------
 // Messages sent from the renderer to the browser.
 
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index af03423..8a01f2a3 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -257,7 +257,6 @@
   IPC_STRUCT_TRAITS_MEMBER(enable_referrers)
   IPC_STRUCT_TRAITS_MEMBER(enable_do_not_track)
   IPC_STRUCT_TRAITS_MEMBER(webrtc_ip_handling_policy)
-  IPC_STRUCT_TRAITS_MEMBER(default_zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(user_agent_override)
   IPC_STRUCT_TRAITS_MEMBER(accept_languages)
   IPC_STRUCT_TRAITS_MEMBER(report_frame_name_changes)
@@ -512,6 +511,9 @@
 
   // The maximum size to layout the page if auto-resize is enabled.
   IPC_STRUCT_MEMBER(gfx::Size, max_size)
+
+  // The page zoom level.
+  IPC_STRUCT_MEMBER(double, page_zoom_level)
 IPC_STRUCT_END()
 
 #if defined(OS_MACOSX)
@@ -666,19 +668,6 @@
                     GURL /* url */,
                     double /* zoom_level */)
 
-// Set the zoom level for a particular url, so all render views
-// displaying this url can update their zoom levels to match.
-// If scheme is empty, then only host is used for matching.
-IPC_MESSAGE_CONTROL3(ViewMsg_SetZoomLevelForCurrentURL,
-                     std::string /* scheme */,
-                     std::string /* host */,
-                     double /* zoom_level */)
-
-// Set the zoom level for a particular render view.
-IPC_MESSAGE_ROUTED2(ViewMsg_SetZoomLevelForView,
-                    bool /* uses_temporary_zoom_level */,
-                    double /* zoom_level */)
-
 // Change encoding of page in the renderer.
 IPC_MESSAGE_ROUTED1(ViewMsg_SetPageEncoding,
                     std::string /*new encoding name*/)
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 1ec0132e..356f77f 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -267,6 +267,8 @@
       'renderer/java/gin_java_bridge_value_converter.h',
       'renderer/java/gin_java_function_invocation_helper.cc',
       'renderer/java/gin_java_function_invocation_helper.h',
+      'renderer/manifest/manifest_debug_info.cc',
+      'renderer/manifest/manifest_debug_info.h',
       'renderer/manifest/manifest_manager.cc',
       'renderer/manifest/manifest_manager.h',
       'renderer/manifest/manifest_parser.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 43908d6..207f615 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -226,6 +226,7 @@
       'browser/frame_host/render_widget_host_view_child_frame_browsertest.cc',
       'browser/gpu/gpu_ipc_browsertests.cc',
       'browser/host_zoom_map_impl_browsertest.cc',
+      'browser/iframe_zoom_browsertest.cc',
       'browser/indexed_db/indexed_db_browsertest.cc',
       'browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc',
       'browser/indexed_db/mock_browsertest_indexed_db_class_factory.h',
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index 8abc98e..98f7bc1 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -327,6 +327,8 @@
 void GpuChildThread::SendAcceleratedSurfaceBuffersSwapped(
     gpu::SurfaceHandle surface_handle,
     CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
     const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
     const gfx::Size& size,
     float scale_factor,
@@ -334,6 +336,10 @@
   AcceleratedSurfaceBuffersSwappedParams params;
   params.surface_handle = surface_handle;
   params.ca_context_id = ca_context_id;
+  params.fullscreen_low_power_ca_context_valid =
+      fullscreen_low_power_ca_context_valid;
+  params.fullscreen_low_power_ca_context_id =
+      fullscreen_low_power_ca_context_id;
   params.io_surface = io_surface;
   params.size = size;
   params.scale_factor = scale_factor;
diff --git a/content/gpu/gpu_child_thread.h b/content/gpu/gpu_child_thread.h
index e9f06561..2617d647 100644
--- a/content/gpu/gpu_child_thread.h
+++ b/content/gpu/gpu_child_thread.h
@@ -98,6 +98,8 @@
   void SendAcceleratedSurfaceBuffersSwapped(
       gpu::SurfaceHandle surface_handle,
       CAContextID ca_context_id,
+      bool fullscreen_low_power_ca_context_valid,
+      CAContextID fullscreen_low_power_ca_context_id,
       const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
       const gfx::Size& size,
       float scale_factor,
diff --git a/content/gpu/gpu_watchdog_thread.cc b/content/gpu/gpu_watchdog_thread.cc
index 536854fc..dddb409 100644
--- a/content/gpu/gpu_watchdog_thread.cc
+++ b/content/gpu/gpu_watchdog_thread.cc
@@ -244,7 +244,8 @@
 #if defined(OS_WIN)
   // Defer termination until a certain amount of CPU time has elapsed on the
   // watched thread.
-  base::TimeDelta time_since_arm = GetWatchedThreadTime() - arm_cpu_time_;
+  base::ThreadTicks current_cpu_time = GetWatchedThreadTime();
+  base::TimeDelta time_since_arm = current_cpu_time - arm_cpu_time_;
   if (use_thread_cpu_time_ && (time_since_arm < timeout_)) {
     message_loop()->PostDelayedTask(
         FROM_HERE,
@@ -349,8 +350,12 @@
   ULONGLONG interrupt_delay = fire_interrupt_time - arm_interrupt_time_;
 
   base::debug::Alias(&interrupt_delay);
+  base::debug::Alias(&current_cpu_time);
   base::debug::Alias(&time_since_arm);
 
+  bool using_thread_ticks = base::ThreadTicks::IsSupported();
+  base::debug::Alias(&using_thread_ticks);
+
   bool using_high_res_timer = base::Time::IsHighResolutionTimerInUse();
   base::debug::Alias(&using_high_res_timer);
 #endif
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
index bc51883..9b2a0b3 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
@@ -849,7 +849,6 @@
         }
         if (isEditableText) {
             node.setText(charSequence);
-            node.setEditable(true);
         } else {
             node.setContentDescription(charSequence);
         }
@@ -916,7 +915,7 @@
 
     @CalledByNative
     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
-            boolean isRoot, String roleDescription) {
+            boolean isRoot, boolean isEditableText, String roleDescription) {
         // Requires KitKat or higher.
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatBrowserAccessibilityManager.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatBrowserAccessibilityManager.java
index 45595bc..0fc2bbbb 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatBrowserAccessibilityManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatBrowserAccessibilityManager.java
@@ -29,12 +29,15 @@
 
     @Override
     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
-            boolean isRoot, String roleDescription) {
+            boolean isRoot, boolean isEditableText, String roleDescription) {
         Bundle bundle = node.getExtras();
         bundle.putCharSequence("AccessibilityNodeInfo.roleDescription", roleDescription);
         if (isRoot) {
             bundle.putCharSequence("ACTION_ARGUMENT_HTML_ELEMENT_STRING_VALUES",
                     mSupportedHtmlElementTypes);
         }
+        if (isEditableText) {
+            node.setEditable(true);
+        }
     }
 }
diff --git a/content/public/common/renderer_preferences.cc b/content/public/common/renderer_preferences.cc
index 2a9f8676..797dd9b 100644
--- a/content/public/common/renderer_preferences.cc
+++ b/content/public/common/renderer_preferences.cc
@@ -31,7 +31,6 @@
       use_custom_colors(true),
       enable_referrers(true),
       enable_do_not_track(false),
-      default_zoom_level(0),
       report_frame_name_changes(false),
       tap_multiple_targets_strategy(TAP_MULTIPLE_TARGETS_STRATEGY_POPUP),
       disable_client_blocked_error_page(false),
diff --git a/content/public/common/renderer_preferences.h b/content/public/common/renderer_preferences.h
index 2b085ae..73d215a 100644
--- a/content/public/common/renderer_preferences.h
+++ b/content/public/common/renderer_preferences.h
@@ -102,9 +102,6 @@
   // in webrtc_ip_handling_policy.h.
   std::string webrtc_ip_handling_policy;
 
-  // Default page zoom level.
-  double default_zoom_level;
-
   // The user agent given to WebKit when it requests one and the user agent is
   // being overridden for the current navigation.
   std::string user_agent_override;
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 284498a..2869f30 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -662,6 +662,18 @@
   return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL);
 }
 
+bool ExecuteScriptAndExtractDouble(const ToRenderFrameHost& adapter,
+                                   const std::string& script, double* result) {
+  DCHECK(result);
+  std::unique_ptr<base::Value> value;
+  if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
+      !value.get()) {
+    return false;
+  }
+
+  return value->GetAsDouble(result);
+}
+
 bool ExecuteScriptAndExtractInt(const ToRenderFrameHost& adapter,
                                 const std::string& script, int* result) {
   DCHECK(result);
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index fed1e73..6b2adffa 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -203,6 +203,9 @@
 // sets |result| to the value passed to "window.domAutomationController.send" by
 // the executed script. They return true on success, false if the script
 // execution failed or did not evaluate to the expected type.
+bool ExecuteScriptAndExtractDouble(const ToRenderFrameHost& adapter,
+                                   const std::string& script,
+                                   double* result) WARN_UNUSED_RESULT;
 bool ExecuteScriptAndExtractInt(const ToRenderFrameHost& adapter,
                                 const std::string& script,
                                 int* result) WARN_UNUSED_RESULT;
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 6a1daa4..dd756c4b 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -403,18 +403,13 @@
         src.keyboardShortcut().utf8());
   }
 
-  if (dst->role == ui::AX_ROLE_STATIC_TEXT ||
-      dst->role == ui::AX_ROLE_LINE_BREAK ||
-      dst->role == ui::AX_ROLE_INLINE_TEXT_BOX) {
-    if (!src.nextOnLine().isDetached()) {
-      dst->AddIntAttribute(ui::AX_ATTR_NEXT_ON_LINE_ID,
-                           src.nextOnLine().axID());
-    }
+  if (!src.nextOnLine().isDetached()) {
+    dst->AddIntAttribute(ui::AX_ATTR_NEXT_ON_LINE_ID, src.nextOnLine().axID());
+  }
 
-    if (!src.previousOnLine().isDetached()) {
-      dst->AddIntAttribute(ui::AX_ATTR_PREVIOUS_ON_LINE_ID,
-                           src.previousOnLine().axID());
-    }
+  if (!src.previousOnLine().isDetached()) {
+    dst->AddIntAttribute(ui::AX_ATTR_PREVIOUS_ON_LINE_ID,
+                         src.previousOnLine().axID());
   }
 
   if (!src.ariaActiveDescendant().isDetached()) {
diff --git a/content/renderer/bluetooth/bluetooth_dispatcher.cc b/content/renderer/bluetooth/bluetooth_dispatcher.cc
index 3ffec02..bb7a174 100644
--- a/content/renderer/bluetooth/bluetooth_dispatcher.cc
+++ b/content/renderer/bluetooth/bluetooth_dispatcher.cc
@@ -32,21 +32,6 @@
 using blink::WebString;
 using blink::WebVector;
 
-struct BluetoothPrimaryServiceRequest {
-  BluetoothPrimaryServiceRequest(
-      blink::WebString device_id,
-      blink::WebString service_uuid,
-      blink::WebBluetoothGetPrimaryServiceCallbacks* callbacks)
-      : device_id(device_id),
-        service_uuid(service_uuid),
-        callbacks(callbacks) {}
-  ~BluetoothPrimaryServiceRequest() {}
-
-  blink::WebString device_id;
-  blink::WebString service_uuid;
-  std::unique_ptr<blink::WebBluetoothGetPrimaryServiceCallbacks> callbacks;
-};
-
 namespace content {
 
 namespace {
@@ -114,10 +99,6 @@
                       OnGATTServerConnectSuccess);
   IPC_MESSAGE_HANDLER(BluetoothMsg_GATTServerConnectError,
                       OnGATTServerConnectError);
-  IPC_MESSAGE_HANDLER(BluetoothMsg_GetPrimaryServiceSuccess,
-                      OnGetPrimaryServiceSuccess);
-  IPC_MESSAGE_HANDLER(BluetoothMsg_GetPrimaryServiceError,
-                      OnGetPrimaryServiceError);
   IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   DCHECK(handled) << "Unhandled message:" << msg.type();
@@ -167,18 +148,6 @@
       CurrentWorkerId(), frame_routing_id, device_id.utf8()));
 }
 
-void BluetoothDispatcher::getPrimaryService(
-    int frame_routing_id,
-    const blink::WebString& device_id,
-    const blink::WebString& service_uuid,
-    blink::WebBluetoothGetPrimaryServiceCallbacks* callbacks) {
-  int request_id = pending_primary_service_requests_.Add(
-      new BluetoothPrimaryServiceRequest(device_id, service_uuid, callbacks));
-  Send(new BluetoothHostMsg_GetPrimaryService(
-      CurrentWorkerId(), request_id, frame_routing_id, device_id.utf8(),
-      service_uuid.utf8()));
-}
-
 void BluetoothDispatcher::WillStopCurrentWorkerThread() {
   delete this;
 }
@@ -226,28 +195,4 @@
   pending_connect_requests_.Remove(request_id);
 }
 
-void BluetoothDispatcher::OnGetPrimaryServiceSuccess(
-    int thread_id,
-    int request_id,
-    const std::string& service_instance_id) {
-  DCHECK(pending_primary_service_requests_.Lookup(request_id)) << request_id;
-  BluetoothPrimaryServiceRequest* request =
-      pending_primary_service_requests_.Lookup(request_id);
-  request->callbacks->onSuccess(
-      base::WrapUnique(new WebBluetoothRemoteGATTService(
-          WebString::fromUTF8(service_instance_id), request->service_uuid,
-          true /* isPrimary */, request->device_id)));
-  pending_primary_service_requests_.Remove(request_id);
-}
-
-void BluetoothDispatcher::OnGetPrimaryServiceError(int thread_id,
-                                                   int request_id,
-                                                   WebBluetoothError error) {
-  DCHECK(pending_primary_service_requests_.Lookup(request_id)) << request_id;
-
-  pending_primary_service_requests_.Lookup(request_id)
-      ->callbacks->onError(WebBluetoothError(error));
-  pending_primary_service_requests_.Remove(request_id);
-}
-
 }  // namespace content
diff --git a/content/renderer/bluetooth/bluetooth_dispatcher.h b/content/renderer/bluetooth/bluetooth_dispatcher.h
index 3dd5a11..561db7d 100644
--- a/content/renderer/bluetooth/bluetooth_dispatcher.h
+++ b/content/renderer/bluetooth/bluetooth_dispatcher.h
@@ -27,8 +27,6 @@
 class Message;
 }
 
-struct BluetoothPrimaryServiceRequest;
-
 namespace content {
 class ThreadSafeSender;
 
@@ -62,11 +60,6 @@
                const blink::WebString& device_id,
                blink::WebBluetoothRemoteGATTServerConnectCallbacks* callbacks);
   void disconnect(int frame_routing_id, const blink::WebString& device_id);
-  void getPrimaryService(
-      int frame_routing_id,
-      const blink::WebString& device_id,
-      const blink::WebString& service_uuid,
-      blink::WebBluetoothGetPrimaryServiceCallbacks* callbacks);
 
   // WorkerThread::Observer implementation.
   void WillStopCurrentWorkerThread() override;
@@ -83,12 +76,6 @@
   void OnGATTServerConnectError(int thread_id,
                                 int request_id,
                                 blink::WebBluetoothError error);
-  void OnGetPrimaryServiceSuccess(int thread_id,
-                                  int request_id,
-                                  const std::string& service_instance_id);
-  void OnGetPrimaryServiceError(int thread_id,
-                                int request_id,
-                                blink::WebBluetoothError error);
 
   scoped_refptr<ThreadSafeSender> thread_safe_sender_;
 
@@ -100,10 +87,6 @@
   // Owns callback objects.
   IDMap<blink::WebBluetoothRemoteGATTServerConnectCallbacks, IDMapOwnPointer>
       pending_connect_requests_;
-  // Tracks requests to get a primary service from a device.
-  // Owns request objects.
-  IDMap<BluetoothPrimaryServiceRequest, IDMapOwnPointer>
-      pending_primary_service_requests_;
 
   DISALLOW_COPY_AND_ASSIGN(BluetoothDispatcher);
 };
diff --git a/content/renderer/bluetooth/web_bluetooth_impl.cc b/content/renderer/bluetooth/web_bluetooth_impl.cc
index 9905639..1f1615f 100644
--- a/content/renderer/bluetooth/web_bluetooth_impl.cc
+++ b/content/renderer/bluetooth/web_bluetooth_impl.cc
@@ -4,6 +4,8 @@
 
 #include "content/renderer/bluetooth/web_bluetooth_impl.h"
 
+#include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/memory/ptr_util.h"
@@ -15,6 +17,7 @@
 #include "mojo/public/cpp/bindings/array.h"
 #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h"
 #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h"
+#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h"
 
 namespace content {
 
@@ -49,8 +52,11 @@
     const blink::WebString& device_id,
     const blink::WebString& service_uuid,
     blink::WebBluetoothGetPrimaryServiceCallbacks* callbacks) {
-  GetDispatcher()->getPrimaryService(frame_routing_id_, device_id, service_uuid,
-                                     callbacks);
+  GetWebBluetoothService().RemoteServerGetPrimaryService(
+      mojo::String::From(device_id), mojo::String::From(service_uuid),
+      base::Bind(&WebBluetoothImpl::OnGetPrimaryServiceComplete,
+                 base::Unretained(this), device_id,
+                 base::Passed(base::WrapUnique(callbacks))));
 }
 
 void WebBluetoothImpl::getCharacteristics(
@@ -136,6 +142,22 @@
                  value.PassStorage()));
 }
 
+void WebBluetoothImpl::OnGetPrimaryServiceComplete(
+    const blink::WebString& device_id,
+    std::unique_ptr<blink::WebBluetoothGetPrimaryServiceCallbacks> callbacks,
+    blink::mojom::WebBluetoothError error,
+    blink::mojom::WebBluetoothRemoteGATTServicePtr service) {
+  if (error == blink::mojom::WebBluetoothError::SUCCESS) {
+    callbacks->onSuccess(
+        base::WrapUnique(new blink::WebBluetoothRemoteGATTService(
+            blink::WebString::fromUTF8(service->instance_id),
+            blink::WebString::fromUTF8(service->uuid), true /* isPrimary */,
+            device_id)));
+  } else {
+    callbacks->onError(error);
+  }
+}
+
 void WebBluetoothImpl::OnGetCharacteristicsComplete(
     const blink::WebString& service_instance_id,
     std::unique_ptr<blink::WebBluetoothGetCharacteristicsCallbacks> callbacks,
diff --git a/content/renderer/bluetooth/web_bluetooth_impl.h b/content/renderer/bluetooth/web_bluetooth_impl.h
index a495253..47a03f2 100644
--- a/content/renderer/bluetooth/web_bluetooth_impl.h
+++ b/content/renderer/bluetooth/web_bluetooth_impl.h
@@ -85,6 +85,11 @@
       mojo::Array<uint8_t> value) override;
 
   // Callbacks for WebBluetoothService calls:
+  void OnGetPrimaryServiceComplete(
+      const blink::WebString& device_id,
+      std::unique_ptr<blink::WebBluetoothGetPrimaryServiceCallbacks> callbacks,
+      blink::mojom::WebBluetoothError error,
+      blink::mojom::WebBluetoothRemoteGATTServicePtr service);
   void OnGetCharacteristicsComplete(
       const blink::WebString& service_instance_id,
       std::unique_ptr<blink::WebBluetoothGetCharacteristicsCallbacks> callbacks,
diff --git a/content/renderer/devtools/devtools_agent.cc b/content/renderer/devtools/devtools_agent.cc
index 64dacd3..b718f9d 100644
--- a/content/renderer/devtools/devtools_agent.cc
+++ b/content/renderer/devtools/devtools_agent.cc
@@ -8,14 +8,17 @@
 
 #include <map>
 
+#include "base/json/json_writer.h"
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "content/common/devtools_messages.h"
 #include "content/common/frame_messages.h"
+#include "content/public/common/manifest.h"
 #include "content/renderer/devtools/devtools_client.h"
 #include "content/renderer/devtools/devtools_cpu_throttler.h"
+#include "content/renderer/manifest/manifest_manager.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_widget.h"
 #include "ipc/ipc_channel.h"
@@ -37,6 +40,8 @@
 namespace {
 
 const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
+const char kPageGetAppManifest[] = "Page.getAppManifest";
+
 
 class WebKitClientMessageLoopImpl
     : public WebDevToolsAgentClient::WebKitClientMessageLoop {
@@ -66,7 +71,8 @@
       paused_in_mouse_move_(false),
       paused_(false),
       frame_(frame),
-      cpu_throttler_(new DevToolsCPUThrottler()) {
+      cpu_throttler_(new DevToolsCPUThrottler()),
+      weak_factory_(this) {
   g_agent_for_routing_id.Get()[routing_id()] = this;
   frame_->GetWebFrame()->setDevToolsAgentClient(this);
 }
@@ -232,6 +238,13 @@
                                                  const std::string& method,
                                                  const std::string& message) {
   TRACE_EVENT0("devtools", "DevToolsAgent::OnDispatchOnInspectorBackend");
+  if (method == kPageGetAppManifest) {
+    ManifestManager* manager = frame_->manifest_manager();
+    manager->GetManifest(
+        base::Bind(&DevToolsAgent::GotManifest,
+        weak_factory_.GetWeakPtr(), session_id, call_id));
+    return;
+  }
   GetWebAgent()->dispatchOnInspectorBackend(session_id,
                                             call_id,
                                             WebString::fromUTF8(method),
@@ -269,4 +282,41 @@
   return is_attached_;
 }
 
+void DevToolsAgent::GotManifest(int session_id,
+                                int call_id,
+                                const Manifest& manifest,
+                                const ManifestDebugInfo& debug_info) {
+  if (!is_attached_)
+    return;
+
+  std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+  response->SetInteger("id", call_id);
+  std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+  std::unique_ptr<base::ListValue> errors(new base::ListValue());
+
+  bool failed = false;
+  for (const auto& error : debug_info.errors) {
+    base::DictionaryValue* error_value = new base::DictionaryValue();
+    errors->Append(error_value);
+    error_value->SetString("message", error.message);
+    error_value->SetBoolean("critical", error.critical);
+    error_value->SetInteger("line", error.line);
+    error_value->SetInteger("column", error.column);
+    if (error.critical)
+      failed = true;
+  }
+
+  WebString url = frame_->GetWebFrame()->document().manifestURL().string();
+  result->SetString("url", url);
+  if (!failed)
+    result->SetString("data", debug_info.raw_data);
+  result->Set("errors", errors.release());
+  response->Set("result", result.release());
+
+  std::string json_message;
+  base::JSONWriter::Write(*response, &json_message);
+  SendChunkedProtocolMessage(this, routing_id(), session_id, call_id,
+                             json_message, std::string());
+}
+
 }  // namespace content
diff --git a/content/renderer/devtools/devtools_agent.h b/content/renderer/devtools/devtools_agent.h
index c716a2b..b07b7a6 100644
--- a/content/renderer/devtools/devtools_agent.h
+++ b/content/renderer/devtools/devtools_agent.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "third_party/WebKit/public/web/WebDevToolsAgentClient.h"
@@ -22,6 +23,8 @@
 
 class DevToolsCPUThrottler;
 class RenderFrameImpl;
+struct Manifest;
+struct ManifestDebugInfo;
 
 // DevToolsAgent belongs to the inspectable RenderFrameImpl and communicates
 // with WebDevToolsAgent. There is a corresponding DevToolsAgentHost
@@ -85,6 +88,11 @@
   void ContinueProgram();
   void OnSetupDevToolsClient(const std::string& compatibility_script);
 
+  void GotManifest(int session_id,
+                   int command_id,
+                   const Manifest& manifest,
+                   const ManifestDebugInfo& debug_info);
+
   bool is_attached_;
   bool is_devtools_client_;
   bool paused_in_mouse_move_;
@@ -93,6 +101,7 @@
   base::Callback<void(int, int, const std::string&, const std::string&)>
       send_protocol_message_callback_for_test_;
   std::unique_ptr<DevToolsCPUThrottler> cpu_throttler_;
+  base::WeakPtrFactory<DevToolsAgent> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsAgent);
 };
diff --git a/content/renderer/manifest/manifest_debug_info.cc b/content/renderer/manifest/manifest_debug_info.cc
new file mode 100644
index 0000000..c2ff627
--- /dev/null
+++ b/content/renderer/manifest/manifest_debug_info.cc
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium 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/manifest/manifest_debug_info.h"
+
+namespace content {
+
+ManifestDebugInfo::ManifestDebugInfo() = default;
+
+ManifestDebugInfo::~ManifestDebugInfo() = default;
+
+} // namespace content
diff --git a/content/renderer/manifest/manifest_debug_info.h b/content/renderer/manifest/manifest_debug_info.h
new file mode 100644
index 0000000..b5ff7006
--- /dev/null
+++ b/content/renderer/manifest/manifest_debug_info.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium 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_MANIFEST_MANIFEST_DEBUG_INFO_H_
+#define CONTENT_RENDERER_MANIFEST_MANIFEST_DEBUG_INFO_H_
+
+#include <string>
+#include <vector>
+
+namespace content {
+
+// ManifestDebugInfo contains debug information for the parsed manifest.
+// It is created upon parsing and is available along the Manifest itself
+// via ManifestManager. Parsing errors can be generic and critical, critical
+// errors result in parser failure.
+struct ManifestDebugInfo {
+  struct Error {
+    Error(const std::string& message, bool critical, int line, int column)
+        : message(message),
+          critical(critical),
+          line(line),
+          column(column) {}
+    std::string message;
+    bool critical;
+    int line;
+    int column;
+  };
+
+  ManifestDebugInfo();
+  ~ManifestDebugInfo();
+  std::vector<Error> errors;
+  std::string raw_data;
+};
+
+} // namespace content
+
+#endif  // CONTENT_RENDERER_MANIFEST_MANIFEST_DEBUG_INFO_H_
diff --git a/content/renderer/manifest/manifest_manager.cc b/content/renderer/manifest/manifest_manager.cc
index fda8c835..1f9d4ed 100644
--- a/content/renderer/manifest/manifest_manager.cc
+++ b/content/renderer/manifest/manifest_manager.cc
@@ -60,7 +60,8 @@
 }
 
 void ManifestManager::OnRequestManifestComplete(
-    int request_id, const Manifest& manifest) {
+    int request_id, const Manifest& manifest,
+    const ManifestDebugInfo&) {
   // When sent via IPC, the Manifest must follow certain security rules.
   Manifest ipc_manifest = manifest;
   ipc_manifest.name = base::NullableString16(
@@ -92,12 +93,12 @@
 
 void ManifestManager::GetManifest(const GetManifestCallback& callback) {
   if (!may_have_manifest_) {
-    callback.Run(Manifest());
+    callback.Run(Manifest(), ManifestDebugInfo());
     return;
   }
 
   if (!manifest_dirty_) {
-    callback.Run(manifest_);
+    callback.Run(manifest_, manifest_debug_info_);
     return;
   }
 
@@ -143,6 +144,11 @@
                  render_frame()->GetWebFrame()->document().url()));
 }
 
+static const std::string& GetMessagePrefix() {
+  CR_DEFINE_STATIC_LOCAL(std::string, message_prefix, ("Manifest: "));
+  return message_prefix;
+}
+
 void ManifestManager::OnManifestFetchComplete(
     const GURL& document_url,
     const blink::WebURLResponse& response,
@@ -154,21 +160,23 @@
   }
 
   ManifestUmaUtil::FetchSucceeded();
-
   ManifestParser parser(data, response.url(), document_url);
   parser.Parse();
 
   fetcher_.reset();
+  manifest_debug_info_.raw_data = data;
+  parser.TakeErrors(&manifest_debug_info_.errors);
 
-  for (const std::unique_ptr<ManifestParser::ErrorInfo>& error_info :
-       parser.errors()) {
+  for (const auto& error : manifest_debug_info_.errors) {
     blink::WebConsoleMessage message;
-    message.level = blink::WebConsoleMessage::LevelError;
-    message.text = blink::WebString::fromUTF8(error_info->error_msg);
+    message.level = error.critical ? blink::WebConsoleMessage::LevelError :
+        blink::WebConsoleMessage::LevelWarning;
+    message.text =
+        blink::WebString::fromUTF8(GetMessagePrefix() + error.message);
     message.url =
         render_frame()->GetWebFrame()->document().manifestURL().string();
-    message.lineNumber = error_info->error_line;
-    message.columnNumber = error_info->error_column;
+    message.lineNumber = error.line;
+    message.columnNumber = error.column;
     render_frame()->GetWebFrame()->addMessageToConsole(message);
   }
 
@@ -189,14 +197,12 @@
 
   manifest_dirty_ = state != ResolveStateSuccess;
 
-  Manifest manifest = manifest_;
-  std::list<GetManifestCallback> callbacks = pending_callbacks_;
-
-  pending_callbacks_.clear();
+  std::list<GetManifestCallback> callbacks;
+  callbacks.swap(pending_callbacks_);
 
   for (std::list<GetManifestCallback>::const_iterator it = callbacks.begin();
        it != callbacks.end(); ++it) {
-    it->Run(manifest);
+    it->Run(manifest_, manifest_debug_info_);
   }
 }
 
diff --git a/content/renderer/manifest/manifest_manager.h b/content/renderer/manifest/manifest_manager.h
index 88304bbe..6ed6122 100644
--- a/content/renderer/manifest/manifest_manager.h
+++ b/content/renderer/manifest/manifest_manager.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "content/public/common/manifest.h"
 #include "content/public/renderer/render_frame_observer.h"
+#include "content/renderer/manifest/manifest_debug_info.h"
 
 class GURL;
 
@@ -31,7 +32,8 @@
 // GetManifest().
 class ManifestManager : public RenderFrameObserver {
  public:
-  typedef base::Callback<void(const Manifest&)> GetManifestCallback;
+  typedef base::Callback<void(const Manifest&,
+                              const ManifestDebugInfo&)> GetManifestCallback;
 
   explicit ManifestManager(RenderFrame* render_frame);
   ~ManifestManager() override;
@@ -56,7 +58,9 @@
   // process.
   void OnHasManifest(int request_id);
   void OnRequestManifest(int request_id);
-  void OnRequestManifestComplete(int request_id, const Manifest&);
+  void OnRequestManifestComplete(int request_id,
+                                 const Manifest&,
+                                 const ManifestDebugInfo&);
 
   void FetchManifest();
   void OnManifestFetchComplete(const GURL& document_url,
@@ -78,6 +82,9 @@
   // Current Manifest. Might be outdated if manifest_dirty_ is true.
   Manifest manifest_;
 
+  // Current Manifest debug information.
+  ManifestDebugInfo manifest_debug_info_;
+
   std::list<GetManifestCallback> pending_callbacks_;
 
   DISALLOW_COPY_AND_ASSIGN(ManifestManager);
diff --git a/content/renderer/manifest/manifest_parser.cc b/content/renderer/manifest/manifest_parser.cc
index 40d4d33..1222a55 100644
--- a/content/renderer/manifest/manifest_parser.cc
+++ b/content/renderer/manifest/manifest_parser.cc
@@ -84,12 +84,6 @@
   return sizes;
 }
 
-const std::string& GetErrorPrefix() {
-  CR_DEFINE_STATIC_LOCAL(std::string, error_prefix,
-                         ("Manifest parsing error: "));
-  return error_prefix;
-}
-
 }  // anonymous namespace
 
 
@@ -114,7 +108,7 @@
       &error_column);
 
   if (!value) {
-    AddErrorInfo(GetErrorPrefix() + error_msg, error_line, error_column);
+    AddErrorInfo(error_msg, true, error_line, error_column);
     ManifestUmaUtil::ParseFailed();
     failed_ = true;
     return;
@@ -122,8 +116,7 @@
 
   base::DictionaryValue* dictionary = nullptr;
   if (!value->GetAsDictionary(&dictionary)) {
-    AddErrorInfo(GetErrorPrefix() +
-                 "root element must be a valid JSON object.");
+    AddErrorInfo("root element must be a valid JSON object.", true);
     ManifestUmaUtil::ParseFailed();
     failed_ = true;
     return;
@@ -150,9 +143,10 @@
   return manifest_;
 }
 
-const std::vector<std::unique_ptr<ManifestParser::ErrorInfo>>&
-ManifestParser::errors() const {
-  return errors_;
+void ManifestParser::TakeErrors(
+    std::vector<ManifestDebugInfo::Error>* errors) {
+  errors->clear();
+  errors->swap(errors_);
 }
 
 bool ManifestParser::failed() const {
@@ -167,7 +161,7 @@
 
   bool value;
   if (!dictionary.GetBoolean(key, &value)) {
-    AddErrorInfo(GetErrorPrefix() + "property '" + key + "' ignored, type " +
+    AddErrorInfo("property '" + key + "' ignored, type " +
                  "boolean expected.");
     return default_value;
   }
@@ -184,7 +178,7 @@
 
   base::string16 value;
   if (!dictionary.GetString(key, &value)) {
-    AddErrorInfo(GetErrorPrefix() + "property '" + key + "' ignored, type " +
+    AddErrorInfo("property '" + key + "' ignored, type " +
                  "string expected.");
     return base::NullableString16();
   }
@@ -203,7 +197,7 @@
 
   blink::WebColor color;
   if (!blink::WebCSSParser::parseColor(&color, parsed_color.string())) {
-    AddErrorInfo(GetErrorPrefix() + "property '" + key + "' ignored, '" +
+    AddErrorInfo("property '" + key + "' ignored, '" +
                  base::UTF16ToUTF8(parsed_color.string()) + "' is not a " +
                  "valid color.");
       return Manifest::kInvalidOrMissingColor;
@@ -243,7 +237,7 @@
     return GURL();
 
   if (start_url.GetOrigin() != document_url_.GetOrigin()) {
-    AddErrorInfo(GetErrorPrefix() + "property 'start_url' ignored, should be " +
+    AddErrorInfo("property 'start_url' ignored, should be "
                  "same origin as document.");
     return GURL();
   }
@@ -266,7 +260,7 @@
   else if (base::LowerCaseEqualsASCII(display.string(), "browser"))
     return blink::WebDisplayModeBrowser;
   else {
-    AddErrorInfo(GetErrorPrefix() + "unknown 'display' value ignored.");
+    AddErrorInfo("unknown 'display' value ignored.");
     return blink::WebDisplayModeUndefined;
   }
 }
@@ -300,7 +294,7 @@
                                       "portrait-secondary"))
     return blink::WebScreenOrientationLockPortraitSecondary;
   else {
-    AddErrorInfo(GetErrorPrefix() + "unknown 'orientation' value ignored.");
+    AddErrorInfo("unknown 'orientation' value ignored.");
     return blink::WebScreenOrientationLockDefault;
   }
 }
@@ -323,7 +317,7 @@
 
   std::vector<gfx::Size> sizes = ParseIconSizesHTML(sizes_str.string());
   if (sizes.empty()) {
-    AddErrorInfo(GetErrorPrefix() + "found icon with no valid size.");
+    AddErrorInfo("found icon with no valid size.");
   }
   return sizes;
 }
@@ -336,8 +330,7 @@
 
   const base::ListValue* icons_list = nullptr;
   if (!dictionary.GetList("icons", &icons_list)) {
-    AddErrorInfo(GetErrorPrefix() +
-                 "property 'icons' ignored, type array expected.");
+    AddErrorInfo("property 'icons' ignored, type array expected.");
     return icons;
   }
 
@@ -384,9 +377,8 @@
 
   const base::ListValue* applications_list = nullptr;
   if (!dictionary.GetList("related_applications", &applications_list)) {
-    AddErrorInfo(
-        GetErrorPrefix() +
-        "property 'related_applications' ignored, type array expected.");
+    AddErrorInfo("property 'related_applications' ignored,"
+                 " type array expected.");
     return applications;
   }
 
@@ -400,9 +392,8 @@
         ParseRelatedApplicationPlatform(*application_dictionary);
     // "If platform is undefined, move onto the next item if any are left."
     if (application.platform.is_null()) {
-      AddErrorInfo(
-          GetErrorPrefix() +
-          "'platform' is a required field, related application ignored.");
+      AddErrorInfo("'platform' is a required field, related application"
+                   " ignored.");
       continue;
     }
 
@@ -411,9 +402,8 @@
     // "If both id and url are undefined, move onto the next item if any are
     // left."
     if (application.url.is_empty() && application.id.is_null()) {
-      AddErrorInfo(
-          GetErrorPrefix() +
-          "one of 'url' or 'id' is required, related application ignored.");
+      AddErrorInfo("one of 'url' or 'id' is required, related application"
+                   " ignored.");
       continue;
     }
 
@@ -444,9 +434,10 @@
 }
 
 void ManifestParser::AddErrorInfo(const std::string& error_msg,
+                                  bool critical,
                                   int error_line,
                                   int error_column) {
-  errors_.push_back(
-      base::WrapUnique(new ErrorInfo(error_msg, error_line, error_column)));
+  errors_.push_back({error_msg, critical, error_line, error_column});
 }
+
 } // namespace content
diff --git a/content/renderer/manifest/manifest_parser.h b/content/renderer/manifest/manifest_parser.h
index 5ec7f51..0f12c5a4 100644
--- a/content/renderer/manifest/manifest_parser.h
+++ b/content/renderer/manifest/manifest_parser.h
@@ -14,6 +14,7 @@
 #include "base/strings/string_piece.h"
 #include "content/common/content_export.h"
 #include "content/public/common/manifest.h"
+#include "content/renderer/manifest/manifest_debug_info.h"
 
 class GURL;
 
@@ -28,16 +29,6 @@
 // http://w3c.github.io/manifest/#dfn-steps-for-processing-a-manifest
 class CONTENT_EXPORT ManifestParser {
  public:
-  class ErrorInfo {
-   public:
-    ErrorInfo(const std::string& error_msg, int error_line, int error_column)
-        : error_msg(error_msg),
-          error_line(error_line),
-          error_column(error_column) {}
-    const std::string error_msg;
-    const int error_line;
-    const int error_column;
-  };
   ManifestParser(const base::StringPiece& data,
                  const GURL& manifest_url,
                  const GURL& document_url);
@@ -48,9 +39,10 @@
   void Parse();
 
   const Manifest& manifest() const;
-  const std::vector<std::unique_ptr<ErrorInfo>>& errors() const;
   bool failed() const;
 
+  void TakeErrors(std::vector<ManifestDebugInfo::Error>* errors);
+
  private:
   // Used to indicate whether to strip whitespace when parsing a string.
   enum TrimType {
@@ -190,6 +182,7 @@
       const base::DictionaryValue& dictionary);
 
   void AddErrorInfo(const std::string& error_msg,
+                    bool critical = false,
                     int error_line = 0,
                     int error_column = 0);
 
@@ -199,7 +192,7 @@
 
   bool failed_;
   Manifest manifest_;
-  std::vector<std::unique_ptr<ErrorInfo>> errors_;
+  std::vector<ManifestDebugInfo::Error> errors_;
 
   DISALLOW_COPY_AND_ASSIGN(ManifestParser);
 };
diff --git a/content/renderer/manifest/manifest_parser_unittest.cc b/content/renderer/manifest/manifest_parser_unittest.cc
index 47ae44d..b86a0e7 100644
--- a/content/renderer/manifest/manifest_parser_unittest.cc
+++ b/content/renderer/manifest/manifest_parser_unittest.cc
@@ -33,11 +33,12 @@
                                  const GURL& manifest_url) {
     ManifestParser parser(data, document_url, manifest_url);
     parser.Parse();
+    std::vector<ManifestDebugInfo::Error> errors;
+    parser.TakeErrors(&errors);
+
     errors_.clear();
-    for (const std::unique_ptr<ManifestParser::ErrorInfo>& error_info :
-         parser.errors()) {
-      errors_.push_back(error_info->error_msg);
-    }
+    for (const auto& error : errors)
+      errors_.push_back(error.message);
     return parser.manifest();
   }
 
@@ -74,9 +75,11 @@
                         GURL("http://example.com"),
                         GURL("http://example.com"));
   parser.Parse();
+  std::vector<ManifestDebugInfo::Error> errors;
+  parser.TakeErrors(&errors);
 
   // .Parse() should have been call without crashing and succeeded.
-  EXPECT_EQ(0u, parser.errors().size());
+  EXPECT_EQ(0u, errors.size());
   EXPECT_FALSE(parser.manifest().IsEmpty());
 }
 
@@ -85,7 +88,7 @@
 
   // This Manifest is not a valid JSON object, it's a parsing error.
   EXPECT_EQ(1u, GetErrorCount());
-  EXPECT_EQ("Manifest parsing error: Line: 1, column: 1, Unexpected token.",
+  EXPECT_EQ("Line: 1, column: 1, Unexpected token.",
             errors()[0]);
 
   // A parsing error is equivalent to an empty manifest.
@@ -126,28 +129,21 @@
 
   EXPECT_EQ(8u, GetErrorCount());
 
-  EXPECT_EQ("Manifest parsing error: property 'name' ignored,"
-            " type string expected.",
+  EXPECT_EQ("property 'name' ignored, type string expected.",
             errors()[0]);
-  EXPECT_EQ("Manifest parsing error: property 'short_name' ignored,"
-            " type string expected.",
+  EXPECT_EQ("property 'short_name' ignored, type string expected.",
             errors()[1]);
-  EXPECT_EQ("Manifest parsing error: property 'start_url' ignored,"
-            " type string expected.",
+  EXPECT_EQ("property 'start_url' ignored, type string expected.",
             errors()[2]);
-  EXPECT_EQ("Manifest parsing error: unknown 'display' value ignored.",
+  EXPECT_EQ("unknown 'display' value ignored.",
             errors()[3]);
-  EXPECT_EQ("Manifest parsing error: property 'orientation' ignored,"
-            " type string expected.",
+  EXPECT_EQ("property 'orientation' ignored, type string expected.",
             errors()[4]);
-  EXPECT_EQ("Manifest parsing error: property 'icons' ignored, "
-            "type array expected.",
+  EXPECT_EQ("property 'icons' ignored, type array expected.",
             errors()[5]);
-  EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
-            " type string expected.",
+  EXPECT_EQ("property 'theme_color' ignored, type string expected.",
             errors()[6]);
-  EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
-            " type string expected.",
+  EXPECT_EQ("property 'background_color' ignored, type string expected.",
             errors()[7]);
 }
 
@@ -172,8 +168,7 @@
     Manifest manifest = ParseManifest("{ \"name\": {} }");
     ASSERT_TRUE(manifest.name.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'name' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'name' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -182,8 +177,7 @@
     Manifest manifest = ParseManifest("{ \"name\": 42 }");
     ASSERT_TRUE(manifest.name.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'name' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'name' ignored, type string expected.",
               errors()[0]);
   }
 }
@@ -209,8 +203,7 @@
     Manifest manifest = ParseManifest("{ \"short_name\": {} }");
     ASSERT_TRUE(manifest.short_name.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'short_name' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'short_name' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -219,8 +212,7 @@
     Manifest manifest = ParseManifest("{ \"short_name\": 42 }");
     ASSERT_TRUE(manifest.short_name.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'short_name' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'short_name' ignored, type string expected.",
               errors()[0]);
   }
 }
@@ -248,8 +240,7 @@
     Manifest manifest = ParseManifest("{ \"start_url\": {} }");
     ASSERT_TRUE(manifest.start_url.is_empty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'start_url' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'start_url' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -258,8 +249,7 @@
     Manifest manifest = ParseManifest("{ \"start_url\": 42 }");
     ASSERT_TRUE(manifest.start_url.is_empty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'start_url' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'start_url' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -281,7 +271,7 @@
                               GURL("http://foo.com/index.html"));
     ASSERT_TRUE(manifest.start_url.is_empty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'start_url' ignored, should "
+    EXPECT_EQ("property 'start_url' ignored, should "
               "be same origin as document.",
               errors()[0]);
   }
@@ -318,7 +308,7 @@
     Manifest manifest = ParseManifest("{ \"display\": {} }");
     EXPECT_EQ(manifest.display, blink::WebDisplayModeUndefined);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'display' ignored,"
+    EXPECT_EQ("property 'display' ignored,"
               " type string expected.",
               errors()[0]);
   }
@@ -328,7 +318,7 @@
     Manifest manifest = ParseManifest("{ \"display\": 42 }");
     EXPECT_EQ(manifest.display, blink::WebDisplayModeUndefined);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'display' ignored,"
+    EXPECT_EQ("property 'display' ignored,"
               " type string expected.",
               errors()[0]);
   }
@@ -338,7 +328,7 @@
     Manifest manifest = ParseManifest("{ \"display\": \"browser_something\" }");
     EXPECT_EQ(manifest.display, blink::WebDisplayModeUndefined);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: unknown 'display' value ignored.",
+    EXPECT_EQ("unknown 'display' value ignored.",
               errors()[0]);
   }
 
@@ -399,8 +389,7 @@
     Manifest manifest = ParseManifest("{ \"orientation\": {} }");
     EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'orientation' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'orientation' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -409,8 +398,7 @@
     Manifest manifest = ParseManifest("{ \"orientation\": 42 }");
     EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'orientation' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'orientation' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -419,7 +407,7 @@
     Manifest manifest = ParseManifest("{ \"orientation\": \"naturalish\" }");
     EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: unknown 'orientation' value ignored.",
+    EXPECT_EQ("unknown 'orientation' value ignored.",
               errors()[0]);
   }
 
@@ -564,8 +552,7 @@
     Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": {} } ] }");
     EXPECT_TRUE(manifest.icons.empty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'src' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'src' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -574,8 +561,7 @@
     Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": 42 } ] }");
     EXPECT_TRUE(manifest.icons.empty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'src' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'src' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -614,8 +600,7 @@
         ParseManifest("{ \"icons\": [ {\"src\": \"\", \"type\": {} } ] }");
     EXPECT_TRUE(manifest.icons[0].type.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'type' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'type' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -625,8 +610,7 @@
         ParseManifest("{ \"icons\": [ {\"src\": \"\", \"type\": 42 } ] }");
     EXPECT_TRUE(manifest.icons[0].type.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'type' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'type' ignored, type string expected.",
               errors()[0]);
   }
 }
@@ -654,8 +638,7 @@
         "\"sizes\": {} } ] }");
     EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'sizes' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'sizes' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -665,8 +648,7 @@
         "\"sizes\": 42 } ] }");
     EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'sizes' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'sizes' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -703,7 +685,7 @@
         "\"sizes\": \"004X007  042x00\" } ] }");
     EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: found icon with no valid size.",
+    EXPECT_EQ("found icon with no valid size.",
               errors()[0]);
   }
 
@@ -713,7 +695,7 @@
         "\"sizes\": \"e4X1.0  55ax1e10\" } ] }");
     EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: found icon with no valid size.",
+    EXPECT_EQ("found icon with no valid size.",
               errors()[0]);
   }
 
@@ -737,7 +719,7 @@
     gfx::Size any = gfx::Size(0, 0);
     EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: found icon with no valid size.",
+    EXPECT_EQ("found icon with no valid size.",
               errors()[0]);
   }
 }
@@ -759,8 +741,7 @@
     EXPECT_EQ(manifest.related_applications.size(), 0u);
     EXPECT_TRUE(manifest.IsEmpty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: 'platform' is a required field, "
-              "related application ignored.",
+    EXPECT_EQ("'platform' is a required field, related application ignored.",
               errors()[0]);
   }
 
@@ -772,10 +753,9 @@
     EXPECT_TRUE(manifest.IsEmpty());
     EXPECT_EQ(2u, GetErrorCount());
     EXPECT_EQ(
-        "Manifest parsing error: property 'platform' ignored, type string "
-        "expected.",
+        "property 'platform' ignored, type string expected.",
         errors()[0]);
-    EXPECT_EQ("Manifest parsing error: 'platform' is a required field, "
+    EXPECT_EQ("'platform' is a required field, "
               "related application ignored.",
               errors()[1]);
   }
@@ -787,8 +767,7 @@
     EXPECT_EQ(manifest.related_applications.size(), 0u);
     EXPECT_TRUE(manifest.IsEmpty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: 'platform' is a required field, "
-              "related application ignored.",
+    EXPECT_EQ("'platform' is a required field, related application ignored.",
               errors()[0]);
   }
 
@@ -799,8 +778,7 @@
     EXPECT_EQ(manifest.related_applications.size(), 0u);
     EXPECT_TRUE(manifest.IsEmpty());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: one of 'url' or 'id' is required, "
-              "related application ignored.",
+    EXPECT_EQ("one of 'url' or 'id' is required, related application ignored.",
               errors()[0]);
   }
 
@@ -871,11 +849,9 @@
                                   "foo"));
     EXPECT_FALSE(manifest.IsEmpty());
     EXPECT_EQ(2u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: one of 'url' or 'id' is required, "
-              "related application ignored.",
+    EXPECT_EQ("one of 'url' or 'id' is required, related application ignored.",
               errors()[0]);
-    EXPECT_EQ("Manifest parsing error: 'platform' is a required field, "
-              "related application ignored.",
+    EXPECT_EQ("'platform' is a required field, related application ignored.",
               errors()[1]);
   }
 }
@@ -896,7 +872,7 @@
     EXPECT_FALSE(manifest.prefer_related_applications);
     EXPECT_EQ(1u, GetErrorCount());
     EXPECT_EQ(
-        "Manifest parsing error: property 'prefer_related_applications' "
+        "property 'prefer_related_applications' "
         "ignored, type boolean expected.",
         errors()[0]);
   }
@@ -906,7 +882,7 @@
     EXPECT_FALSE(manifest.prefer_related_applications);
     EXPECT_EQ(1u, GetErrorCount());
     EXPECT_EQ(
-        "Manifest parsing error: property 'prefer_related_applications' "
+        "property 'prefer_related_applications' "
         "ignored, type boolean expected.",
         errors()[0]);
   }
@@ -915,7 +891,7 @@
     EXPECT_FALSE(manifest.prefer_related_applications);
     EXPECT_EQ(1u, GetErrorCount());
     EXPECT_EQ(
-        "Manifest parsing error: property 'prefer_related_applications' "
+        "property 'prefer_related_applications' "
         "ignored, type boolean expected.",
         errors()[0]);
   }
@@ -950,8 +926,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": {} }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'theme_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -960,8 +935,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": false }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'theme_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -970,8 +944,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": null }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'theme_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -980,8 +953,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": [] }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'theme_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -990,8 +962,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": 42 }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'theme_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -1000,7 +971,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": \"foo(bar)\" }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored,"
+    EXPECT_EQ("property 'theme_color' ignored,"
               " 'foo(bar)' is not a valid color.",
               errors()[0]);
   }
@@ -1010,8 +981,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": \"bleu\" }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored, 'bleu'"
-              " is not a valid color.",
+    EXPECT_EQ("property 'theme_color' ignored, 'bleu' is not a valid color.",
               errors()[0]);
   }
 
@@ -1020,7 +990,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": \"FF00FF\" }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored, 'FF00FF'"
+    EXPECT_EQ("property 'theme_color' ignored, 'FF00FF'"
               " is not a valid color.",
               errors()[0]);
   }
@@ -1030,7 +1000,7 @@
     Manifest manifest = ParseManifest("{ \"theme_color\": \"#ABC #DEF\" }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored, "
+    EXPECT_EQ("property 'theme_color' ignored, "
               "'#ABC #DEF' is not a valid color.",
               errors()[0]);
   }
@@ -1041,7 +1011,7 @@
         "{ \"theme_color\": \"#AABBCC #DDEEFF\" }");
     EXPECT_EQ(manifest.theme_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'theme_color' ignored, "
+    EXPECT_EQ("property 'theme_color' ignored, "
               "'#AABBCC #DDEEFF' is not a valid color.",
               errors()[0]);
   }
@@ -1119,8 +1089,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": {} }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'background_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -1129,8 +1098,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": false }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'background_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -1139,8 +1107,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": null }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'background_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -1149,8 +1116,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": [] }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'background_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -1159,8 +1125,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": 42 }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'background_color' ignored, type string expected.",
               errors()[0]);
   }
 
@@ -1169,7 +1134,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": \"foo(bar)\" }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
+    EXPECT_EQ("property 'background_color' ignored,"
               " 'foo(bar)' is not a valid color.",
               errors()[0]);
   }
@@ -1179,7 +1144,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": \"bleu\" }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
+    EXPECT_EQ("property 'background_color' ignored,"
               " 'bleu' is not a valid color.",
               errors()[0]);
   }
@@ -1189,7 +1154,7 @@
     Manifest manifest = ParseManifest("{ \"background_color\": \"FF00FF\" }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored,"
+    EXPECT_EQ("property 'background_color' ignored,"
               " 'FF00FF' is not a valid color.",
               errors()[0]);
   }
@@ -1200,7 +1165,7 @@
         "{ \"background_color\": \"#ABC #DEF\" }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored, "
+    EXPECT_EQ("property 'background_color' ignored, "
               "'#ABC #DEF' is not a valid color.",
               errors()[0]);
   }
@@ -1211,7 +1176,7 @@
         "{ \"background_color\": \"#AABBCC #DDEEFF\" }");
     EXPECT_EQ(manifest.background_color, Manifest::kInvalidOrMissingColor);
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'background_color' ignored, "
+    EXPECT_EQ("property 'background_color' ignored, "
               "'#AABBCC #DDEEFF' is not a valid color.",
               errors()[0]);
   }
@@ -1289,16 +1254,14 @@
     Manifest manifest = ParseManifest("{ \"gcm_sender_id\": {} }");
     EXPECT_TRUE(manifest.gcm_sender_id.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'gcm_sender_id' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'gcm_sender_id' ignored, type string expected.",
               errors()[0]);
   }
   {
     Manifest manifest = ParseManifest("{ \"gcm_sender_id\": 42 }");
     EXPECT_TRUE(manifest.gcm_sender_id.is_null());
     EXPECT_EQ(1u, GetErrorCount());
-    EXPECT_EQ("Manifest parsing error: property 'gcm_sender_id' ignored,"
-              " type string expected.",
+    EXPECT_EQ("property 'gcm_sender_id' ignored, type string expected.",
               errors()[0]);
   }
 }
diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc
index 1122903..31b0b33 100644
--- a/content/renderer/media/webmediaplayer_ms.cc
+++ b/content/renderer/media/webmediaplayer_ms.cc
@@ -12,10 +12,10 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "build/build_config.h"
-#include "cc/blink/context_provider_web_context.h"
 #include "cc/blink/web_layer_impl.h"
 #include "cc/layers/video_frame_provider_client_impl.h"
 #include "cc/layers/video_layer.h"
+#include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/public/renderer/media_stream_audio_renderer.h"
 #include "content/public/renderer/media_stream_renderer_factory.h"
 #include "content/public/renderer/video_frame_provider.h"
@@ -328,11 +328,12 @@
   DVLOG(3) << __FUNCTION__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  const scoped_refptr<media::VideoFrame> frame = compositor_->GetCurrentFrame();
+  const scoped_refptr<media::VideoFrame> frame =
+      compositor_->GetCurrentFrameWithoutUpdatingStatistics();
 
   media::Context3D context_3d;
   if (frame && frame->HasTextures()) {
-    cc::ContextProvider* provider =
+    auto* provider =
         RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
     // GPU Process crashed.
     if (!provider)
@@ -452,7 +453,8 @@
   TRACE_EVENT0("media", "WebMediaPlayerMS:copyVideoTextureToPlatformTexture");
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  scoped_refptr<media::VideoFrame> video_frame = compositor_->GetCurrentFrame();
+  scoped_refptr<media::VideoFrame> video_frame =
+      compositor_->GetCurrentFrameWithoutUpdatingStatistics();
 
   if (!video_frame.get() || !video_frame->HasTextures() ||
       media::VideoFrame::NumPlanes(video_frame->format()) != 1) {
diff --git a/content/renderer/media/webmediaplayer_ms_compositor.cc b/content/renderer/media/webmediaplayer_ms_compositor.cc
index a95357b..ba37698 100644
--- a/content/renderer/media/webmediaplayer_ms_compositor.cc
+++ b/content/renderer/media/webmediaplayer_ms_compositor.cc
@@ -11,7 +11,7 @@
 #include "base/hash.h"
 #include "base/single_thread_task_runner.h"
 #include "base/values.h"
-#include "cc/blink/context_provider_web_context.h"
+#include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/renderer/media/webmediaplayer_ms.h"
 #include "content/renderer/render_thread_impl.h"
 #include "media/base/media_switches.h"
@@ -49,7 +49,7 @@
                           frame->visible_rect().height());
     SkCanvas canvas(bitmap);
 
-    cc::ContextProvider* const provider =
+    auto* provider =
         RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
     if (provider) {
       const media::Context3D context_3d =
@@ -287,13 +287,19 @@
 scoped_refptr<media::VideoFrame> WebMediaPlayerMSCompositor::GetCurrentFrame() {
   DVLOG(3) << __FUNCTION__;
   base::AutoLock auto_lock(current_frame_lock_);
+  current_frame_used_by_compositor_ = true;
   return current_frame_;
 }
 
 void WebMediaPlayerMSCompositor::PutCurrentFrame() {
   DVLOG(3) << __FUNCTION__;
+}
+
+scoped_refptr<media::VideoFrame>
+WebMediaPlayerMSCompositor::GetCurrentFrameWithoutUpdatingStatistics() {
+  DVLOG(3) << __FUNCTION__;
   base::AutoLock auto_lock(current_frame_lock_);
-  current_frame_used_by_compositor_ = true;
+  return current_frame_;
 }
 
 void WebMediaPlayerMSCompositor::StartRendering() {
diff --git a/content/renderer/media/webmediaplayer_ms_compositor.h b/content/renderer/media/webmediaplayer_ms_compositor.h
index 927c39e8..c588d59 100644
--- a/content/renderer/media/webmediaplayer_ms_compositor.h
+++ b/content/renderer/media/webmediaplayer_ms_compositor.h
@@ -78,6 +78,13 @@
   scoped_refptr<media::VideoFrame> GetCurrentFrame() override;
   void PutCurrentFrame() override;
 
+  // Return the current frame being rendered.
+  // Difference between GetCurrentFrame(): GetCurrentFrame() is designed for
+  // chrome compositor to pull frame from WebMediaPlayerMSCompositor, and thus
+  // calling GetCurrentFrame() will affect statistics like |dropped_frames_|
+  // etc. Calling this function has no side effect.
+  scoped_refptr<media::VideoFrame> GetCurrentFrameWithoutUpdatingStatistics();
+
   void StartRendering();
   void StopRendering();
   void ReplaceCurrentFrameWithACopy();
diff --git a/content/renderer/media/webmediaplayer_ms_unittest.cc b/content/renderer/media/webmediaplayer_ms_unittest.cc
index e3b7530..70520aa 100644
--- a/content/renderer/media/webmediaplayer_ms_unittest.cc
+++ b/content/renderer/media/webmediaplayer_ms_unittest.cc
@@ -634,10 +634,10 @@
   // Here we call pause, and expect a freezing frame.
   EXPECT_CALL(*this, DoStopRendering());
   player_.pause();
-  auto prev_frame = compositor_->GetCurrentFrame();
+  auto prev_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   message_loop_controller_.RunAndWaitForStatus(
       media::PipelineStatus::PIPELINE_OK);
-  auto after_frame = compositor_->GetCurrentFrame();
+  auto after_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   EXPECT_EQ(prev_frame->timestamp(), after_frame->timestamp());
   testing::Mock::VerifyAndClearExpectations(this);
 
@@ -675,20 +675,20 @@
   // Here we call pause, and expect a freezing frame.
   EXPECT_CALL(*this, DoStopRendering());
   player_.pause();
-  auto prev_frame = compositor_->GetCurrentFrame();
+  auto prev_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   message_loop_controller_.RunAndWaitForStatus(
       media::PipelineStatus::PIPELINE_OK);
-  auto after_frame = compositor_->GetCurrentFrame();
+  auto after_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   EXPECT_EQ(prev_frame->timestamp(), after_frame->timestamp());
   testing::Mock::VerifyAndClearExpectations(this);
 
   // We resume the player, and expect rendering can continue.
   EXPECT_CALL(*this, DoStartRendering());
   player_.play();
-  prev_frame = compositor_->GetCurrentFrame();
+  prev_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   message_loop_controller_.RunAndWaitForStatus(
       media::PipelineStatus::PIPELINE_OK);
-  after_frame = compositor_->GetCurrentFrame();
+  after_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   EXPECT_NE(prev_frame->timestamp(), after_frame->timestamp());
   testing::Mock::VerifyAndClearExpectations(this);
 
@@ -731,18 +731,18 @@
 
   // Switch to background rendering, expect rendering to continue.
   SetBackgroundRendering(true);
-  auto prev_frame = compositor_->GetCurrentFrame();
+  auto prev_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   message_loop_controller_.RunAndWaitForStatus(
       media::PipelineStatus::PIPELINE_OK);
-  auto after_frame = compositor_->GetCurrentFrame();
+  auto after_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   EXPECT_NE(prev_frame->timestamp(), after_frame->timestamp());
 
   // Switch to foreground rendering.
   SetBackgroundRendering(false);
-  prev_frame = compositor_->GetCurrentFrame();
+  prev_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   message_loop_controller_.RunAndWaitForStatus(
       media::PipelineStatus::PIPELINE_OK);
-  after_frame = compositor_->GetCurrentFrame();
+  after_frame = compositor_->GetCurrentFrameWithoutUpdatingStatistics();
   EXPECT_NE(prev_frame->timestamp(), after_frame->timestamp());
   testing::Mock::VerifyAndClearExpectations(this);
 
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index 2b01901e..9c87355 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -16,7 +16,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/single_thread_task_runner.h"
 #include "base/thread_task_runner_handle.h"
-#include "cc/blink/context_provider_web_context.h"
+#include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/renderer/pepper/pepper_video_decoder_host.h"
 #include "content/renderer/render_thread_impl.h"
@@ -61,7 +61,7 @@
 // YUV->RGB converter class using a shader and FBO.
 class VideoDecoderShim::YUVConverter {
  public:
-  YUVConverter(const scoped_refptr<cc_blink::ContextProviderWebContext>&);
+  YUVConverter(scoped_refptr<ContextProviderCommandBuffer>);
   ~YUVConverter();
   bool Initialize();
   void Convert(const scoped_refptr<media::VideoFrame>& frame, GLuint tex_out);
@@ -72,7 +72,7 @@
   GLuint CreateProgram(const char* name, GLuint vshader, GLuint fshader);
   GLuint CreateTexture();
 
-  scoped_refptr<cc_blink::ContextProviderWebContext> context_provider_;
+  scoped_refptr<ContextProviderCommandBuffer> context_provider_;
   gpu::gles2::GLES2Interface* gl_;
   GLuint frame_buffer_;
   GLuint vertex_buffer_;
@@ -102,8 +102,8 @@
 };
 
 VideoDecoderShim::YUVConverter::YUVConverter(
-    const scoped_refptr<cc_blink::ContextProviderWebContext>& context_provider)
-    : context_provider_(context_provider),
+    scoped_refptr<ContextProviderCommandBuffer> context_provider)
+    : context_provider_(std::move(context_provider)),
       gl_(context_provider_->ContextGL()),
       frame_buffer_(0),
       vertex_buffer_(0),
diff --git a/content/renderer/pepper/video_decoder_shim.h b/content/renderer/pepper/video_decoder_shim.h
index db4a830c..68ac06f 100644
--- a/content/renderer/pepper/video_decoder_shim.h
+++ b/content/renderer/pepper/video_decoder_shim.h
@@ -23,10 +23,6 @@
 class SingleThreadTaskRunner;
 }
 
-namespace cc_blink {
-class ContextProviderWebContext;
-}
-
 namespace gpu {
 namespace gles2 {
 class GLES2Interface;
@@ -39,6 +35,7 @@
 
 namespace content {
 
+class ContextProviderCommandBuffer;
 class PepperVideoDecoderHost;
 
 // This class is a shim to wrap a media::VideoDecoder so that it can be used
@@ -90,7 +87,7 @@
 
   PepperVideoDecoderHost* host_;
   scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
-  scoped_refptr<cc_blink::ContextProviderWebContext> context_provider_;
+  scoped_refptr<ContextProviderCommandBuffer> context_provider_;
 
   // The current decoded frame size.
   gfx::Size texture_size_;
diff --git a/content/renderer/push_messaging/push_messaging_dispatcher.cc b/content/renderer/push_messaging/push_messaging_dispatcher.cc
index 1cfc044f..e25af93 100644
--- a/content/renderer/push_messaging/push_messaging_dispatcher.cc
+++ b/content/renderer/push_messaging/push_messaging_dispatcher.cc
@@ -67,7 +67,8 @@
     blink::WebServiceWorkerRegistration* service_worker_registration,
     const blink::WebPushSubscriptionOptions& options,
     blink::WebPushSubscriptionCallbacks* callbacks,
-    const Manifest& manifest) {
+    const Manifest& manifest,
+    const ManifestDebugInfo&) {
   // Get the sender_info from the manifest since it wasn't provided by
   // the caller.
   if (manifest.IsEmpty()) {
diff --git a/content/renderer/push_messaging/push_messaging_dispatcher.h b/content/renderer/push_messaging/push_messaging_dispatcher.h
index 71294e2..5dcbf8b 100644
--- a/content/renderer/push_messaging/push_messaging_dispatcher.h
+++ b/content/renderer/push_messaging/push_messaging_dispatcher.h
@@ -29,6 +29,7 @@
 namespace content {
 
 struct Manifest;
+struct ManifestDebugInfo;
 struct PushSubscriptionOptions;
 
 class PushMessagingDispatcher : public RenderFrameObserver,
@@ -51,7 +52,8 @@
       blink::WebServiceWorkerRegistration* service_worker_registration,
       const blink::WebPushSubscriptionOptions& options,
       blink::WebPushSubscriptionCallbacks* callbacks,
-      const Manifest& manifest);
+      const Manifest& manifest,
+      const ManifestDebugInfo&);
 
   void DoSubscribe(
       blink::WebServiceWorkerRegistration* service_worker_registration,
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 029ec0bc..8b82079 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -52,6 +52,7 @@
 #include "content/common/clipboard_messages.h"
 #include "content/common/frame_messages.h"
 #include "content/common/frame_replication_state.h"
+#include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/common/input_messages.h"
 #include "content/common/navigation_params.h"
 #include "content/common/page_messages.h"
@@ -201,7 +202,6 @@
 #if defined(OS_ANDROID)
 #include <cpu-features.h>
 
-#include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/renderer/java/gin_java_bridge_dispatcher.h"
 #include "content/renderer/media/android/renderer_media_player_manager.h"
 #include "content/renderer/media/android/renderer_media_session_manager.h"
@@ -211,8 +211,6 @@
 #include "content/renderer/media/android/webmediasession_android.h"
 #include "media/base/android/media_codec_util.h"
 #include "third_party/WebKit/public/platform/WebFloatPoint.h"
-#else
-#include "cc/blink/context_provider_web_context.h"
 #endif
 
 #if defined(ENABLE_PEPPER_CDMS)
@@ -603,7 +601,7 @@
 }
 
 media::Context3D GetSharedMainThreadContext3D() {
-  cc::ContextProvider* provider =
+  ContextProviderCommandBuffer* provider =
       RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
   if (!provider)
     return media::Context3D();
@@ -4568,6 +4566,8 @@
     DCHECK(!navigation_state->request_params().should_clear_history_list);
     params.history_list_was_cleared = false;
     params.report_type = FrameMsg_UILoadMetricsReportType::NO_REPORT;
+    // Subframes should match the zoom level of the main frame.
+    render_view_->webview()->setZoomLevel(render_view_->page_zoom_level());
   }
 
   // This message needs to be sent before any of allowScripts(),
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index fdba7c5..bb7fee6 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -259,51 +259,6 @@
   DISALLOW_COPY_AND_ASSIGN(WebThreadForCompositor);
 };
 
-class RenderViewZoomer : public RenderViewVisitor {
- public:
-  RenderViewZoomer(const std::string& scheme,
-                   const std::string& host,
-                   double zoom_level) : scheme_(scheme),
-                                        host_(host),
-                                        zoom_level_(zoom_level) {
-  }
-
-  bool Visit(RenderView* render_view) override {
-    WebView* webview = render_view->GetWebView();
-    RenderViewImpl* render_view_impl =
-        static_cast<RenderViewImpl*>(render_view);
-    // Remote frames don't host documents.
-    // TODO(wjmaclean) Although it seems likely that a frame without a
-    // document can safely early-out here, we should confirm this is truly
-    // the case. https://crbug.com/477007
-    if (webview->mainFrame()->isWebRemoteFrame())
-      return true;
-
-    WebDocument document = webview->mainFrame()->document();
-
-    // Don't set zoom level for full-page plugin since they don't use the same
-    // zoom settings.
-    if (document.isPluginDocument())
-      return true;
-    GURL url(document.url());
-    // Empty scheme works as wildcard that matches any scheme,
-    if ((net::GetHostOrSpecFromURL(url) == host_) &&
-        (scheme_.empty() || scheme_ == url.scheme()) &&
-        !render_view_impl->uses_temporary_zoom_level()) {
-      webview->hidePopups();
-      render_view_impl->SetZoomLevel(zoom_level_);
-    }
-    return true;
-  }
-
- private:
-  const std::string scheme_;
-  const std::string host_;
-  const double zoom_level_;
-
-  DISALLOW_COPY_AND_ASSIGN(RenderViewZoomer);
-};
-
 void* CreateHistogram(
     const char *name, int min, int max, size_t buckets) {
   if (min <= 0)
@@ -447,8 +402,7 @@
 }
 
 std::unique_ptr<WebGraphicsContext3DCommandBufferImpl> CreateOffscreenContext(
-    scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
-    bool share_resources) {
+    scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
   DCHECK(gpu_channel_host);
   // This is used to create a few different offscreen contexts:
   // - The shared main thread context (offscreen) used by blink for canvas.
@@ -468,7 +422,7 @@
       gpu::kNullSurfaceHandle,
       GURL("chrome://gpu/RenderThreadImpl::CreateOffscreenContext3d"),
       gpu_channel_host.get(), attributes, gfx::PreferIntegratedGpu,
-      share_resources, automatic_flushes, nullptr));
+      automatic_flushes));
 }
 
 }  // namespace
@@ -1483,7 +1437,7 @@
   return nullptr;
 }
 
-scoped_refptr<cc_blink::ContextProviderWebContext>
+scoped_refptr<ContextProviderCommandBuffer>
 RenderThreadImpl::SharedMainThreadContextProvider() {
   DCHECK(IsMainThread());
   if (shared_main_thread_contexts_ &&
@@ -1498,10 +1452,9 @@
     return nullptr;
   }
 
-  constexpr bool share_resources = false;
   shared_main_thread_contexts_ = new ContextProviderCommandBuffer(
-      CreateOffscreenContext(std::move(gpu_channel_host), share_resources),
-      gpu::SharedMemoryLimits(), RENDERER_MAINTHREAD_CONTEXT);
+      CreateOffscreenContext(std::move(gpu_channel_host)),
+      gpu::SharedMemoryLimits(), nullptr, RENDERER_MAINTHREAD_CONTEXT);
   if (!shared_main_thread_contexts_->BindToCurrentThread())
     shared_main_thread_contexts_ = nullptr;
   return shared_main_thread_contexts_;
@@ -1700,8 +1653,6 @@
   IPC_BEGIN_MESSAGE_MAP(RenderThreadImpl, msg)
     IPC_MESSAGE_HANDLER(FrameMsg_NewFrame, OnCreateNewFrame)
     IPC_MESSAGE_HANDLER(FrameMsg_NewFrameProxy, OnCreateNewFrameProxy)
-    IPC_MESSAGE_HANDLER(ViewMsg_SetZoomLevelForCurrentURL,
-                        OnSetZoomLevelForCurrentURL)
     // TODO(port): removed from render_messages_internal.h;
     // is there a new non-windows message I should add here?
     IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
@@ -1763,13 +1714,6 @@
                                      replicated_state);
 }
 
-void RenderThreadImpl::OnSetZoomLevelForCurrentURL(const std::string& scheme,
-                                                   const std::string& host,
-                                                   double zoom_level) {
-  RenderViewZoomer zoomer(scheme, host, zoom_level);
-  RenderView::ForEach(&zoomer);
-}
-
 void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) {
   CompositorDependencies* compositor_deps = this;
   // When bringing in render_view, also bring in webkit's glue and jsbindings.
@@ -2010,10 +1954,9 @@
     return shared_worker_context_provider_;
   }
 
-  constexpr bool share_resources = true;
   shared_worker_context_provider_ = new ContextProviderCommandBuffer(
-      CreateOffscreenContext(std::move(gpu_channel_host), share_resources),
-      gpu::SharedMemoryLimits(), RENDER_WORKER_CONTEXT);
+      CreateOffscreenContext(std::move(gpu_channel_host)),
+      gpu::SharedMemoryLimits(), nullptr, RENDER_WORKER_CONTEXT);
   if (!shared_worker_context_provider_->BindToCurrentThread())
     shared_worker_context_provider_ = nullptr;
   if (shared_worker_context_provider_)
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index a6254f0..cbb564da 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -47,7 +47,6 @@
 
 namespace blink {
 class WebGamepads;
-class WebGraphicsContext3D;
 class WebMediaStreamCenter;
 class WebMediaStreamCenterClient;
 }
@@ -63,10 +62,6 @@
 class TaskGraphRunner;
 }
 
-namespace cc_blink {
-class ContextProviderWebContext;
-}
-
 namespace gpu {
 class GpuChannelHost;
 }
@@ -355,8 +350,7 @@
 
   media::GpuVideoAcceleratorFactories* GetGpuFactories();
 
-  scoped_refptr<cc_blink::ContextProviderWebContext>
-  SharedMainThreadContextProvider();
+  scoped_refptr<ContextProviderCommandBuffer> SharedMainThreadContextProvider();
 
   // AudioRendererMixerManager instance which manages renderer side mixer
   // instances shared based on configured audio parameters.  Lazily created on
@@ -489,9 +483,6 @@
                              int opener_routing_id,
                              int parent_routing_id,
                              const FrameReplicationState& replicated_state);
-  void OnSetZoomLevelForCurrentURL(const std::string& scheme,
-                                   const std::string& host,
-                                   double zoom_level);
   void OnCreateNewView(const ViewMsg_New_Params& params);
   void OnTransferBitmap(const SkBitmap& bitmap, int resource_id);
 #if defined(ENABLE_PLUGINS)
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 9530526..e66c4c30 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -368,7 +368,8 @@
   }
 
   void SetZoomLevel(double level) {
-    view()->OnSetZoomLevelForView(false, level);
+    view()->OnSetZoomLevel(
+        PageMsg_SetZoomLevel_Command::USE_CURRENT_TEMPORARY_MODE, level);
   }
 
  private:
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 5d5bad3..b79693b 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -631,6 +631,7 @@
       top_controls_height_(0.f),
       has_focus_(false),
       has_scrolled_focused_editable_node_into_rect_(false),
+      page_zoom_level_(params.page_zoom_level),
       main_render_frame_(nullptr),
       frame_widget_(nullptr),
       speech_recognition_dispatcher_(NULL),
@@ -801,6 +802,8 @@
     webview()->mainFrame()->toWebLocalFrame()->forceSandboxFlags(
         params.replicated_frame_state.sandbox_flags);
   }
+
+  page_zoom_level_ = params.page_zoom_level;
 }
 
 RenderViewImpl::~RenderViewImpl() {
@@ -1303,8 +1306,6 @@
     IPC_MESSAGE_HANDLER(ViewMsg_Zoom, OnZoom)
     IPC_MESSAGE_HANDLER(ViewMsg_SetZoomLevelForLoadingURL,
                         OnSetZoomLevelForLoadingURL)
-    IPC_MESSAGE_HANDLER(ViewMsg_SetZoomLevelForView,
-                        OnSetZoomLevelForView)
     IPC_MESSAGE_HANDLER(ViewMsg_SetPageEncoding, OnSetPageEncoding)
     IPC_MESSAGE_HANDLER(ViewMsg_ResetPageEncodingToDefault,
                         OnResetPageEncodingToDefault)
@@ -1345,8 +1346,11 @@
                         OnReleaseDisambiguationPopupBitmap)
     IPC_MESSAGE_HANDLER(ViewMsg_ForceRedraw, OnForceRedraw)
     IPC_MESSAGE_HANDLER(ViewMsg_SelectWordAroundCaret, OnSelectWordAroundCaret)
+
+    // Page messages.
     IPC_MESSAGE_HANDLER(PageMsg_UpdateWindowScreenRect,
                         OnUpdateWindowScreenRect)
+    IPC_MESSAGE_HANDLER(PageMsg_SetZoomLevel, OnSetZoomLevel)
 #if defined(OS_ANDROID)
     IPC_MESSAGE_HANDLER(ViewMsg_UpdateTopControlsState,
                         OnUpdateTopControlsState)
@@ -1601,6 +1605,7 @@
   view_params.enable_auto_resize = false;
   view_params.min_size = gfx::Size();
   view_params.max_size = gfx::Size();
+  view_params.page_zoom_level = page_zoom_level_;
 
   RenderViewImpl* view =
       RenderViewImpl::Create(compositor_deps_, view_params, true);
@@ -1674,6 +1679,10 @@
 }
 
 void RenderViewImpl::SetZoomLevel(double zoom_level) {
+  // If we change the zoom level for the view, make sure any subsequent subframe
+  // loads reflect the current zoom level.
+  page_zoom_level_ = zoom_level;
+
   webview()->setZoomLevel(zoom_level);
   FOR_EACH_OBSERVER(RenderViewObserver, observers_, OnZoomLevelChanged());
 }
@@ -2381,6 +2390,11 @@
 
 void RenderViewImpl::OnSetZoomLevelForLoadingURL(const GURL& url,
                                                  double zoom_level) {
+  // TODO(wjmaclean): We should see if this restriction is really necessary,
+  // since it isn't enforced in other parts of the page zoom system (e.g.
+  // when a users changes the zoom of a currently displayed page). Android
+  // has no UI for this, so in theory the following code would normally just use
+  // the default zoom anyways.
 #if !defined(OS_ANDROID)
   // On Android, page zoom isn't used, and in case of WebView, text zoom is used
   // for legacy WebView text scaling emulation. Thus, the code that resets
@@ -2389,12 +2403,26 @@
 #endif
 }
 
-void RenderViewImpl::OnSetZoomLevelForView(bool uses_temporary_zoom_level,
-                                           double level) {
-  uses_temporary_zoom_level_ = uses_temporary_zoom_level;
-
+void RenderViewImpl::OnSetZoomLevel(
+    PageMsg_SetZoomLevel_Command command,
+    double zoom_level) {
+  switch (command) {
+    case PageMsg_SetZoomLevel_Command::CLEAR_TEMPORARY:
+      uses_temporary_zoom_level_ = false;
+      break;
+    case PageMsg_SetZoomLevel_Command::SET_TEMPORARY:
+      uses_temporary_zoom_level_ = true;
+      break;
+    case PageMsg_SetZoomLevel_Command::USE_CURRENT_TEMPORARY_MODE:
+      // Don't override a temporary zoom level without an explicit SET.
+      if (uses_temporary_zoom_level_)
+        return;
+      break;
+    default:
+      NOTIMPLEMENTED();
+  }
   webview()->hidePopups();
-  SetZoomLevel(level);
+  SetZoomLevel(zoom_level);
 }
 
 void RenderViewImpl::OnSetPageEncoding(const std::string& encoding_name) {
@@ -2584,7 +2612,6 @@
 
 void RenderViewImpl::OnSetRendererPrefs(
     const RendererPreferences& renderer_prefs) {
-  double old_zoom_level = renderer_preferences_.default_zoom_level;
   std::string old_accept_languages = renderer_preferences_.accept_languages;
 
   renderer_preferences_ = renderer_prefs;
@@ -2608,17 +2635,6 @@
   }
 #endif  // defined(USE_DEFAULT_RENDER_THEME)
 
-  // If the zoom level for this page matches the old zoom default, and this
-  // is not a plugin, update the zoom level to match the new default.
-  if (webview() && webview()->mainFrame()->isWebLocalFrame() &&
-      !webview()->mainFrame()->document().isPluginDocument() &&
-      !ZoomValuesEqual(old_zoom_level,
-                       renderer_preferences_.default_zoom_level) &&
-      ZoomValuesEqual(webview()->zoomLevel(), old_zoom_level)) {
-    SetZoomLevel(renderer_preferences_.default_zoom_level);
-    zoomLevelChanged();
-  }
-
   if (webview() &&
       old_accept_languages != renderer_preferences_.accept_languages) {
     webview()->acceptLanguagesChanged();
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 4c3d165..26ff553 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -32,6 +32,7 @@
 #include "content/common/frame_message_enums.h"
 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
 #include "content/common/navigation_gesture.h"
+#include "content/common/page_message_enums.h"
 #include "content/common/view_message_enums.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/referrer.h"
@@ -45,7 +46,6 @@
 #include "content/renderer/render_widget_owner_delegate.h"
 #include "content/renderer/stats_collection_observer.h"
 #include "ipc/ipc_platform_file.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/web/WebAXObject.h"
@@ -236,6 +236,10 @@
   // as that is only for changes that aren't initiated by the client.
   void SetZoomLevel(double zoom_level);
 
+  double page_zoom_level() {
+    return page_zoom_level_;
+  }
+
   // Indicates whether this page has been focused by the browser.
   bool has_focus() const { return has_focus_; }
 
@@ -685,7 +689,7 @@
   void OnSetRendererPrefs(const RendererPreferences& renderer_prefs);
   void OnSetWebUIProperty(const std::string& name, const std::string& value);
   void OnSetZoomLevelForLoadingURL(const GURL& url, double zoom_level);
-  void OnSetZoomLevelForView(bool uses_temporary_zoom_level, double level);
+  void OnSuppressDialogsUntilSwapOut();
   void OnThemeChanged();
   void OnUpdateTargetURLAck();
   void OnUpdateWebPreferences(const WebPreferences& prefs);
@@ -705,6 +709,7 @@
 
   // Page message handlers -----------------------------------------------------
   void OnUpdateWindowScreenRect(gfx::Rect window_screen_rect);
+  void OnSetZoomLevel(PageMsg_SetZoomLevel_Command command, double zoom_level);
 
   // Adding a new message handler? Please add it in alphabetical order above
   // and put it in the same position in the .cc file.
@@ -911,6 +916,10 @@
   bool has_scrolled_focused_editable_node_into_rect_;
   gfx::Rect rect_for_scrolled_focused_editable_node_;
 
+  // Used to indicate the zoom level to be used during subframe loads, since
+  // they should match page zoom level.
+  double page_zoom_level_;
+
   // Helper objects ------------------------------------------------------------
 
   RenderFrameImpl* main_render_frame_;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 3807127..72573db 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -68,7 +68,6 @@
 #include "ipc/ipc_sync_message.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/WebPoint.h"
 #include "third_party/WebKit/public/platform/WebRect.h"
 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
@@ -220,12 +219,11 @@
   attributes.bind_generates_resource = false;
   attributes.lose_context_when_out_of_memory = true;
 
-  bool share_resources = true;
   bool automatic_flushes = false;
 
   return base::WrapUnique(new content::WebGraphicsContext3DCommandBufferImpl(
       gpu::kNullSurfaceHandle, url, gpu_channel_host.get(), attributes,
-      gfx::PreferIntegratedGpu, share_resources, automatic_flushes, nullptr));
+      gfx::PreferIntegratedGpu, automatic_flushes));
 }
 
 }  // namespace
@@ -780,10 +778,6 @@
     limits.start_transfer_buffer_size = 64 * 1024;
     limits.min_transfer_buffer_size = 64 * 1024;
 
-    context_provider = new ContextProviderCommandBuffer(
-        CreateOffscreenContext(std::move(gpu_channel_host),
-                               GetURLForGraphicsContext3D()),
-        limits, RENDER_COMPOSITOR_CONTEXT);
     worker_context_provider =
         RenderThreadImpl::current()->SharedWorkerContextProvider();
     if (!worker_context_provider) {
@@ -791,6 +785,12 @@
       return nullptr;
     }
 
+    // The compositor context shares resources with the worker context.
+    context_provider = new ContextProviderCommandBuffer(
+        CreateOffscreenContext(std::move(gpu_channel_host),
+                               GetURLForGraphicsContext3D()),
+        limits, worker_context_provider.get(), RENDER_COMPOSITOR_CONTEXT);
+
 #if defined(OS_ANDROID)
     if (RenderThreadImpl::current() &&
         RenderThreadImpl::current()->sync_compositor_message_filter()) {
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index ac698ef36..6f3dc952 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -20,7 +20,6 @@
 #include "skia/ext/platform_canvas.h"
 #include "third_party/WebKit/public/platform/WebCanvas.h"
 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/WebLayer.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/web/WebWidget.h"
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index ac93eff..e8c18cd 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -20,7 +20,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "cc/blink/context_provider_web_context.h"
 #include "components/scheduler/child/web_scheduler_impl.h"
 #include "components/scheduler/child/web_task_runner_impl.h"
 #include "components/scheduler/renderer/renderer_scheduler.h"
@@ -1035,19 +1034,21 @@
     return nullptr;
   }
 
-  // WebGL contexts must fail creation if the share group is lost.
-  if (share_provider &&
-      share_provider->contextGL()->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
-    std::string error_message(
-        "OffscreenContext Creation failed, Shared context is lost");
-    gl_info->errorMessage = WebString::fromUTF8(error_message);
-    return nullptr;
-  }
+  content::WebGraphicsContext3DProviderImpl* share_provider_impl =
+      static_cast<content::WebGraphicsContext3DProviderImpl*>(share_provider);
+  ContextProviderCommandBuffer* share_context = nullptr;
 
-  WebGraphicsContext3DCommandBufferImpl* share_context =
-      share_provider ? static_cast<WebGraphicsContext3DCommandBufferImpl*>(
-                           share_provider->context3d())
-                     : nullptr;
+  // WebGL contexts must fail creation if the share group is lost.
+  if (share_provider_impl) {
+    auto* gl = share_provider_impl->contextGL();
+    if (gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+      std::string error_message(
+          "OffscreenContext Creation failed, Shared context is lost");
+      gl_info->errorMessage = WebString::fromUTF8(error_message);
+      return nullptr;
+    }
+    share_context = share_provider_impl->context_provider();
+  }
 
   // This is an offscreen context, which doesn't use the default frame buffer,
   // so don't request any alpha, depth, stencil, antialiasing.
@@ -1067,7 +1068,6 @@
   else if (web_attributes.webGLVersion == 2)
     attributes.context_type = gpu::gles2::CONTEXT_TYPE_WEBGL2;
 
-  bool share_resources = false;
   bool automatic_flushes = true;
   // Prefer discrete GPU for WebGL.
   gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
@@ -1077,8 +1077,9 @@
           base::WrapUnique(new WebGraphicsContext3DCommandBufferImpl(
               gpu::kNullSurfaceHandle, GURL(top_document_web_url),
               gpu_channel_host.get(), attributes, gpu_preference,
-              share_resources, automatic_flushes, share_context)),
-          gpu::SharedMemoryLimits(), RENDERER_MAINTHREAD_CONTEXT));
+              automatic_flushes)),
+          gpu::SharedMemoryLimits(), share_context,
+          RENDERER_MAINTHREAD_CONTEXT));
   if (!provider->BindToCurrentThread()) {
     // Collect Graphicsinfo if there is a context failure or it is failed
     // purposefully in case of layout tests.
@@ -1092,7 +1093,7 @@
 
 blink::WebGraphicsContext3DProvider*
 RendererBlinkPlatformImpl::createSharedOffscreenGraphicsContext3DProvider() {
-  scoped_refptr<cc_blink::ContextProviderWebContext> provider =
+  scoped_refptr<ContextProviderCommandBuffer> provider =
       RenderThreadImpl::current()->SharedMainThreadContextProvider();
   if (!provider)
     return nullptr;
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index e361e3b..b758d287 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -21,7 +21,6 @@
 #include "content/renderer/top_level_blame_context.h"
 #include "content/renderer/webpublicsuffixlist_impl.h"
 #include "device/vibration/vibration_manager.mojom.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBFactory.h"
 #include "third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationType.h"
 
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.cc b/content/renderer/webgraphicscontext3d_provider_impl.cc
index 16deb534..17f34bc 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.cc
+++ b/content/renderer/webgraphicscontext3d_provider_impl.cc
@@ -4,23 +4,18 @@
 
 #include "content/renderer/webgraphicscontext3d_provider_impl.h"
 
-#include "cc/blink/context_provider_web_context.h"
+#include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "third_party/WebKit/public/platform/functional/WebFunction.h"
 
 namespace content {
 
 WebGraphicsContext3DProviderImpl::WebGraphicsContext3DProviderImpl(
-    scoped_refptr<cc_blink::ContextProviderWebContext> provider)
-    : provider_(provider) {
-}
+    scoped_refptr<ContextProviderCommandBuffer> provider)
+    : provider_(std::move(provider)) {}
 
 WebGraphicsContext3DProviderImpl::~WebGraphicsContext3DProviderImpl() {}
 
-blink::WebGraphicsContext3D* WebGraphicsContext3DProviderImpl::context3d() {
-  return provider_->WebContext3D();
-}
-
 gpu::gles2::GLES2Interface* WebGraphicsContext3DProviderImpl::contextGL() {
   return provider_->ContextGL();
 }
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.h b/content/renderer/webgraphicscontext3d_provider_impl.h
index a17000f..b6358ae 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.h
+++ b/content/renderer/webgraphicscontext3d_provider_impl.h
@@ -10,10 +10,6 @@
 #include "content/common/content_export.h"
 #include "third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h"
 
-namespace cc_blink {
-class ContextProviderWebContext;
-}
-
 namespace gpu {
 namespace gles2 {
 class GLES2Interface;
@@ -21,16 +17,16 @@
 }
 
 namespace content {
+class ContextProviderCommandBuffer;
 
 class CONTENT_EXPORT WebGraphicsContext3DProviderImpl
     : public NON_EXPORTED_BASE(blink::WebGraphicsContext3DProvider) {
  public:
   explicit WebGraphicsContext3DProviderImpl(
-      scoped_refptr<cc_blink::ContextProviderWebContext> provider);
+      scoped_refptr<ContextProviderCommandBuffer> provider);
   ~WebGraphicsContext3DProviderImpl() override;
 
   // WebGraphicsContext3DProvider implementation.
-  blink::WebGraphicsContext3D* context3d() override;
   gpu::gles2::GLES2Interface* contextGL() override;
   GrContext* grContext() override;
   gpu::Capabilities getCapabilities() override;
@@ -38,8 +34,12 @@
   void setErrorMessageCallback(
       blink::WebFunction<void(const char*, int32_t)>) override;
 
+  ContextProviderCommandBuffer* context_provider() const {
+    return provider_.get();
+  }
+
  private:
-  scoped_refptr<cc_blink::ContextProviderWebContext> provider_;
+  scoped_refptr<ContextProviderCommandBuffer> provider_;
 };
 
 }  // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 20efd38..c3220ad 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -895,8 +895,8 @@
   }
 }
 
-if (is_android) {
-  # TODO(GYP): Port Windows and ChromeOS logic.
+if (is_android || is_chromeos) {
+  # TODO(GYP): Port Windows logic.
   test("video_decode_accelerator_unittest") {
     deps = [
       "//base",
@@ -911,6 +911,9 @@
       "//ui/gl:test_support",
     ]
     configs += [ "//third_party/khronos:khronos_headers" ]
+    if (is_chromeos && target_cpu != "arm") {
+      configs += [ "//third_party/libva:libva_config" ]
+    }
     sources = [
       "//content/common/gpu/media/video_accelerator_unittest_helpers.h",
     ]
@@ -934,5 +937,47 @@
         "//ui/android:ui_java",
       ]
     }
+    if (is_chromeos && use_ozone) {
+      deps += [
+        "//ui/display",  # Used by rendering_helper.cc
+        "//ui/ozone",  # Used by rendering_helper.cc
+      ]
+    }
+    if (use_x11) {
+      configs += [ "//build/config/linux:x11" ]
+      deps += [ "//ui/gfx/x" ]
+    }
+  }
+}
+
+if (is_chromeos || is_mac) {
+  test("video_encode_accelerator_unittest") {
+    deps = [
+      "//base",
+      "//content",
+      "//media",
+      "//media/base:test_support",
+      "//testing/gtest",
+      "//ui/base",
+      "//ui/gfx",
+      "//ui/gfx:test_support",
+      "//ui/gfx/geometry",
+      "//ui/gl",
+      "//ui/gl:test_support",
+    ]
+    configs += [
+      "//third_party/libva:libva_config",
+      "//third_party/libyuv:libyuv_config",
+    ]
+    sources = [
+      "//content/common/gpu/media/video_accelerator_unittest_helpers.h",
+      "//content/common/gpu/media/video_encode_accelerator_unittest.cc",
+    ]
+    if (use_x11) {
+      deps += [ "//ui/gfx/x" ]
+    }
+    if (use_ozone) {
+      deps += [ "//ui/ozone" ]
+    }
   }
 }
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 7aaf0e8..64a97aca 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -104,9 +104,6 @@
     self.Fail('conformance/textures/misc/tex-sub-image-2d-bad-args.html',
         bug=570453)
 
-    self.Fail('conformance/extensions/oes-texture-half-float.html',
-        bug=607283)
-
     # Fails on multiple platforms
 
     # OpenGL / NVIDIA failures
@@ -114,6 +111,8 @@
         ['win', 'linux', 'nvidia', 'opengl'], bug=1007) # angle bug ID
 
     # Win failures
+    self.Fail('conformance/extensions/oes-texture-half-float.html',
+        ['win'], bug=607283)
     # Note that the following two tests pass with OpenGL.
     self.Fail('conformance/glsl/bugs/' +
               'pow-of-small-constant-in-user-defined-function.html',
@@ -201,6 +200,10 @@
         ['mac', ('amd', 0x6821)], bug=599272)
 
     # Linux failures
+    # OpenGL
+    self.Fail('conformance/extensions/oes-texture-half-float.html',
+        ['linux', 'opengl'], bug=607283)
+
     # NVIDIA
     self.Fail('conformance/extensions/angle-instanced-arrays.html',
               ['linux', 'nvidia'], bug=544989) # Too flaky to retry
diff --git a/extensions/common/features/simple_feature_unittest.cc b/extensions/common/features/simple_feature_unittest.cc
index 098e2119..e74dc32 100644
--- a/extensions/common/features/simple_feature_unittest.cc
+++ b/extensions/common/features/simple_feature_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_command_line.h"
 #include "base/values.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/value_builder.h"
@@ -29,21 +30,6 @@
   Feature::AvailabilityResult expected_result;
 };
 
-class ScopedCommandLineSwitch {
- public:
-  explicit ScopedCommandLineSwitch(const std::string& arg)
-      : original_command_line_(*base::CommandLine::ForCurrentProcess()) {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(arg);
-  }
-
-  ~ScopedCommandLineSwitch() {
-    *base::CommandLine::ForCurrentProcess() = original_command_line_;
-  }
-
- private:
-  base::CommandLine original_command_line_;
-};
-
 }  // namespace
 
 class SimpleFeatureTest : public testing::Test {
@@ -704,27 +690,34 @@
               feature.IsAvailableToEnvironment().result());
   }
   {
-    ScopedCommandLineSwitch scoped_switch("laser-beams");
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch("laser-beams");
     EXPECT_EQ(Feature::MISSING_COMMAND_LINE_SWITCH,
               feature.IsAvailableToEnvironment().result());
   }
   {
-    ScopedCommandLineSwitch scoped_switch("enable-laser-beams");
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch(
+        "enable-laser-beams");
     EXPECT_EQ(Feature::IS_AVAILABLE,
               feature.IsAvailableToEnvironment().result());
   }
   {
-    ScopedCommandLineSwitch scoped_switch("disable-laser-beams");
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch(
+        "disable-laser-beams");
     EXPECT_EQ(Feature::MISSING_COMMAND_LINE_SWITCH,
               feature.IsAvailableToEnvironment().result());
   }
   {
-    ScopedCommandLineSwitch scoped_switch("laser-beams=1");
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch("laser-beams=1");
     EXPECT_EQ(Feature::IS_AVAILABLE,
               feature.IsAvailableToEnvironment().result());
   }
   {
-    ScopedCommandLineSwitch scoped_switch("laser-beams=0");
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch("laser-beams=0");
     EXPECT_EQ(Feature::MISSING_COMMAND_LINE_SWITCH,
               feature.IsAvailableToEnvironment().result());
   }
diff --git a/extensions/shell/browser/shell_desktop_controller_mac.mm b/extensions/shell/browser/shell_desktop_controller_mac.mm
index 7731b0a..29dd0143 100644
--- a/extensions/shell/browser/shell_desktop_controller_mac.mm
+++ b/extensions/shell/browser/shell_desktop_controller_mac.mm
@@ -7,9 +7,9 @@
 #include "extensions/browser/app_window/native_app_window.h"
 #include "extensions/shell/browser/shell_app_delegate.h"
 #include "extensions/shell/browser/shell_app_window_client.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gfx/screen.h"
 
 namespace extensions {
 
@@ -26,7 +26,7 @@
 
 gfx::Size ShellDesktopControllerMac::GetWindowSize() {
   // This is the full screen size.
-  return gfx::Screen::GetScreen()->GetPrimaryDisplay().bounds().size();
+  return display::Screen::GetScreen()->GetPrimaryDisplay().bounds().size();
 }
 
 AppWindow* ShellDesktopControllerMac::CreateAppWindow(
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 84f8ef73..ca183fe2 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -111,7 +111,7 @@
 
 GLES2Implementation::GLES2Implementation(
     GLES2CmdHelper* helper,
-    ShareGroup* share_group,
+    scoped_refptr<ShareGroup> share_group,
     TransferBufferInterface* transfer_buffer,
     bool bind_generates_resource,
     bool lose_context_when_out_of_memory,
@@ -181,7 +181,7 @@
   });
 
   share_group_ =
-      (share_group ? share_group
+      (share_group ? std::move(share_group)
                    : new ShareGroup(
                          bind_generates_resource,
                          gpu_control_->GetCommandBufferID().GetUnsafeValue()));
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index 489bf7d..5c7261b 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -160,7 +160,7 @@
   static const size_t kMaxSwapBuffers = 2;
 
   GLES2Implementation(GLES2CmdHelper* helper,
-                      ShareGroup* share_group,
+                      scoped_refptr<ShareGroup> share_group,
                       TransferBufferInterface* transfer_buffer,
                       bool bind_generates_resource,
                       bool lose_context_when_out_of_memory,
@@ -262,9 +262,7 @@
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                     base::trace_event::ProcessMemoryDump* pmd) override;
 
-  ShareGroup* share_group() const {
-    return share_group_.get();
-  }
+  const scoped_refptr<ShareGroup>& share_group() const { return share_group_; }
 
   const Capabilities& capabilities() const {
     return capabilities_;
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index 8eeaf24..9f3cbb25 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -1067,30 +1067,16 @@
     case GL_RG32F:
     case GL_RGB32F:
     case GL_RGBA32F:
-      return GL_FLOAT;
+    case GL_R11F_G11F_B10F:
+      return GL_UNSIGNED_BYTE;
     case GL_R16F:
     case GL_RG16F:
-    case GL_R11F_G11F_B10F:
     case GL_RGB16F:
     case GL_RGBA16F:
-      // TODO(zmo): Consider return GL_UNSIGNED_INT_10F_11F_11F_REV and
-      // GL_HALF_FLOAT.
-      return GL_FLOAT;
-    case GL_RGBA:
-    case GL_RGB:
-      // Unsized internal format, check the type
-      switch (texture_type) {
-        case GL_FLOAT:
-        case GL_HALF_FLOAT_OES:
-          return GL_FLOAT;
-        // TODO(zmo): Consider return GL_UNSIGNED_SHORT_5_6_5,
-        // GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1, and
-        // GL_UNSIGNED_INT_2_10_10_10_REV.
-        default:
-          return GL_UNSIGNED_BYTE;
-      }
+      return GL_HALF_FLOAT;
     default:
-      return GL_UNSIGNED_BYTE;
+      // Unsized internal format.
+      return texture_type;
   }
 }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index e8b9c308..fd688cf 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1284,15 +1284,18 @@
   bool FormsTextureCopyingFeedbackLoop(TextureRef* texture, GLint level);
 
   // Check if a framebuffer meets our requirements.
+  // Generates |gl_error| if the framebuffer is incomplete.
   bool CheckFramebufferValid(
       Framebuffer* framebuffer,
       GLenum target,
       bool clear_uncleared_images,
+      GLenum gl_error,
       const char* func_name);
 
   bool CheckBoundDrawFramebufferValid(
       bool clear_uncleared_images, const char* func_name);
-  bool CheckBoundReadFramebufferValid(const char* func_name);
+  // Generates |gl_error| if the bound read fbo is incomplete.
+  bool CheckBoundReadFramebufferValid(const char* func_name, GLenum gl_error);
 
   // Checks if the current program exists and is valid. If not generates the
   // appropriate GL error.  Returns true if the current program is in a usable
@@ -3753,6 +3756,7 @@
     Framebuffer* framebuffer,
     GLenum target,
     bool clear_uncleared_images,
+    GLenum gl_error,
     const char* func_name) {
   if (!framebuffer) {
     if (surfaceless_)
@@ -3792,8 +3796,7 @@
 
   GLenum completeness = framebuffer->IsPossiblyComplete(feature_info_.get());
   if (completeness != GL_FRAMEBUFFER_COMPLETE) {
-    LOCAL_SET_GL_ERROR(
-        GL_INVALID_FRAMEBUFFER_OPERATION, func_name, "framebuffer incomplete");
+    LOCAL_SET_GL_ERROR(gl_error, func_name, "framebuffer incomplete");
     return false;
   }
 
@@ -3806,8 +3809,7 @@
       if (framebuffer->GetStatus(texture_manager(), target) !=
           GL_FRAMEBUFFER_COMPLETE) {
         LOCAL_SET_GL_ERROR(
-            GL_INVALID_FRAMEBUFFER_OPERATION, func_name,
-            "framebuffer incomplete (clear)");
+            gl_error, func_name, "framebuffer incomplete (clear)");
         return false;
       }
       ClearUnclearedAttachments(target, framebuffer);
@@ -3818,8 +3820,7 @@
     if (framebuffer->GetStatus(texture_manager(), target) !=
         GL_FRAMEBUFFER_COMPLETE) {
       LOCAL_SET_GL_ERROR(
-          GL_INVALID_FRAMEBUFFER_OPERATION, func_name,
-          "framebuffer incomplete (check)");
+          gl_error, func_name, "framebuffer incomplete (check)");
       return false;
     }
     framebuffer_manager()->MarkAsComplete(framebuffer);
@@ -3833,17 +3834,20 @@
       GL_DRAW_FRAMEBUFFER : GL_FRAMEBUFFER;
   Framebuffer* framebuffer = GetFramebufferInfoForTarget(target);
   bool valid = CheckFramebufferValid(
-      framebuffer, target, clear_uncleared_images, func_name);
+      framebuffer, target, clear_uncleared_images,
+      GL_INVALID_FRAMEBUFFER_OPERATION, func_name);
   if (valid && !features().chromium_framebuffer_multisample)
     OnUseFramebuffer();
   return valid;
 }
 
-bool GLES2DecoderImpl::CheckBoundReadFramebufferValid(const char* func_name) {
+bool GLES2DecoderImpl::CheckBoundReadFramebufferValid(
+    const char* func_name, GLenum gl_error) {
   GLenum target = features().chromium_framebuffer_multisample ?
       GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER;
   Framebuffer* framebuffer = GetFramebufferInfoForTarget(target);
-  bool valid = CheckFramebufferValid(framebuffer, target, true, func_name);
+  bool valid = CheckFramebufferValid(
+      framebuffer, target, true, gl_error, func_name);
   return valid;
 }
 
@@ -5369,50 +5373,52 @@
 bool GLES2DecoderImpl::GetHelper(
     GLenum pname, GLint* params, GLsizei* num_written) {
   DCHECK(num_written);
+  switch (pname) {
+    case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
+    case GL_IMPLEMENTATION_COLOR_READ_TYPE:
+      // They are not supported on Desktop GL until 4.1, but could be exposed
+      // through GL_OES_read_format extension. However, a conflicting extension
+      // GL_ARB_ES2_compatibility specifies an error case when requested on
+      // integer/floating point buffers.
+      // To simpify the handling, we just query and check for GL errors. If an
+      // GL error occur, we fall back to our internal implementation.
+      *num_written = 1;
+      if (!CheckBoundReadFramebufferValid("glGetIntegerv",
+                                          GL_INVALID_OPERATION)) {
+        if (params) {
+          *params = 0;
+        }
+        return true;
+      }
+      if (params) {
+        ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::GetHelper",
+                                           GetErrorState());
+        glGetIntegerv(pname, params);
+        if (glGetError() != GL_NO_ERROR) {
+          if (pname == GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
+            *params = GLES2Util::GetGLReadPixelsImplementationFormat(
+                GetBoundReadFrameBufferInternalFormat(),
+                GetBoundReadFrameBufferTextureType(),
+                feature_info_->feature_flags().ext_read_format_bgra);
+          } else {
+            *params = GLES2Util::GetGLReadPixelsImplementationType(
+                GetBoundReadFrameBufferInternalFormat(),
+                GetBoundReadFrameBufferTextureType());
+          }
+        }
+        if (*params == GL_HALF_FLOAT &&
+            (feature_info_->context_type() == CONTEXT_TYPE_WEBGL1 ||
+             feature_info_->context_type() == CONTEXT_TYPE_OPENGLES2)) {
+          *params = GL_HALF_FLOAT_OES;
+        }
+      }
+      return true;
+    default:
+      break;
+  }
+
   if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
     switch (pname) {
-      case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
-        *num_written = 1;
-        // Return the GL implementation's preferred format and (see below type)
-        // if we have the GL extension that exposes this. This allows the GPU
-        // client to use the implementation's preferred format for glReadPixels
-        // for optimisation.
-        //
-        // A conflicting extension (GL_ARB_ES2_compatibility) specifies an error
-        // case when requested on integer/floating point buffers but which is
-        // acceptable on GLES2 and with the GL_OES_read_format extension.
-        //
-        // Therefore if an error occurs we swallow the error and use the
-        // internal implementation.
-        if (params) {
-          if (context_->HasExtension("GL_OES_read_format")) {
-            ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::GetHelper",
-                                               GetErrorState());
-            glGetIntegerv(pname, params);
-            if (glGetError() == GL_NO_ERROR)
-              return true;
-          }
-          *params = GLES2Util::GetGLReadPixelsImplementationFormat(
-              GetBoundReadFrameBufferInternalFormat(),
-              GetBoundReadFrameBufferTextureType(),
-              feature_info_->feature_flags().ext_read_format_bgra);
-        }
-        return true;
-      case GL_IMPLEMENTATION_COLOR_READ_TYPE:
-        *num_written = 1;
-        if (params) {
-          if (context_->HasExtension("GL_OES_read_format")) {
-            ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::GetHelper",
-                                               GetErrorState());
-            glGetIntegerv(pname, params);
-            if (glGetError() == GL_NO_ERROR)
-              return true;
-          }
-          *params = GLES2Util::GetGLReadPixelsImplementationType(
-              GetBoundReadFrameBufferInternalFormat(),
-              GetBoundReadFrameBufferTextureType());
-        }
-        return true;
       case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
         *num_written = 1;
         if (params) {
@@ -6788,7 +6794,8 @@
   DCHECK(!ShouldDeferReads() && !ShouldDeferDraws());
 
   if (!CheckBoundDrawFramebufferValid(true, "glBlitFramebufferCHROMIUM") ||
-      !CheckBoundReadFramebufferValid("glBlitFramebufferCHROMIUM")) {
+      !CheckBoundReadFramebufferValid("glBlitFramebufferCHROMIUM",
+                                      GL_INVALID_FRAMEBUFFER_OPERATION)) {
     return;
   }
 
@@ -9578,7 +9585,8 @@
     return error::kNoError;
   }
 
-  if (!CheckBoundReadFramebufferValid("glReadPixels")) {
+  if (!CheckBoundReadFramebufferValid("glReadPixels",
+                                      GL_INVALID_FRAMEBUFFER_OPERATION)) {
     return error::kNoError;
   }
   GLenum src_internal_format = GetBoundReadFrameBufferInternalFormat();
@@ -9632,11 +9640,7 @@
           case GL_HALF_FLOAT_OES:
           case GL_FLOAT:
           case GL_UNSIGNED_INT_10F_11F_11F_REV:
-            if (!feature_info_->IsES3Enabled()) {
-              accepted_types.push_back(GL_UNSIGNED_BYTE);
-            } else {
-              accepted_types.push_back(GL_FLOAT);
-            }
+            accepted_types.push_back(GL_FLOAT);
             break;
           default:
             accepted_types.push_back(GL_UNSIGNED_BYTE);
@@ -9674,6 +9678,10 @@
         "format and type incompatible with the current read framebuffer");
     return error::kNoError;
   }
+  if (type == GL_HALF_FLOAT_OES &&
+      !(feature_info_->gl_version_info().is_es2)) {
+    type = GL_HALF_FLOAT;
+  }
   if (width == 0 || height == 0) {
     return error::kNoError;
   }
@@ -11794,7 +11802,8 @@
     return;
   }
 
-  if (!CheckBoundReadFramebufferValid("glCopyTexImage2D")) {
+  if (!CheckBoundReadFramebufferValid("glCopyTexImage2D",
+                                      GL_INVALID_FRAMEBUFFER_OPERATION)) {
     return;
   }
 
@@ -11993,7 +12002,8 @@
     return;
   }
 
-  if (!CheckBoundReadFramebufferValid("glCopyTexImage2D")) {
+  if (!CheckBoundReadFramebufferValid("glCopyTexImage2D",
+                                      GL_INVALID_FRAMEBUFFER_OPERATION)) {
     return;
   }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
index 258d63d4..09ae7a3e 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -3198,47 +3198,37 @@
   EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));
 }
 
-TEST_P(GLES2DecoderManualInitTest, ReadFormatExtension) {
-  InitState init;
-  init.extensions = "GL_OES_read_format";
-  init.bind_generates_resource = true;
-  InitDecoder(init);
-
-  EXPECT_CALL(*gl_, GetError())
-      .WillOnce(Return(GL_NO_ERROR))
-      .WillOnce(Return(GL_NO_ERROR))
-      .WillOnce(Return(GL_NO_ERROR))
-      .WillOnce(Return(GL_NO_ERROR))
-      .RetiresOnSaturation();
-  EXPECT_CALL(*gl_, GetError()).Times(6).RetiresOnSaturation();
-
-  typedef GetIntegerv::Result Result;
-  Result* result = static_cast<Result*>(shared_memory_address_);
-  GetIntegerv cmd;
-  const GLuint kFBOClientTextureId = 4100;
-  const GLuint kFBOServiceTextureId = 4101;
-
-  // Register a texture id.
-  EXPECT_CALL(*gl_, GenTextures(_, _))
-      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
-      .RetiresOnSaturation();
-  GenHelper<GenTexturesImmediate>(kFBOClientTextureId);
-
-  // Setup "render to" texture.
-  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
+TEST_P(GLES2DecoderTest, ImplementationReadColorFormatAndType) {
+  ClearSharedMemory();
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   DoTexImage2D(
-      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
+      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+      kSharedMemoryId, kSharedMemoryOffset);
   DoBindFramebuffer(
       GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
   DoFramebufferTexture2D(GL_FRAMEBUFFER,
                          GL_COLOR_ATTACHMENT0,
                          GL_TEXTURE_2D,
-                         kFBOClientTextureId,
-                         kFBOServiceTextureId,
+                         client_texture_id_,
+                         kServiceTextureId,
                          0,
                          GL_NO_ERROR);
 
+  typedef GetIntegerv::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  GetIntegerv cmd;
+
   result->size = 0;
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(_))
+      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
+      .RetiresOnSaturation();
   EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation();
   cmd.Init(GL_IMPLEMENTATION_COLOR_READ_FORMAT,
            shared_memory_id_,
@@ -3248,64 +3238,16 @@
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 
   result->size = 0;
-  EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation();
-  cmd.Init(GL_IMPLEMENTATION_COLOR_READ_TYPE,
-           shared_memory_id_,
-           shared_memory_offset_);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(1, result->GetNumResults());
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
-}
-
-TEST_P(GLES2DecoderManualInitTest, NoReadFormatExtension) {
-  InitState init;
-  init.bind_generates_resource = true;
-  InitDecoder(init);
-
   EXPECT_CALL(*gl_, GetError())
       .WillOnce(Return(GL_NO_ERROR))
       .WillOnce(Return(GL_NO_ERROR))
       .WillOnce(Return(GL_NO_ERROR))
       .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
       .RetiresOnSaturation();
-
-  typedef GetIntegerv::Result Result;
-  Result* result = static_cast<Result*>(shared_memory_address_);
-  GetIntegerv cmd;
-  const GLuint kFBOClientTextureId = 4100;
-  const GLuint kFBOServiceTextureId = 4101;
-
-  // Register a texture id.
-  EXPECT_CALL(*gl_, GenTextures(_, _))
-      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
-      .RetiresOnSaturation();
-  GenHelper<GenTexturesImmediate>(kFBOClientTextureId);
-
-  // Setup "render to" texture.
-  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
-  DoTexImage2D(
-      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
-  DoBindFramebuffer(
-      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
-  DoFramebufferTexture2D(GL_FRAMEBUFFER,
-                         GL_COLOR_ATTACHMENT0,
-                         GL_TEXTURE_2D,
-                         kFBOClientTextureId,
-                         kFBOServiceTextureId,
-                         0,
-                         GL_NO_ERROR);
-
-  result->size = 0;
-  EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(0).RetiresOnSaturation();
-  cmd.Init(GL_IMPLEMENTATION_COLOR_READ_FORMAT,
-           shared_memory_id_,
-           shared_memory_offset_);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(1, result->GetNumResults());
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
-
-  result->size = 0;
-  EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(0).RetiresOnSaturation();
+  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(_))
+      .Times(0);
+  EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation();
   cmd.Init(GL_IMPLEMENTATION_COLOR_READ_TYPE,
            shared_memory_id_,
            shared_memory_offset_);
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index f11ea71f..4b8a498 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -259,7 +259,7 @@
   }
 
   gles2::ContextGroup* context_group = NULL;
-  gles2::ShareGroup* client_share_group = NULL;
+  scoped_refptr<gles2::ShareGroup> client_share_group;
   if (options.share_group_manager) {
     context_group = options.share_group_manager->decoder_->GetContextGroup();
     client_share_group =
@@ -373,14 +373,11 @@
 
   // Create the object exposing the OpenGL API.
   const bool support_client_side_arrays = true;
-  gles2_implementation_.reset(
-      new gles2::GLES2Implementation(gles2_helper_.get(),
-                                     client_share_group,
-                                     transfer_buffer_.get(),
-                                     options.bind_generates_resource,
-                                     options.lose_context_when_out_of_memory,
-                                     support_client_side_arrays,
-                                     this));
+  gles2_implementation_.reset(new gles2::GLES2Implementation(
+      gles2_helper_.get(), std::move(client_share_group),
+      transfer_buffer_.get(), options.bind_generates_resource,
+      options.lose_context_when_out_of_memory, support_client_side_arrays,
+      this));
 
   ASSERT_TRUE(gles2_implementation_->Initialize(
       kStartTransferBufferSize, kMinTransferBufferSize, kMaxTransferBufferSize,
diff --git a/gpu/command_buffer/tests/gl_readback_unittest.cc b/gpu/command_buffer/tests/gl_readback_unittest.cc
index c9038dc..0c728e42 100644
--- a/gpu/command_buffer/tests/gl_readback_unittest.cc
+++ b/gpu/command_buffer/tests/gl_readback_unittest.cc
@@ -141,7 +141,16 @@
   return shader;
 }
 
-TEST_F(GLReadbackTest, ReadPixelsFloat) {
+// TODO(zmo): ReadPixels with float type isn't implemented in ANGLE ES2
+// backend. crbug.com/607283.
+// TODO(zmo): This test also fails on some android devices when the readback
+// type is HALF_FLOAT_OES. Likely it's due to a driver bug. crbug.com/607936.
+#if defined(OS_WIN) || defined(OS_ANDROID)
+#define MAYBE_ReadPixelsFloat DISABLED_ReadPixelsFloat
+#else
+#define MAYBE_ReadPixelsFloat ReadPixelsFloat
+#endif
+TEST_F(GLReadbackTest, MAYBE_ReadPixelsFloat) {
   const GLsizei kTextureSize = 4;
   const GLfloat kDrawColor[4] = { -10.9f, 0.5f, 10.5f, 100.12f };
   const GLfloat kEpsilon = 0.01f;
diff --git a/gpu/ipc/client/gpu_context_tests.h b/gpu/ipc/client/gpu_context_tests.h
index 9687c4a0..cced50c 100644
--- a/gpu/ipc/client/gpu_context_tests.h
+++ b/gpu/ipc/client/gpu_context_tests.h
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 // These tests are run twice:
-// Once in a gpu test with an in-process WebGraphicsContext3D.
-// Once in a browsertest with a gpu-process WebGraphicsContext3D.
+// Once in a gpu test with an in-process command buffer.
+// Once in a browsertest with an out-of-process command buffer and gpu-process.
 
 #include "base/bind.h"
 #include "base/run_loop.h"
diff --git a/gpu/ipc/service/gpu_channel_manager_delegate.h b/gpu/ipc/service/gpu_channel_manager_delegate.h
index e31dd1a..7cd320e 100644
--- a/gpu/ipc/service/gpu_channel_manager_delegate.h
+++ b/gpu/ipc/service/gpu_channel_manager_delegate.h
@@ -57,6 +57,8 @@
   virtual void SendAcceleratedSurfaceBuffersSwapped(
       gpu::SurfaceHandle surface_handle,
       CAContextID ca_context_id,
+      bool fullscreen_low_power_ca_context_valid,
+      CAContextID fullscreen_low_power_ca_context_id,
       const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
       const gfx::Size& size,
       float scale_factor,
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index 5ddb4836..48660fdc 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -45,6 +45,8 @@
 void TestGpuChannelManagerDelegate::SendAcceleratedSurfaceBuffersSwapped(
     gpu::SurfaceHandle surface_handle,
     CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
     const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
     const gfx::Size& size,
     float scale_factor,
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h
index 5df0ca8..dd400b10 100644
--- a/gpu/ipc/service/gpu_channel_test_common.h
+++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -49,6 +49,8 @@
   void SendAcceleratedSurfaceBuffersSwapped(
       gpu::SurfaceHandle surface_handle,
       CAContextID ca_context_id,
+      bool fullscreen_low_power_ca_context_valid,
+      CAContextID fullscreen_low_power_ca_context_id,
       const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
       const gfx::Size& size,
       float scale_factor,
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.h b/gpu/ipc/service/image_transport_surface_overlay_mac.h
index 7357aed2..78886dd 100644
--- a/gpu/ipc/service/image_transport_surface_overlay_mac.h
+++ b/gpu/ipc/service/image_transport_surface_overlay_mac.h
@@ -78,6 +78,8 @@
   void SendAcceleratedSurfaceBuffersSwapped(
       gpu::SurfaceHandle surface_handle,
       CAContextID ca_context_id,
+      bool fullscreen_low_power_ca_context_valid,
+      CAContextID fullscreen_low_power_ca_context_id,
       const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
       const gfx::Size& size,
       float scale_factor,
@@ -91,6 +93,7 @@
 
   bool use_remote_layer_api_;
   base::scoped_nsobject<CAContext> ca_context_;
+  base::scoped_nsobject<CAContext> fullscreen_low_power_ca_context_;
   std::unique_ptr<ui::CALayerTreeCoordinator> ca_layer_tree_coordinator_;
 
   gfx::Size pixel_size_;
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
index 207e24b..b75f200 100644
--- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm
+++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
@@ -107,6 +107,11 @@
     ca_context_.reset([
         [CAContext contextWithCGSConnection:connection_id options:@{}] retain]);
     [ca_context_ setLayer:ca_layer_tree_coordinator_->GetCALayerForDisplay()];
+
+    fullscreen_low_power_ca_context_.reset([
+        [CAContext contextWithCGSConnection:connection_id options:@{}] retain]);
+    [fullscreen_low_power_ca_context_ setLayer:
+        ca_layer_tree_coordinator_->GetFullscreenLowPowerLayerForDisplay()];
   }
   return true;
 }
@@ -144,6 +149,8 @@
 void ImageTransportSurfaceOverlayMac::SendAcceleratedSurfaceBuffersSwapped(
     gpu::SurfaceHandle surface_handle,
     CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
     const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface,
     const gfx::Size& size,
     float scale_factor,
@@ -153,7 +160,8 @@
                        "GLImpl", static_cast<int>(gfx::GetGLImplementation()),
                        "width", size.width());
   manager_->delegate()->SendAcceleratedSurfaceBuffersSwapped(
-      surface_handle, ca_context_id, io_surface, size, scale_factor,
+      surface_handle, ca_context_id, fullscreen_low_power_ca_context_valid,
+      fullscreen_low_power_ca_context_id, io_surface, size, scale_factor,
       std::move(latency_info));
 }
 
@@ -191,18 +199,22 @@
 
   // Send acknowledgement to the browser.
   CAContextID ca_context_id = 0;
+  CAContextID fullscreen_low_power_ca_context_id = 0;
   gfx::ScopedRefCountedIOSurfaceMachPort io_surface_mach_port;
   if (use_remote_layer_api_) {
     ca_context_id = [ca_context_ contextId];
+    fullscreen_low_power_ca_context_id =
+        [fullscreen_low_power_ca_context_ contextId];
   } else {
     IOSurfaceRef io_surface =
         ca_layer_tree_coordinator_->GetIOSurfaceForDisplay();
     if (io_surface)
       io_surface_mach_port.reset(IOSurfaceCreateMachPort(io_surface));
   }
-  SendAcceleratedSurfaceBuffersSwapped(handle_, ca_context_id,
-                                       io_surface_mach_port, pixel_size_,
-                                       scale_factor_, std::move(latency_info_));
+  SendAcceleratedSurfaceBuffersSwapped(
+      handle_, ca_context_id, fullscreen_low_power_layer_valid,
+      fullscreen_low_power_ca_context_id, io_surface_mach_port, pixel_size_,
+      scale_factor_, std::move(latency_info_));
 
   // Reset all state for the next frame.
   latency_info_.clear();
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
index 26b0cf9..e8c9434 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
@@ -416,11 +416,23 @@
   AppRequestContext* context = new AppRequestContext();
   context->CopyFrom(main_context);
 
+  // Use a separate ChannelIDService.
+  std::unique_ptr<net::ChannelIDService> channel_id_service(
+      new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr),
+                                base::WorkerPool::GetTaskRunner(true)));
+
+  // Build a new HttpNetworkSession that uses the new ChannelIDService.
+  net::HttpNetworkSession::Params network_params =
+      http_network_session_->params();
+  network_params.channel_id_service = channel_id_service.get();
+  std::unique_ptr<net::HttpNetworkSession> http_network_session(
+      new net::HttpNetworkSession(network_params));
+
   // Use a separate HTTP disk cache for isolated apps.
   std::unique_ptr<net::HttpCache::BackendFactory> app_backend =
       net::HttpCache::DefaultBackend::InMemory(0);
   std::unique_ptr<net::HttpCache> app_http_cache =
-      CreateHttpFactory(http_network_session_.get(), std::move(app_backend));
+      CreateHttpFactory(http_network_session.get(), std::move(app_backend));
 
   cookie_util::CookieStoreConfig ios_cookie_config(
       base::FilePath(),
@@ -429,7 +441,10 @@
   std::unique_ptr<net::CookieStore> cookie_store =
       cookie_util::CreateCookieStore(ios_cookie_config);
 
-  // Transfer ownership of the cookies and cache to AppRequestContext.
+  // Transfer ownership of the ChannelIDStore, HttpNetworkSession, cookies, and
+  // cache to AppRequestContext.
+  context->SetChannelIDService(std::move(channel_id_service));
+  context->SetHttpNetworkSession(std::move(http_network_session));
   context->SetCookieStore(std::move(cookie_store));
   context->SetHttpTransactionFactory(std::move(app_http_cache));
 
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc
index ed24da0..e4e6e9f 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc
@@ -140,6 +140,17 @@
   set_cookie_store(cookie_store_.get());
 }
 
+void ChromeBrowserStateIOData::AppRequestContext::SetChannelIDService(
+    std::unique_ptr<net::ChannelIDService> channel_id_service) {
+  channel_id_service_ = std::move(channel_id_service);
+  set_channel_id_service(channel_id_service_.get());
+}
+
+void ChromeBrowserStateIOData::AppRequestContext::SetHttpNetworkSession(
+    std::unique_ptr<net::HttpNetworkSession> http_network_session) {
+  http_network_session_ = std::move(http_network_session);
+}
+
 void ChromeBrowserStateIOData::AppRequestContext::SetHttpTransactionFactory(
     std::unique_ptr<net::HttpTransactionFactory> http_factory) {
   http_factory_ = std::move(http_factory);
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
index dfa3dd1e..c9786a8 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
@@ -134,6 +134,10 @@
     AppRequestContext();
 
     void SetCookieStore(std::unique_ptr<net::CookieStore> cookie_store);
+    void SetChannelIDService(
+        std::unique_ptr<net::ChannelIDService> channel_id_service);
+    void SetHttpNetworkSession(
+        std::unique_ptr<net::HttpNetworkSession> http_network_session);
     void SetHttpTransactionFactory(
         std::unique_ptr<net::HttpTransactionFactory> http_factory);
     void SetJobFactory(std::unique_ptr<net::URLRequestJobFactory> job_factory);
@@ -142,6 +146,8 @@
 
    private:
     std::unique_ptr<net::CookieStore> cookie_store_;
+    std::unique_ptr<net::ChannelIDService> channel_id_service_;
+    std::unique_ptr<net::HttpNetworkSession> http_network_session_;
     std::unique_ptr<net::HttpTransactionFactory> http_factory_;
     std::unique_ptr<net::URLRequestJobFactory> job_factory_;
   };
diff --git a/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm b/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
index d283a95..b689c59 100644
--- a/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
+++ b/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
@@ -40,7 +40,8 @@
 
 // Rappor prefix, which is used for both overridable and non-overridable
 // interstitials so we don't leak the "overridable" bit.
-const char kSSLRapporPrefix[] = "ssl2";
+const char kDeprecatedSSLRapporPrefix[] = "ssl2";
+const char kSSLRapporPrefix[] = "ssl3";
 
 void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed,
                                        bool proceed,
@@ -95,7 +96,9 @@
   reporting_info.metric_prefix =
       overridable_ ? "ssl_overridable" : "ssl_nonoverridable";
   reporting_info.rappor_prefix = kSSLRapporPrefix;
-  reporting_info.rappor_report_type = rappor::UMA_RAPPOR_TYPE;
+  reporting_info.deprecated_rappor_prefix = kDeprecatedSSLRapporPrefix;
+  reporting_info.rappor_report_type = rappor::LOW_FREQUENCY_UMA_RAPPOR_TYPE;
+  reporting_info.deprecated_rappor_report_type = rappor::UMA_RAPPOR_TYPE;
   IOSChromeMetricsHelper* ios_chrome_metrics_helper =
       new IOSChromeMetricsHelper(web_state, request_url, reporting_info);
   controller_->set_metrics_helper(base::WrapUnique(ios_chrome_metrics_helper));
diff --git a/ios/third_party/earl_grey/BUILD.gn b/ios/third_party/earl_grey/BUILD.gn
index ec2b125..2f27a4e 100644
--- a/ios/third_party/earl_grey/BUILD.gn
+++ b/ios/third_party/earl_grey/BUILD.gn
@@ -253,4 +253,5 @@
   public_configs = [ ":config" ]
   configs += [ "//build/config/compiler:enable_arc" ]
   configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+  configs += [ "//build/config/gcc:symbol_visibility_default" ]
 }
diff --git a/ios/third_party/ochamcrest/BUILD.gn b/ios/third_party/ochamcrest/BUILD.gn
index 754f4ff..4994658 100644
--- a/ios/third_party/ochamcrest/BUILD.gn
+++ b/ios/third_party/ochamcrest/BUILD.gn
@@ -230,4 +230,5 @@
 
   configs += [ "//build/config/compiler:enable_arc" ]
   configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+  configs += [ "//build/config/gcc:symbol_visibility_default" ]
 }
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 81d6a05..f044e915 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -169,7 +169,6 @@
     "web_state/crw_web_view_scroll_view_proxy.mm",
     "web_state/error_translation_util.h",
     "web_state/error_translation_util.mm",
-    "web_state/frame_info.h",
     "web_state/global_web_state_event_tracker.h",
     "web_state/global_web_state_event_tracker.mm",
     "web_state/global_web_state_observer.cc",
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index e6451c2b..268c64ee 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -205,7 +205,6 @@
         'web_state/crw_web_view_scroll_view_proxy.mm',
         'web_state/error_translation_util.h',
         'web_state/error_translation_util.mm',
-        'web_state/frame_info.h',
         'web_state/global_web_state_event_tracker.h',
         'web_state/global_web_state_event_tracker.mm',
         'web_state/global_web_state_observer.cc',
diff --git a/ios/web/public/web_state/ui/crw_web_delegate.h b/ios/web/public/web_state/ui/crw_web_delegate.h
index 65c7fbd..d29c6f6 100644
--- a/ios/web/public/web_state/ui/crw_web_delegate.h
+++ b/ios/web/public/web_state/ui/crw_web_delegate.h
@@ -218,10 +218,6 @@
 - (void)webControllerDidUpdateSSLStatusForCurrentNavigationItem:
     (CRWWebController*)webController;
 
-// Called when CRWWebController did update page load progress.
-- (void)webController:(CRWWebController*)webController
-    didUpdateProgress:(CGFloat)progress;
-
 // Called when web view process has been terminated.
 - (void)webControllerWebProcessDidCrash:(CRWWebController*)webController;
 
diff --git a/ios/web/web_state/frame_info.h b/ios/web/web_state/frame_info.h
deleted file mode 100644
index 825f17f..0000000
--- a/ios/web/web_state/frame_info.h
+++ /dev/null
@@ -1,19 +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 IOS_WEB_WEB_STATE_FRAME_INFO_H_
-#define IOS_WEB_WEB_STATE_FRAME_INFO_H_
-
-namespace web {
-
-// Data Transfer Object which encapsulates information about a frame.
-struct FrameInfo {
-  explicit FrameInfo(bool main_frame) : is_main_frame(main_frame) {}
-  // true if frame is main frame, false if subframe.
-  bool is_main_frame;
-};
-
-}  // namespace web
-
-#endif  // IOS_WEB_WEB_STATE_FRAME_INFO_H_
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index dc2089a7..61dd29a 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -81,7 +81,6 @@
 #import "ios/web/web_state/crw_pass_kit_downloader.h"
 #import "ios/web/web_state/crw_web_view_proxy_impl.h"
 #import "ios/web/web_state/error_translation_util.h"
-#include "ios/web/web_state/frame_info.h"
 #import "ios/web/web_state/js/crw_js_plugin_placeholder_manager.h"
 #import "ios/web/web_state/js/crw_js_post_request_loader.h"
 #import "ios/web/web_state/js/crw_js_window_id_manager.h"
@@ -802,14 +801,10 @@
 - (void)didBlockPopupWithURL:(GURL)popupURL
                    sourceURL:(GURL)sourceURL
               referrerPolicy:(const std::string&)referrerPolicyString;
-// Best guess as to whether the request is a main frame request. This method
-// should not be assumed correct for security evaluations, as it is possible to
-// spoof.
-- (BOOL)isPutativeMainFrameRequest:(NSURLRequest*)request
-                       targetFrame:(const web::FrameInfo*)targetFrame;
-// Returns whether external URL request should be opened.
-- (BOOL)shouldOpenExternalURLRequest:(NSURLRequest*)request
-                         targetFrame:(const web::FrameInfo*)targetFrame;
+// Returns YES if the navigation action is associated with a main frame request.
+- (BOOL)isMainFrameNavigationAction:(WKNavigationAction*)action;
+// Returns whether external URL navigation action should be opened.
+- (BOOL)shouldOpenExternalURLForNavigationAction:(WKNavigationAction*)action;
 // Called when a page updates its history stack using pushState or replaceState.
 - (void)didUpdateHistoryStateWithPageURL:(const GURL&)url;
 // Updates SSL status for the current navigation item based on the information
@@ -945,14 +940,9 @@
             (base::DictionaryValue*)message
                                           context:(NSDictionary*)context;
 
-// Returns YES if the given load request should be allowed to continue. If this
-// returns NO, the load should be cancelled. |targetFrame| contains information
-// about the frame to which navigation is targeted, can be null.
-// |isLinkClick| should indicate whether the navigation is the
-// result of a link click (either directly, or via JS triggered by a link).
-- (BOOL)shouldAllowLoadWithRequest:(NSURLRequest*)request
-                       targetFrame:(const web::FrameInfo*)targetFrame
-                       isLinkClick:(BOOL)isLinkClick;
+// Returns YES if the given |action| should be allowed to continue.
+// If this returns NO, the load should be cancelled.
+- (BOOL)shouldAllowLoadWithNavigationAction:(WKNavigationAction*)action;
 // Called when a load ends in an error.
 // TODO(stuartmorgan): Figure out if there's actually enough shared logic that
 // this makes sense. At the very least remove inMainFrame since that only makes
@@ -3252,10 +3242,10 @@
 // TODO(stuartmorgan): This is mostly logic from the original UIWebView delegate
 // method, which provides less information than the WKWebView version. Audit
 // this for things that should be handled in the subclass instead.
-- (BOOL)shouldAllowLoadWithRequest:(NSURLRequest*)request
-                       targetFrame:(const web::FrameInfo*)targetFrame
-                       isLinkClick:(BOOL)isLinkClick {
+- (BOOL)shouldAllowLoadWithNavigationAction:(WKNavigationAction*)action {
+  NSURLRequest* request = action.request;
   GURL requestURL = net::GURLWithNSURL(request.URL);
+  BOOL isLinkClick = [self isLinkNavigation:action.navigationType];
 
   // Check if the request should be delayed.
   if (_externalRequest && _externalRequest->url == requestURL) {
@@ -3305,12 +3295,12 @@
   // TODO(droger):  Check transition type before opening an external
   // application? For example, only allow it for TYPED and LINK transitions.
   if (![CRWWebController webControllerCanShow:requestURL]) {
-    if (![self shouldOpenExternalURLRequest:request targetFrame:targetFrame]) {
+    if (![self shouldOpenExternalURLForNavigationAction:action]) {
       return NO;
     }
 
     // Stop load if navigation is believed to be happening on the main frame.
-    if ([self isPutativeMainFrameRequest:request targetFrame:targetFrame])
+    if ([self isMainFrameNavigationAction:action])
       [self stopLoading];
 
     if ([_delegate openExternalURL:requestURL linkClicked:isLinkClick]) {
@@ -3575,10 +3565,8 @@
 }
 
 - (void)updatedProgress:(float)progress {
-  if ([_delegate
-          respondsToSelector:@selector(webController:didUpdateProgress:)]) {
-    [_delegate webController:self didUpdateProgress:progress];
-  }
+  // This is a UIWebView callback, which is no longer called.
+  NOTREACHED();
 }
 
 - (void)certificateUsed:(net::X509Certificate*)certificate
@@ -4347,29 +4335,19 @@
   }
 }
 
-- (BOOL)isPutativeMainFrameRequest:(NSURLRequest*)request
-                       targetFrame:(const web::FrameInfo*)targetFrame {
-  // Determine whether the request is for the main frame using frame info if
-  // available. In the case of missing frame info, the request is considered to
-  // have originated from the main frame if either of the following is true:
-  //   (a) The request's URL matches the request's main document URL
-  //   (b) The request's URL resourceSpecifier matches the request's
-  //       mainDocumentURL specifier, as is the case upon redirect from http
-  //       App Store links to a URL with itms-apps scheme. This appears to be is
-  //       App Store specific behavior, specially handled by web view.
-  // Note: These heuristics are not guaranteed to be correct, and should not be
-  // used for any decisions with security implications.
-  return targetFrame
-             ? targetFrame->is_main_frame
-             : [request.URL isEqual:request.mainDocumentURL] ||
-                   [request.URL.resourceSpecifier
-                       isEqual:request.mainDocumentURL.resourceSpecifier];
+- (BOOL)isMainFrameNavigationAction:(WKNavigationAction*)action {
+  if (action.targetFrame) {
+    return action.targetFrame.mainFrame;
+  }
+  // According to WKNavigationAction documentation, in the case of a new window
+  // navigation, target frame will be nil. In this case check if the
+  // |sourceFrame| is the mainFrame.
+  return action.sourceFrame.mainFrame;
 }
 
-- (BOOL)shouldOpenExternalURLRequest:(NSURLRequest*)request
-                         targetFrame:(const web::FrameInfo*)targetFrame {
+- (BOOL)shouldOpenExternalURLForNavigationAction:(WKNavigationAction*)action {
   ExternalURLRequestStatus requestStatus = NUM_EXTERNAL_URL_REQUEST_STATUS;
-  if ([self isPutativeMainFrameRequest:request targetFrame:targetFrame]) {
+  if ([self isMainFrameNavigationAction:action]) {
     requestStatus = MAIN_FRAME_ALLOWED;
   } else {
     // If the request's main document URL differs from that at the time of the
@@ -4377,7 +4355,7 @@
     // interacted.
     BOOL userInteractedWithRequestMainFrame =
         [self userClickedRecently] &&
-        net::GURLWithNSURL(request.mainDocumentURL) ==
+        net::GURLWithNSURL(action.request.mainDocumentURL) ==
             _lastUserInteraction->main_document_url;
     // Prevent subframe requests from opening an external URL if the user has
     // not interacted with the request's main frame.
@@ -4391,7 +4369,7 @@
     return NO;
   }
 
-  GURL requestURL = net::GURLWithNSURL(request.URL);
+  GURL requestURL = net::GURLWithNSURL(action.request.URL);
   return [_delegate respondsToSelector:@selector(webController:
                                            shouldOpenExternalURL:)] &&
          [_delegate webController:self shouldOpenExternalURL:requestURL];
@@ -4778,17 +4756,17 @@
 
 - (WKWebView*)webView:(WKWebView*)webView
     createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration
-               forNavigationAction:(WKNavigationAction*)navigationAction
+               forNavigationAction:(WKNavigationAction*)action
                     windowFeatures:(WKWindowFeatures*)windowFeatures {
   if (self.shouldSuppressDialogs) {
     [self didSuppressDialog];
     return nil;
   }
 
-  GURL requestURL = net::GURLWithNSURL(navigationAction.request.URL);
+  GURL requestURL = net::GURLWithNSURL(action.request.URL);
 
   if (![self userIsInteracting]) {
-    NSString* referer = [self refererFromNavigationAction:navigationAction];
+    NSString* referer = [self refererFromNavigationAction:action];
     GURL referrerURL =
         referer ? GURL(base::SysNSStringToUTF8(referer)) : [self currentURL];
     if ([self shouldBlockPopupWithURL:requestURL sourceURL:referrerURL]) {
@@ -4899,7 +4877,7 @@
 #pragma mark WKNavigationDelegate Methods
 
 - (void)webView:(WKWebView*)webView
-    decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction
+    decidePolicyForNavigationAction:(WKNavigationAction*)action
                     decisionHandler:
                         (void (^)(WKNavigationActionPolicy))decisionHandler {
   _webProcessIsDead = NO;
@@ -4908,22 +4886,15 @@
     return;
   }
 
-  NSURLRequest* request = navigationAction.request;
-  GURL url = net::GURLWithNSURL(request.URL);
-
   // The page will not be changed until this navigation is commited, so the
   // retrieved state will be pending until |didCommitNavigation| callback.
-  [self updatePendingNavigationInfoFromNavigationAction:navigationAction];
+  [self updatePendingNavigationInfoFromNavigationAction:action];
 
-  web::FrameInfo targetFrame(navigationAction.targetFrame.mainFrame);
-  BOOL isLinkClick = [self isLinkNavigation:navigationAction.navigationType];
-  BOOL allowLoad = [self shouldAllowLoadWithRequest:request
-                                        targetFrame:&targetFrame
-                                        isLinkClick:isLinkClick];
+  BOOL allowLoad = [self shouldAllowLoadWithNavigationAction:action];
 
   if (allowLoad) {
-    allowLoad = self.webStateImpl->ShouldAllowRequest(request);
-    if (!allowLoad && navigationAction.targetFrame.mainFrame) {
+    allowLoad = self.webStateImpl->ShouldAllowRequest(action.request);
+    if (!allowLoad && action.targetFrame.mainFrame) {
       [_pendingNavigationInfo setCancelled:YES];
     }
   }
@@ -5211,14 +5182,8 @@
 }
 
 - (void)webViewEstimatedProgressDidChange {
-  if (_isBeingDestroyed)
-    return;
-
-  self.webStateImpl->SendChangeLoadProgress([_webView estimatedProgress]);
-  if ([_delegate
-          respondsToSelector:@selector(webController:didUpdateProgress:)]) {
-    [_delegate webController:self
-           didUpdateProgress:[_webView estimatedProgress]];
+  if (!_isBeingDestroyed) {
+    self.webStateImpl->SendChangeLoadProgress([_webView estimatedProgress]);
   }
 }
 
diff --git a/ipc/mojo/ipc_message_pipe_reader.cc b/ipc/mojo/ipc_message_pipe_reader.cc
index de5726d..0335335 100644
--- a/ipc/mojo/ipc_message_pipe_reader.cc
+++ b/ipc/mojo/ipc_message_pipe_reader.cc
@@ -108,15 +108,8 @@
   DCHECK_LE(num_handles, std::numeric_limits<uint32_t>::max());
 
   mojo_message->set_interface_id(sender_interface_id_);
-  result = mojo::WriteMessageRaw(
-      sender_pipe_, mojo_message->data(), mojo_message->data_num_bytes(),
-      reinterpret_cast<const MojoHandle*>(mojo_message->handles()->data()),
-      static_cast<uint32_t>(num_handles), MOJO_WRITE_MESSAGE_FLAG_NONE);
-
-  // If the write was successful, the handles have been transferred and they
-  // should not be closed when the message is destroyed.
-  if (result == MOJO_RESULT_OK)
-    mojo_message->mutable_handles()->clear();
+  result = mojo::WriteMessageNew(sender_pipe_, mojo_message->TakeMojoMessage(),
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE);
 
   DVLOG(4) << "Send " << message->type() << ": " << message->size();
   return result == MOJO_RESULT_OK;
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index e9b8602..8289f73 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -381,8 +381,11 @@
 
     case kSuspended:
       renderer_.reset();
-      statistics_.audio_memory_usage = 0;
-      statistics_.video_memory_usage = 0;
+      {
+        base::AutoLock auto_lock(lock_);
+        statistics_.audio_memory_usage = 0;
+        statistics_.video_memory_usage = 0;
+      }
       base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK);
       return;
 
diff --git a/media/blink/key_system_config_selector.cc b/media/blink/key_system_config_selector.cc
index f48f34b2..ec36f2e7 100644
--- a/media/blink/key_system_config_selector.cc
+++ b/media/blink/key_system_config_selector.cc
@@ -310,20 +310,33 @@
     const std::string& container_mime_type,
     const std::string& codecs,
     KeySystemConfigSelector::ConfigState* config_state) {
+  // From RFC6838: "Both top-level type and subtype names are case-insensitive."
+  std::string container_lower = base::ToLowerASCII(container_mime_type);
+
+  // contentTypes must provide a codec string unless the container implies a
+  // particular codec. For EME, none of the currently supported containers
+  // imply a codec, so |codecs| must be provided.
+  if (codecs.empty()) {
+    // Since the spec didn't initially require this, add an exemption for
+    // some existing containers to give clients time to adapt.
+    // TODO(jrummell): Remove this exemption once the number of contentTypes
+    // without codecs drops low enough (UMA added in the blink code).
+    // http://crbug.com/605661.
+    if (container_lower != "audio/webm" && container_lower != "video/webm" &&
+        container_lower != "audio/mp4" && container_lower != "video/mp4") {
+      return false;
+    }
+  }
+
   // Check that |container_mime_type| and |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 could complicate the
   // robustness algorithm).
-  if (!IsSupportedMediaFormat(container_mime_type, codecs,
+  if (!IsSupportedMediaFormat(container_lower, codecs,
                               CanUseAesDecryptor(key_system))) {
     return false;
   }
 
-  // TODO(servolk): Converting |container_mime_type| to lower-case could be
-  // moved to KeySystemsImpl::GetContentTypeConfigRule, plus we could add some
-  // upper-case container name test cases in media/base/key_systems_unittest.cc.
-  std::string container_lower = base::ToLowerASCII(container_mime_type);
-
   // Check that |container_mime_type| and |codecs| are supported by the CDM.
   // This check does not handle extended codecs, so extended codec information
   // is stripped (extended codec information was checked above).
@@ -574,6 +587,7 @@
     }
 
     // 10.3. Add video capabilities to accumulated configuration.
+    accumulated_configuration->hasVideoCapabilities = true;
     accumulated_configuration->videoCapabilities = video_capabilities;
   }
 
@@ -592,6 +606,7 @@
     }
 
     // 11.3. Add audio capabilities to accumulated configuration.
+    accumulated_configuration->hasAudioCapabilities = true;
     accumulated_configuration->audioCapabilities = audio_capabilities;
   }
 
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 00d946d..ae82fad 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -245,19 +245,11 @@
   source = "media_manifest.json"
 }
 
-# Note, the following tests must be loaded via mojo_runner as an app, e.g.
-#
-#   mojo/tools/apptest_runner.py
-#     --apptest-filter mojo:media_apptests out/Debug
-#
-#   mojo/tools/apptest_runner.py
-#     --apptest-filter mojo:media_pipeline_integration_apptests out/Debug
-#
-mojo_native_application("media_apptests") {
+test("media_mojo_shell_unittests") {
   testonly = true
 
   sources = [
-    "media_apptest.cc",
+    "media_mojo_unittest.cc",
   ]
 
   deps = [
@@ -266,44 +258,40 @@
     "//media/base:test_support",
     "//media/mojo/common",
     "//media/mojo/interfaces",
-    "//services/shell/public/cpp:test_support",
+    "//services/shell/public/cpp:shell_test_support",
+    "//services/shell/public/cpp/test:run_all_shelltests",
     "//testing/gmock",
     "//testing/gtest",
   ]
 
   data_deps = [
-    ":apptest_manifest",
+    ":test_manifest",
     ":media",
   ]
 }
 
-mojo_application_manifest("apptest_manifest") {
-  application_name = "media_apptests"
-  source = "apptest_manifest.json"
+mojo_application_manifest("test_manifest") {
+  application_name = "media_mojo_shell_unittests"
+  type = "exe"
+  source = "test_manifest.json"
 }
 
-mojo_native_application("media_pipeline_integration_apptests") {
+test("media_pipeline_integration_unittests") {
   testonly = true
 
   deps = [
     "//media/test:mojo_pipeline_integration_tests",
+    "//services/shell/public/cpp/test:run_all_shelltests",
   ]
 
   data_deps = [
     ":media",
-    ":pipeline_apptest_manifest",
+    ":pipeline_test_manifest",
   ]
 }
 
-mojo_application_manifest("pipeline_apptest_manifest") {
-  application_name = "media_pipeline_integration_apptests"
-  source = "pipeline_apptest_manifest.json"
-}
-
-group("tests") {
-  testonly = true
-  deps = [
-    ":media_apptests",
-    ":media_pipeline_integration_apptests",
-  ]
+mojo_application_manifest("pipeline_test_manifest") {
+  application_name = "media_pipeline_integration_unittests"
+  type = "exe"
+  source = "pipeline_test_manifest.json"
 }
diff --git a/media/mojo/services/media_apptest.cc b/media/mojo/services/media_mojo_unittest.cc
similarity index 86%
rename from media/mojo/services/media_apptest.cc
rename to media/mojo/services/media_mojo_unittest.cc
index 018d572..dd835a5 100644
--- a/media/mojo/services/media_apptest.cc
+++ b/media/mojo/services/media_mojo_unittest.cc
@@ -20,7 +20,7 @@
 #include "media/mojo/interfaces/renderer.mojom.h"
 #include "media/mojo/interfaces/service_factory.mojom.h"
 #include "media/mojo/services/mojo_demuxer_stream_impl.h"
-#include "services/shell/public/cpp/application_test_base.h"
+#include "services/shell/public/cpp/shell_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using testing::Exactly;
@@ -52,19 +52,20 @@
   DISALLOW_COPY_AND_ASSIGN(MockRendererClient);
 };
 
-class MediaAppTest : public shell::test::ApplicationTestBase {
+class MediaShellTest : public shell::test::ShellTest {
  public:
-  MediaAppTest()
-      : renderer_client_binding_(&renderer_client_),
+  MediaShellTest()
+      : ShellTest("exe:media_mojo_unittests"),
+        renderer_client_binding_(&renderer_client_),
         video_demuxer_stream_(DemuxerStream::VIDEO) {}
-  ~MediaAppTest() override {}
+  ~MediaShellTest() override {}
 
   void SetUp() override {
-    ApplicationTestBase::SetUp();
+    ShellTest::SetUp();
 
     connection_ = connector()->Connect("mojo:media");
     connection_->SetConnectionLostClosure(
-        base::Bind(&MediaAppTest::ConnectionClosed, base::Unretained(this)));
+        base::Bind(&MediaShellTest::ConnectionClosed, base::Unretained(this)));
 
     connection_->GetInterface(&service_factory_);
 
@@ -90,7 +91,7 @@
         .WillOnce(InvokeWithoutArgs(run_loop_.get(), &base::RunLoop::Quit));
     cdm_->Initialize(
         key_system, kSecurityOrigin, interfaces::CdmConfig::From(CdmConfig()),
-        base::Bind(&MediaAppTest::OnCdmInitialized, base::Unretained(this)));
+        base::Bind(&MediaShellTest::OnCdmInitialized, base::Unretained(this)));
   }
 
   MOCK_METHOD1(OnRendererInitialized, void(bool));
@@ -109,7 +110,7 @@
         .WillOnce(InvokeWithoutArgs(run_loop_.get(), &base::RunLoop::Quit));
     renderer_->Initialize(renderer_client_binding_.CreateInterfacePtrAndBind(),
                           nullptr, std::move(video_stream),
-                          base::Bind(&MediaAppTest::OnRendererInitialized,
+                          base::Bind(&MediaShellTest::OnRendererInitialized,
                                      base::Unretained(this)));
   }
 
@@ -130,7 +131,7 @@
  private:
   std::unique_ptr<shell::Connection> connection_;
 
-  DISALLOW_COPY_AND_ASSIGN(MediaAppTest);
+  DISALLOW_COPY_AND_ASSIGN(MediaShellTest);
 };
 
 }  // namespace
@@ -139,12 +140,12 @@
 // even when the loop is idle, we may still have pending events in the pipe.
 
 #if defined(ENABLE_MOJO_CDM)
-TEST_F(MediaAppTest, InitializeCdm_Success) {
+TEST_F(MediaShellTest, InitializeCdm_Success) {
   InitializeCdm(kClearKeyKeySystem, true, 1);
   run_loop_->Run();
 }
 
-TEST_F(MediaAppTest, InitializeCdm_InvalidKeySystem) {
+TEST_F(MediaShellTest, InitializeCdm_InvalidKeySystem) {
   InitializeCdm(kInvalidKeySystem, false, 0);
   run_loop_->Run();
 }
@@ -158,18 +159,18 @@
 #define MAYBE_InitializeRenderer_Success InitializeRenderer_Success
 #endif
 
-TEST_F(MediaAppTest, MAYBE_InitializeRenderer_Success) {
+TEST_F(MediaShellTest, MAYBE_InitializeRenderer_Success) {
   InitializeRenderer(TestVideoConfig::Normal(), true);
   run_loop_->Run();
 }
 
-TEST_F(MediaAppTest, InitializeRenderer_InvalidConfig) {
+TEST_F(MediaShellTest, InitializeRenderer_InvalidConfig) {
   InitializeRenderer(TestVideoConfig::Invalid(), false);
   run_loop_->Run();
 }
 #endif  // defined(ENABLE_MOJO_RENDERER)
 
-TEST_F(MediaAppTest, Lifetime) {
+TEST_F(MediaShellTest, Lifetime) {
   // Disconnecting CDM and Renderer services doesn't terminate the app.
   cdm_.reset();
   renderer_.reset();
diff --git a/media/mojo/services/pipeline_test_manifest.json b/media/mojo/services/pipeline_test_manifest.json
new file mode 100644
index 0000000..25978dd
--- /dev/null
+++ b/media/mojo/services/pipeline_test_manifest.json
@@ -0,0 +1,10 @@
+{
+  "manifest_version": 1,
+  "name": "exe:media_pipeline_integration_unittests",
+  "display_name": "Media Pipeline Integration Unittests",
+  "capabilities": {
+    "required": {
+      "*": { "classes": [ "app" ] }
+    }
+  }
+}
diff --git a/media/mojo/services/test_manifest.json b/media/mojo/services/test_manifest.json
new file mode 100644
index 0000000..d4dbaba8
--- /dev/null
+++ b/media/mojo/services/test_manifest.json
@@ -0,0 +1,11 @@
+{
+  "manifest_version":  1,
+  "name": "exe:media_mojo_shell_unittests",
+  "display_name": "Media Mojo Shell Unittests",
+  "capabilities": {
+    "required": {
+      "*": { "classes": [ "app" ] },
+      "mojo:media": { "interfaces": [ "media::interfaces::ServiceFactory" ] }
+    }
+  }
+}
diff --git a/media/test/BUILD.gn b/media/test/BUILD.gn
index 10ecd76..16c9d57 100644
--- a/media/test/BUILD.gn
+++ b/media/test/BUILD.gn
@@ -102,7 +102,7 @@
       "//media/mojo/interfaces",
       "//media/mojo/services:proxy",
       "//media/mojo/services:renderer_service",
-      "//services/shell/public/cpp:test_support",
+      "//services/shell/public/cpp:shell_test_support",
       "//testing/gtest",
       "//ui/gfx:test_support",
       "//ui/gfx/geometry",
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index f6a88773..5aac5cc 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -39,8 +39,8 @@
 #include "media/mojo/interfaces/renderer.mojom.h"
 #include "media/mojo/interfaces/service_factory.mojom.h"
 #include "media/mojo/services/mojo_renderer_impl.h"
-#include "services/shell/public/cpp/application_test_base.h"
 #include "services/shell/public/cpp/connect.h"
+#include "services/shell/public/cpp/shell_test.h"
 
 // TODO(dalecurtis): The mojo renderer is in another process, so we have no way
 // currently to get hashes for video and audio samples.  This also means that
@@ -661,14 +661,20 @@
   base::TimeDelta last_timestamp_offset_;
 };
 
-#if defined(MOJO_RENDERER)
-class PipelineIntegrationTestHost : public shell::test::ApplicationTestBase,
+// TODO(xhwang): These tests have been disabled for some time as apptests and no
+//               longer pass. They need to be reconstituted as shell tests.
+//               Currently there are compile issues which must be resolved,
+//               preferably by eliminating multiple inheritance here which is
+//               banned by Google C++ style.
+#if defined(MOJO_RENDERER) && defined(ENABLE_MOJO_PIPELINE_INTEGRATION_TEST)
+class PipelineIntegrationTestHost : public shell::test::ShellTest,
                                     public PipelineIntegrationTestBase {
  public:
-  bool ShouldCreateDefaultRunLoop() override { return false; }
+  PipelineIntegrationTestHost()
+      : shell::test::ShellTest("exe:media_pipeline_integration_shelltests") {}
 
   void SetUp() override {
-    ApplicationTestBase::SetUp();
+    ShellTest::SetUp();
     InitializeMediaLibrary();
   }
 
diff --git a/mojo/mojo_public.gyp b/mojo/mojo_public.gyp
index a92ce12..77e0b7726 100644
--- a/mojo/mojo_public.gyp
+++ b/mojo/mojo_public.gyp
@@ -100,6 +100,7 @@
         'public/cpp/system/data_pipe.h',
         'public/cpp/system/functions.h',
         'public/cpp/system/handle.h',
+        'public/cpp/system/message.h',
         'public/cpp/system/message_pipe.h',
         'public/cpp/system/watcher.cc',
         'public/cpp/system/watcher.h',
@@ -164,6 +165,8 @@
         'public/cpp/bindings/lib/map_internal.h',
         'public/cpp/bindings/lib/map_serialization.h',
         'public/cpp/bindings/lib/message.cc',
+        'public/cpp/bindings/lib/message_buffer.cc',
+        'public/cpp/bindings/lib/message_buffer.h',
         'public/cpp/bindings/lib/message_builder.cc',
         'public/cpp/bindings/lib/message_builder.h',
         'public/cpp/bindings/lib/message_filter.cc',
@@ -178,8 +181,6 @@
         'public/cpp/bindings/lib/native_struct_serialization.cc',
         'public/cpp/bindings/lib/native_struct_serialization.h',
         'public/cpp/bindings/lib/no_interface.cc',
-        'public/cpp/bindings/lib/pickle_buffer.cc',
-        'public/cpp/bindings/lib/pickle_buffer.h',
         'public/cpp/bindings/lib/pipe_control_message_handler.cc',
         'public/cpp/bindings/lib/pipe_control_message_handler.h',
         'public/cpp/bindings/lib/pipe_control_message_handler_delegate.h',
diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h
index dcce9df..d41e107 100644
--- a/mojo/public/c/system/message_pipe.h
+++ b/mojo/public/c/system/message_pipe.h
@@ -308,6 +308,8 @@
 //   |MOJO_RESULT_OK| if |message| is a valid message object. |*buffer| will
 //       be updated to point to mutable message bytes.
 //   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object.
+//
+// NOTE: A returned buffer address is always guaranteed to be 8-byte aligned.
 MOJO_SYSTEM_EXPORT MojoResult MojoGetMessageBuffer(MojoMessageHandle message,
                                                    void** buffer);  // Out
 
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 5fe4d9d..4666453 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -50,6 +50,8 @@
     "lib/map_internal.h",
     "lib/map_serialization.h",
     "lib/message.cc",
+    "lib/message_buffer.cc",
+    "lib/message_buffer.h",
     "lib/message_builder.cc",
     "lib/message_builder.h",
     "lib/message_filter.cc",
@@ -64,8 +66,6 @@
     "lib/native_struct_serialization.cc",
     "lib/native_struct_serialization.h",
     "lib/no_interface.cc",
-    "lib/pickle_buffer.cc",
-    "lib/pickle_buffer.h",
     "lib/pipe_control_message_handler.cc",
     "lib/pipe_control_message_handler.h",
     "lib/pipe_control_message_handler_delegate.h",
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
index 98bbce0e..c3b570e 100644
--- a/mojo/public/cpp/bindings/lib/buffer.h
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -10,18 +10,12 @@
 namespace mojo {
 namespace internal {
 
-class PickleBuffer;
-
 // Buffer provides a way to allocate memory. Allocations are 8-byte aligned and
 // zero-initialized. Allocations remain valid for the lifetime of the Buffer.
 class Buffer {
  public:
   virtual ~Buffer() {}
   virtual void* Allocate(size_t num_bytes) = 0;
-
-  // TODO(rockot): Remove this. It's a hack to get a PickleBuffer in
-  // Serialize_ calls without having to update every call site.
-  virtual PickleBuffer* AsPickleBuffer() = 0;
 };
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 1d25640..9f1037c 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -150,20 +150,9 @@
     return true;
 
   MojoResult rv =
-      WriteMessageRaw(message_pipe_.get(),
-                      message->data(),
-                      message->data_num_bytes(),
-                      message->mutable_handles()->empty()
-                          ? nullptr
-                          : reinterpret_cast<const MojoHandle*>(
-                                &message->mutable_handles()->front()),
-                      static_cast<uint32_t>(message->mutable_handles()->size()),
+      WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(),
                       MOJO_WRITE_MESSAGE_FLAG_NONE);
 
-  // The handles are always either transferred or closed, so we don't need the
-  // message to track their lifetime any longer.
-  message->mutable_handles()->clear();
-
   switch (rv) {
     case MOJO_RESULT_OK:
       break;
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
index 9653345..e9517f3 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -39,8 +39,6 @@
   return result;
 }
 
-PickleBuffer* FixedBuffer::AsPickleBuffer() { return nullptr; }
-
 FixedBufferForTesting::FixedBufferForTesting(size_t size) {
   size_ = internal::Align(size);
   // Use calloc here to ensure all message memory is zero'd out.
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
index d087c3b3..9a5704b4 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -51,8 +51,6 @@
   // memory is zero-filled.
   void* Allocate(size_t num_bytes) override;
 
-  PickleBuffer* AsPickleBuffer() override;
-
  protected:
   char* ptr_;
   size_t cursor_;
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
index d7a701b..9e32c5af 100644
--- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
+++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -219,10 +219,11 @@
 
   message->set_request_id(request_id);
 
+  bool is_sync = message->has_flag(kMessageIsSync);
   if (!controller_->SendMessage(message))
     return false;
 
-  if (!message->has_flag(kMessageIsSync)) {
+  if (!is_sync) {
     // We assume ownership of |responder|.
     async_responders_[request_id] = base::WrapUnique(responder);
     return true;
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index e1e8f190..4274cc86 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 
 #include <algorithm>
+#include <utility>
 
 #include "base/logging.h"
 
@@ -23,7 +24,15 @@
 
 void Message::Initialize(size_t capacity, bool zero_initialized) {
   DCHECK(!buffer_);
-  buffer_.reset(new internal::PickleBuffer(capacity, zero_initialized));
+  buffer_.reset(new internal::MessageBuffer(capacity, zero_initialized));
+}
+
+void Message::InitializeFromMojoMessage(ScopedMessageHandle message,
+                                        uint32_t num_bytes,
+                                        std::vector<Handle>* handles) {
+  DCHECK(!buffer_);
+  buffer_.reset(new internal::MessageBuffer(std::move(message), num_bytes));
+  handles_.swap(*handles);
 }
 
 void Message::MoveTo(Message* destination) {
@@ -38,6 +47,37 @@
   buffer_.reset();
 }
 
+ScopedMessageHandle Message::TakeMojoMessage() {
+  if (handles_.empty())  // Fast path for the common case: No handles.
+    return buffer_->TakeMessage();
+
+  // Allocate a new message with space for the handles, then copy the buffer
+  // contents into it.
+  //
+  // TODO(rockot): We could avoid this copy by extending GetSerializedSize()
+  // behavior to collect handles. It's unoptimized for now because it's much
+  // more common to have messages with no handles.
+  ScopedMessageHandle new_message;
+  MojoResult rv = AllocMessage(
+      data_num_bytes(),
+      handles_.empty() ? nullptr
+                       : reinterpret_cast<const MojoHandle*>(handles_.data()),
+      handles_.size(),
+      MOJO_ALLOC_MESSAGE_FLAG_NONE,
+      &new_message);
+  CHECK_EQ(rv, MOJO_RESULT_OK);
+  handles_.clear();
+
+  void* new_buffer = nullptr;
+  rv = GetMessageBuffer(new_message.get(), &new_buffer);
+  CHECK_EQ(rv, MOJO_RESULT_OK);
+
+  memcpy(new_buffer, data(), data_num_bytes());
+  buffer_.reset();
+
+  return new_message;
+}
+
 void Message::CloseHandles() {
   for (std::vector<Handle>::iterator it = handles_.begin();
        it != handles_.end(); ++it) {
@@ -49,28 +89,32 @@
 MojoResult ReadMessage(MessagePipeHandle handle, Message* message) {
   MojoResult rv;
 
+  std::vector<Handle> handles;
+  ScopedMessageHandle mojo_message;
   uint32_t num_bytes = 0, num_handles = 0;
-  rv = ReadMessageRaw(handle,
-                      nullptr,
+  rv = ReadMessageNew(handle,
+                      &mojo_message,
                       &num_bytes,
                       nullptr,
                       &num_handles,
                       MOJO_READ_MESSAGE_FLAG_NONE);
-  if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED)
+  if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    DCHECK_GT(num_handles, 0u);
+    handles.resize(num_handles);
+    rv = ReadMessageNew(handle,
+                        &mojo_message,
+                        &num_bytes,
+                        reinterpret_cast<MojoHandle*>(handles.data()),
+                        &num_handles,
+                        MOJO_READ_MESSAGE_FLAG_NONE);
+  }
+
+  if (rv != MOJO_RESULT_OK)
     return rv;
 
-  message->Initialize(num_bytes, false /* zero_initialized */);
-
-  void* mutable_data = message->buffer()->Allocate(num_bytes);
-  message->mutable_handles()->resize(num_handles);
-
-  rv = ReadMessageRaw(
-      handle, mutable_data, &num_bytes,
-      message->mutable_handles()->empty()
-          ? nullptr
-          : reinterpret_cast<MojoHandle*>(message->mutable_handles()->data()),
-      &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
-  return rv;
+  message->InitializeFromMojoMessage(
+      std::move(mojo_message), num_bytes, &handles);
+  return MOJO_RESULT_OK;
 }
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.cc b/mojo/public/cpp/bindings/lib/message_buffer.cc
new file mode 100644
index 0000000..8315263
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_buffer.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium 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 "mojo/public/cpp/bindings/lib/message_buffer.h"
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+
+namespace mojo {
+namespace internal {
+
+MessageBuffer::MessageBuffer(size_t capacity, bool zero_initialized) {
+  DCHECK_LE(capacity, std::numeric_limits<uint32_t>::max());
+  data_num_bytes_ = static_cast<uint32_t>(capacity);
+
+  MojoResult rv = AllocMessage(capacity, nullptr, 0,
+                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_);
+  CHECK_EQ(rv, MOJO_RESULT_OK);
+
+  if (capacity == 0) {
+    buffer_ = nullptr;
+  } else {
+    rv = GetMessageBuffer(message_.get(), &buffer_);
+    CHECK_EQ(rv, MOJO_RESULT_OK);
+
+    if (zero_initialized)
+      memset(buffer_, 0, capacity);
+  }
+}
+
+MessageBuffer::MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes) {
+  message_ = std::move(message);
+  data_num_bytes_ = num_bytes;
+
+  if (num_bytes == 0) {
+    buffer_ = nullptr;
+  } else {
+    MojoResult rv = GetMessageBuffer(message_.get(), &buffer_);
+    CHECK_EQ(rv, MOJO_RESULT_OK);
+  }
+}
+
+MessageBuffer::~MessageBuffer() {}
+
+void* MessageBuffer::Allocate(size_t delta) {
+  delta = internal::Align(delta);
+
+  DCHECK_LE(delta, static_cast<size_t>(data_num_bytes_));
+  DCHECK_GT(bytes_claimed_ + static_cast<uint32_t>(delta), bytes_claimed_);
+
+  uint32_t new_bytes_claimed = bytes_claimed_ + static_cast<uint32_t>(delta);
+  if (new_bytes_claimed > data_num_bytes_) {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  char* start = static_cast<char*>(buffer_) + bytes_claimed_;
+  bytes_claimed_ = new_bytes_claimed;
+  return static_cast<void*>(start);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.h b/mojo/public/cpp/bindings/lib/message_buffer.h
new file mode 100644
index 0000000..47ffea0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_buffer.h
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/system/message.h"
+
+namespace mojo {
+namespace internal {
+
+// A fixed-size Buffer implementation using a Mojo message object for storage.
+class MessageBuffer : public Buffer {
+ public:
+  // Initializes this buffer to carry a fixed byte capacity and no handles.
+  MessageBuffer(size_t capacity, bool zero_initialized);
+
+  // Initializes this buffer from an existing Mojo MessageHandle.
+  MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes);
+
+  ~MessageBuffer() override;
+
+  void* data() const { return buffer_; }
+  uint32_t data_num_bytes() const { return data_num_bytes_; }
+
+  // Buffer:
+  void* Allocate(size_t delta) override;
+
+  ScopedMessageHandle TakeMessage() { return std::move(message_); }
+
+ private:
+  uint32_t data_num_bytes_ = 0;
+  ScopedMessageHandle message_;
+  void* buffer_;
+
+  uint32_t bytes_claimed_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageBuffer);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.h b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
index 964bbdb1..60d610b 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_serialization.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
@@ -17,7 +17,6 @@
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 #include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
 #include "mojo/public/cpp/bindings/lib/native_struct_data.h"
-#include "mojo/public/cpp/bindings/lib/pickle_buffer.h"
 #include "mojo/public/cpp/bindings/lib/serialization_forward.h"
 
 namespace mojo {
@@ -43,41 +42,20 @@
                       Buffer* buffer,
                       NativeStruct_Data** out,
                       SerializationContext* context) {
-  PickleBuffer* pickler = buffer->AsPickleBuffer();
-  DCHECK(pickler) << "Native types can only be used with PickleBuffers.";
+  base::Pickle pickle;
+  IPC::ParamTraits<T>::Write(&pickle, value);
 
-  ArrayHeader* header =
-      reinterpret_cast<ArrayHeader*>(buffer->Allocate(sizeof(ArrayHeader)));
-
-  // Remember where the Pickle started before writing.
-  base::Pickle* pickle = pickler->pickle();
-  const char* data_start = pickle->end_of_payload();
-
-#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
-  const char* payload_base = pickle->payload();
-  size_t size_before_write = pickle->payload_size();
-#endif
-
-  IPC::ParamTraits<T>::Write(pickle, value);
-
-#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
-  // Ensure the pickle buffer hasn't moved.
-  DCHECK_EQ(payload_base, pickle->payload());
-  // Explicitly validate that the value returned by GetSize() always equals the
-  // number of bytes actually written by Write().
-  DCHECK_GE(pickle->payload_size(), size_before_write);
-  size_t bytes_written = pickle->payload_size() - size_before_write;
-  DCHECK_EQ(Align(bytes_written + sizeof(ArrayHeader)),
-            GetSerializedSizeNative_(value, context));
-#endif
-
-  // Fix up the ArrayHeader so that num_elements contains the length of the
-  // pickled data.
-  size_t pickled_size = pickle->end_of_payload() - data_start;
-  size_t total_size = pickled_size + sizeof(ArrayHeader);
+  size_t total_size = pickle.payload_size() + sizeof(ArrayHeader);
   DCHECK_LT(total_size, std::numeric_limits<uint32_t>::max());
+  DCHECK_EQ(Align(total_size), GetSerializedSizeNative_(value, context));
+
+  // Allocate a uint8 array, initialize its header, and copy the Pickle in.
+  ArrayHeader* header =
+      reinterpret_cast<ArrayHeader*>(buffer->Allocate(total_size));
   header->num_bytes = static_cast<uint32_t>(total_size);
-  header->num_elements = static_cast<uint32_t>(pickled_size);
+  header->num_elements = static_cast<uint32_t>(pickle.payload_size());
+  memcpy(reinterpret_cast<char*>(header) + sizeof(ArrayHeader),
+         pickle.payload(), pickle.payload_size());
 
   *out = reinterpret_cast<NativeStruct_Data*>(header);
 }
diff --git a/mojo/public/cpp/bindings/lib/pickle_buffer.cc b/mojo/public/cpp/bindings/lib/pickle_buffer.cc
deleted file mode 100644
index ecd75dba..0000000
--- a/mojo/public/cpp/bindings/lib/pickle_buffer.cc
+++ /dev/null
@@ -1,98 +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 "mojo/public/cpp/bindings/lib/pickle_buffer.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/pickle.h"
-#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
-
-namespace mojo {
-namespace internal {
-
-class PickleBuffer::Storage : public base::Pickle {
- public:
-  explicit Storage(size_t num_bytes);
-  ~Storage() override {}
-
-  size_t available_capacity() const {
-    return capacity_after_header() - payload_size();
-  }
-
-  void* GetData() { return static_cast<void*>(mutable_payload()); }
-  void* Claim(size_t num_bytes) { return ClaimBytes(num_bytes); }
-
- private:
-  // TODO(rockot): Stop wasting 8 bytes per buffer.
-  //
-  // We don't use Pickle's header at all, but its base header type consumes 4
-  // bytes. We waste another 4 bytes to keep our actual buffer aligned to an
-  // 8-byte boundary.
-  //
-  // The reason we don't use base::Pickle's header is that it stores payload
-  // length in the first 4 bytes. Mojo Messages are packed like mojom structs,
-  // where the first 4 bytes are header size rather than payload size.
-  struct PaddedHeader : public base::Pickle::Header {
-    uint32_t padding;
-  };
-
-  static_assert(sizeof(PaddedHeader) % 8 == 0,
-      "PickleBuffer requires a Pickle header size with 8-byte alignment.");
-
-  DISALLOW_COPY_AND_ASSIGN(Storage);
-};
-
-PickleBuffer::Storage::Storage(size_t num_bytes)
-    : base::Pickle(sizeof(PaddedHeader)) {
-  headerT<PaddedHeader>()->padding = 0;
-  Resize(num_bytes);
-}
-
-PickleBuffer::PickleBuffer(size_t num_bytes, bool zero_initialized)
-    : storage_(new Storage(num_bytes)) {
-  if (zero_initialized)
-    memset(storage_->GetData(), 0, num_bytes);
-}
-
-PickleBuffer::~PickleBuffer() {
-}
-
-const void* PickleBuffer::data() const { return storage_->GetData(); }
-
-size_t PickleBuffer::data_num_bytes() const { return storage_->payload_size(); }
-
-base::Pickle* PickleBuffer::pickle() const {
-  return storage_.get();
-}
-
-void* PickleBuffer::Allocate(size_t num_bytes) {
-  DCHECK(storage_);
-
-  // The last allocation may terminate in between 8-byte boundaries. Pad the
-  // front of this allocation if that's the case.
-  size_t padded_capacity = Align(storage_->payload_size());
-  DCHECK_GE(padded_capacity, storage_->payload_size());
-
-  size_t padding_bytes = padded_capacity - storage_->payload_size();
-  size_t allocation_size = padding_bytes + num_bytes;
-  const void* previous_data_location = storage_->GetData();
-  if (storage_->available_capacity() < allocation_size) {
-    NOTREACHED() <<
-        "Message buffers must be fully allocated before serialization.";
-    return nullptr;
-  }
-  char* p = static_cast<char*>(storage_->Claim(allocation_size));
-  DCHECK_EQ(storage_->GetData(), previous_data_location);
-  return p + padding_bytes;
-}
-
-PickleBuffer* PickleBuffer::AsPickleBuffer() { return this; }
-
-}  // namespace internal
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/pickle_buffer.h b/mojo/public/cpp/bindings/lib/pickle_buffer.h
deleted file mode 100644
index 1fb3921..0000000
--- a/mojo/public/cpp/bindings/lib/pickle_buffer.h
+++ /dev/null
@@ -1,56 +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 MOJO_PUBLIC_CPP_BINDINGS_LIB_PICKLE_BUFFER_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_PICKLE_BUFFER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/pickle.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-
-namespace mojo {
-namespace internal {
-
-// An implementation of Buffer which uses base::Pickle for its backing. Note
-// that this does not use Pickle's header structure at all, instead storing
-// the complete Message (including header) in the Pickle's payload.
-class PickleBuffer : public Buffer {
- public:
-  PickleBuffer(size_t num_bytes, bool zero_initialized);
-  ~PickleBuffer() override;
-
-  const void* data() const;
-
-  void* data() {
-    return const_cast<void*>(static_cast<const PickleBuffer*>(this)->data());
-  }
-
-  size_t data_num_bytes() const;
-
-  base::Pickle* pickle() const;
-
- private:
-  class Storage;
-
-  // Buffer implementation. Note that this cannot grow the Pickle's capacity and
-  // it is an error to Allocate() more bytes in total than have been
-  // pre-allocated using ReserveCapacity() or ReserveUninitializedCapacity().
-  //
-  // This guarantees that the returned data is aligned on an 8-byte boundary.
-  void* Allocate(size_t num_bytes) override;
-  PickleBuffer* AsPickleBuffer() override;
-
-  std::unique_ptr<Storage> storage_;
-
-  DISALLOW_COPY_AND_ASSIGN(PickleBuffer);
-};
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_PICKLE_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc
index 66d7e36..da5f34b 100644
--- a/mojo/public/cpp/bindings/lib/router.cc
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -153,11 +153,12 @@
   if (request_id == 0)
     request_id = next_request_id_++;
 
+  bool is_sync = message->has_flag(kMessageIsSync);
   message->set_request_id(request_id);
   if (!connector_.Accept(message))
     return false;
 
-  if (!message->has_flag(kMessageIsSync)) {
+  if (!is_sync) {
     // We assume ownership of |responder|.
     async_responders_[request_id] = base::WrapUnique(responder);
     return true;
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 2d06500..d8eed2bf 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -13,8 +13,9 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/message_buffer.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
-#include "mojo/public/cpp/bindings/lib/pickle_buffer.h"
+#include "mojo/public/cpp/system/message.h"
 
 namespace mojo {
 
@@ -27,15 +28,18 @@
   Message();
   ~Message();
 
+  // Initializes a Message with enough space for |capacity| bytes.
   void Initialize(size_t capacity, bool zero_initialized);
 
+  // Initializes a Message from an existing Mojo MessageHandle.
+  void InitializeFromMojoMessage(ScopedMessageHandle message,
+                                 uint32_t num_bytes,
+                                 std::vector<Handle>* handles);
+
   // Transfers data and handles to |destination|.
   void MoveTo(Message* destination);
 
-  uint32_t data_num_bytes() const {
-    DCHECK(buffer_->data_num_bytes() <= std::numeric_limits<uint32_t>::max());
-    return static_cast<uint32_t>(buffer_->data_num_bytes());
-  }
+  uint32_t data_num_bytes() const { return buffer_->data_num_bytes(); }
 
   // Access the raw bytes of the message.
   const uint8_t* data() const {
@@ -90,10 +94,15 @@
   // Access the underlying Buffer interface.
   internal::Buffer* buffer() { return buffer_.get(); }
 
+  // Takes a scoped MessageHandle which may be passed to |WriteMessageNew()| for
+  // transmission. Note that this invalidates this Message object, taking
+  // ownership of its internal storage and any attached handles.
+  ScopedMessageHandle TakeMojoMessage();
+
  private:
   void CloseHandles();
 
-  std::unique_ptr<internal::PickleBuffer> buffer_;
+  std::unique_ptr<internal::MessageBuffer> buffer_;
   std::vector<Handle> handles_;
 
   DISALLOW_COPY_AND_ASSIGN(Message);
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 4292bd9dbf..96fd23d2 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -527,11 +527,11 @@
 
   const char kText[] = "hello world";
 
+  // Queue up two messages.
   Message message;
   AllocMessage(kText, &message);
-
-  // Queue up two messages.
   connector0.Accept(&message);
+  AllocMessage(kText, &message);
   connector0.Accept(&message);
 
   base::RunLoop run_loop;
@@ -560,11 +560,11 @@
 
   const char kText[] = "hello world";
 
+  // Queue up two messages.
   Message message;
   AllocMessage(kText, &message);
-
-  // Queue up two messages.
   connector0.Accept(&message);
+  AllocMessage(kText, &message);
   connector0.Accept(&message);
 
   base::RunLoop run_loop;
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
index 066d108..792af21 100644
--- a/mojo/public/cpp/bindings/tests/validation_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -155,7 +155,7 @@
   message->Initialize(static_cast<uint32_t>(data.size()),
                       false /* zero_initialized */);
   if (!data.empty())
-    memcpy(message->buffer()->Allocate(data.size()), &data[0], data.size());
+    memcpy(message->mutable_data(), &data[0], data.size());
   message->mutable_handles()->resize(num_handles);
 
   return true;
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
index 0aef01d..64869f3b 100644
--- a/mojo/public/cpp/system/BUILD.gn
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -9,6 +9,7 @@
     "data_pipe.h",
     "functions.h",
     "handle.h",
+    "message.h",
     "message_pipe.h",
     "watcher.cc",
     "watcher.h",
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h
index 519bcc1e..f83a922 100644
--- a/mojo/public/cpp/system/handle.h
+++ b/mojo/public/cpp/system/handle.h
@@ -124,11 +124,8 @@
 
  private:
   void CloseIfNecessary() {
-    if (!handle_.is_valid())
-      return;
-    MojoResult result = MojoClose(handle_.value());
-    ALLOW_UNUSED_LOCAL(result);
-    DCHECK_EQ(MOJO_RESULT_OK, result);
+    if (handle_.is_valid())
+      handle_.Close();
   }
 
   HandleType handle_;
@@ -162,6 +159,13 @@
   MojoHandle* mutable_value() { return &value_; }
   void set_value(MojoHandle value) { value_ = value; }
 
+  void Close() {
+    DCHECK(is_valid());
+    MojoResult result = MojoClose(value_);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+
  private:
   MojoHandle value_;
 
diff --git a/mojo/public/cpp/system/message.cc b/mojo/public/cpp/system/message.cc
new file mode 100644
index 0000000..09d8d46e
--- /dev/null
+++ b/mojo/public/cpp/system/message.cc
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium 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 "mojo/public/cpp/system/message.h"
+
+namespace mojo {
+
+ScopedMessageHandle::~ScopedMessageHandle() {
+
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
new file mode 100644
index 0000000..f9002e4c
--- /dev/null
+++ b/mojo/public/cpp/system/message.h
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium 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 MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
+
+#include <limits>
+
+#include "base/macros.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+const MojoMessageHandle kInvalidMessageHandleValue =
+    MOJO_MESSAGE_HANDLE_INVALID;
+
+// Handle wrapper base class for a |MojoMessageHandle|.
+class MessageHandle {
+ public:
+  MessageHandle() : value_(kInvalidMessageHandleValue) {}
+  explicit MessageHandle(MojoMessageHandle value) : value_(value) {}
+  ~MessageHandle() {}
+
+  void swap(MessageHandle& other) {
+    MojoMessageHandle temp = value_;
+    value_ = other.value_;
+    other.value_ = temp;
+  }
+
+  bool is_valid() const { return value_ != kInvalidMessageHandleValue; }
+
+  const MojoMessageHandle& value() const { return value_; }
+  MojoMessageHandle* mutable_value() { return &value_; }
+  void set_value(MojoMessageHandle value) { value_ = value; }
+
+  void Close() {
+    DCHECK(is_valid());
+    MojoResult result = MojoFreeMessage(value_);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+
+ private:
+  MojoMessageHandle value_;
+};
+
+using ScopedMessageHandle = ScopedHandleBase<MessageHandle>;
+
+inline MojoResult AllocMessage(size_t num_bytes,
+                               const MojoHandle* handles,
+                               size_t num_handles,
+                               MojoAllocMessageFlags flags,
+                               ScopedMessageHandle* handle) {
+  DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
+  DCHECK_LE(num_handles, std::numeric_limits<uint32_t>::max());
+  MojoMessageHandle raw_handle;
+  MojoResult rv = MojoAllocMessage(static_cast<uint32_t>(num_bytes), handles,
+                                   static_cast<uint32_t>(num_handles), flags,
+                                   &raw_handle);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  handle->reset(MessageHandle(raw_handle));
+  return MOJO_RESULT_OK;
+}
+
+inline MojoResult GetMessageBuffer(MessageHandle message, void** buffer) {
+  DCHECK(message.is_valid());
+  return MojoGetMessageBuffer(message.value(), buffer);
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
index a56021d7..5ba9666e 100644
--- a/mojo/public/cpp/system/message_pipe.h
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -18,6 +18,7 @@
 #include "base/logging.h"
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/message.h"
 
 namespace mojo {
 
@@ -88,6 +89,33 @@
       message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
 }
 
+// Writes to a message pipe. Takes ownership of |message| and any attached
+// handles.
+inline MojoResult WriteMessageNew(MessagePipeHandle message_pipe,
+                                  ScopedMessageHandle message,
+                                  MojoWriteMessageFlags flags) {
+  return MojoWriteMessageNew(
+      message_pipe.value(), message.release().value(), flags);
+}
+
+// Reads from a message pipe. See |MojoReadMessageNew()| for complete
+// documentation.
+inline MojoResult ReadMessageNew(MessagePipeHandle message_pipe,
+                                 ScopedMessageHandle* message,
+                                 uint32_t* num_bytes,
+                                 MojoHandle* handles,
+                                 uint32_t* num_handles,
+                                 MojoReadMessageFlags flags) {
+  MojoMessageHandle raw_message;
+  MojoResult rv = MojoReadMessageNew(message_pipe.value(), &raw_message,
+                                     num_bytes, handles, num_handles, flags);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  message->reset(MessageHandle(raw_message));
+  return MOJO_RESULT_OK;
+}
+
 // Fuses two message pipes together at the given handles. See
 // |MojoFuseMessagePipes()| for complete documentation.
 inline MojoResult FuseMessagePipes(ScopedMessagePipeHandle message_pipe0,
diff --git a/mojo/tools/android_mojo_shell.py b/mojo/tools/android_mojo_shell.py
deleted file mode 100755
index 5ba34fb..0000000
--- a/mojo/tools/android_mojo_shell.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-# 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 argparse
-import logging
-import sys
-
-from mopy.android import AndroidShell
-from mopy.config import Config
-
-USAGE = ('android_mojo_shell.py [<shell-and-app-args>] [<mojo-app>]')
-
-def main():
-  logging.basicConfig()
-
-  parser = argparse.ArgumentParser(usage=USAGE)
-
-  debug_group = parser.add_mutually_exclusive_group()
-  debug_group.add_argument('--debug', help='Debug build (default)',
-                           default=True, action='store_true')
-  debug_group.add_argument('--release', help='Release build', default=False,
-                           dest='debug', action='store_false')
-  parser.add_argument('--target-cpu', help='CPU architecture to run for.',
-                      choices=['x64', 'x86', 'arm'], default='arm')
-  parser.add_argument('--device', help='Serial number of the target device.')
-  parser.add_argument('--verbose', default=False, action='store_true')
-  parser.add_argument('--apk', help='Name of the APK to run.',
-                      default='MojoRunner.apk')
-  runner_args, args = parser.parse_known_args()
-
-  logger = logging.getLogger()
-  logging.basicConfig(stream=sys.stdout, format='%(levelname)s:%(message)s')
-  logger.setLevel(logging.DEBUG if runner_args.verbose else logging.WARNING)
-  logger.debug('Initialized logging: level=%s' % logger.level)
-
-  config = Config(target_os=Config.OS_ANDROID,
-                  target_cpu=runner_args.target_cpu,
-                  is_debug=runner_args.debug,
-                  is_verbose=runner_args.verbose,
-                  apk_name=runner_args.apk)
-  shell = AndroidShell(config)
-  shell.InitShell(runner_args.device)
-  p = shell.ShowLogs()
-  shell.StartActivity('MojoShellActivity', args, sys.stdout, p.terminate)
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/mojo/tools/apptest_runner.py b/mojo/tools/apptest_runner.py
deleted file mode 100755
index 7fa0aba2..0000000
--- a/mojo/tools/apptest_runner.py
+++ /dev/null
@@ -1,193 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-'''A test runner for gtest application tests.'''
-
-import argparse
-import json
-import logging
-import os
-import string
-import sys
-import time
-
-from mopy import gtest
-from mopy.config import Config
-
-
-APPTESTS = os.path.abspath(os.path.join(__file__, '..', 'data', 'apptests'))
-
-
-def main():
-  parser = argparse.ArgumentParser(description='An application test runner.')
-  parser.add_argument('build_dir', type=str, help='The build output directory.')
-  parser.add_argument('--verbose', default=False, action='store_true',
-                      help='Print additional logging information.')
-  parser.add_argument('--repeat-count', default=1, metavar='INT',
-                      action='store', type=int,
-                      help='The number of times to repeat the set of tests.')
-  parser.add_argument('--write-full-results-to', metavar='FILENAME',
-                      help='The path to write the JSON list of full results.')
-  parser.add_argument('--test-launcher-summary-output', metavar='FILENAME',
-                      help='The path to write the JSON list of full results.')
-  parser.add_argument('--test-list-file', metavar='FILENAME', type=file,
-                      default=APPTESTS, help='The file listing tests to run.')
-  parser.add_argument('--apptest-filter', default='',
-                      help='A comma-separated list of mojo:apptests to run.')
-  args, commandline_args = parser.parse_known_args()
-
-  logger = logging.getLogger()
-  logging.basicConfig(stream=sys.stdout, format='%(levelname)s:%(message)s')
-  logger.setLevel(logging.DEBUG if args.verbose else logging.WARNING)
-  logger.debug('Initialized logging: level=%s' % logger.level)
-
-  logger.debug('Test list file: %s', args.test_list_file)
-  config = Config(args.build_dir, is_verbose=args.verbose,
-                  apk_name='MojoRunnerApptests.apk')
-  execution_globals = {'config': config}
-  exec args.test_list_file in execution_globals
-  test_list = execution_globals['tests']
-  logger.debug('Test list: %s' % test_list)
-
-  shell = None
-  if config.target_os == Config.OS_ANDROID:
-    from mopy.android import AndroidShell
-    shell = AndroidShell(config)
-    result = shell.InitShell()
-    if result != 0:
-      return result
-
-  tests = []
-  failed = []
-  failed_suites = 0
-  apptest_filter = [a for a in string.split(args.apptest_filter, ',') if a]
-  gtest_filter = [a for a in commandline_args if a.startswith('--gtest_filter')]
-  for _ in range(args.repeat_count):
-    for test_dict in test_list:
-      test = test_dict['test']
-      test_name = test_dict.get('name', test)
-      test_type = test_dict.get('type', 'gtest')
-      test_args = test_dict.get('args', []) + commandline_args
-      if apptest_filter and not set(apptest_filter) & set([test, test_name]):
-        continue;
-
-      print 'Running %s...%s' % (test_name, ('\n' if args.verbose else '')),
-      sys.stdout.flush()
-
-      assert test_type in ('gtest', 'gtest_isolated')
-      if test_type == 'gtest':
-        print ('WARNING: tests are forced to gtest_isolated until '
-               'http://crbug.com/529487 is fixed')
-        test_type = 'gtest_isolated'
-      isolate = test_type == 'gtest_isolated'
-      (ran, fail) = gtest.run_apptest(config, shell, test_args, test, isolate)
-      # Ignore empty fixture lists when the commandline has a gtest filter flag.
-      if gtest_filter and not ran and not fail:
-        print '[ NO TESTS ] ' + (test_name if args.verbose else '')
-        continue
-      # Use the apptest name if the whole suite failed or no fixtures were run.
-      fail = [test_name] if (not ran and (not fail or fail == [test])) else fail
-      tests.extend(ran)
-      failed.extend(fail)
-      result = ran and not fail
-      print '[  PASSED  ]' if result else '[  FAILED  ]',
-      print test_name if args.verbose or not result else ''
-      # Abort when 3 apptest suites, or a tenth of all, have failed.
-      # base::TestLauncher does this for timeouts and unknown results.
-      failed_suites += 0 if result else 1
-      if failed_suites >= max(3, len(test_list) / 10):
-        print 'Too many failing suites (%d), exiting now.' % failed_suites
-        failed.append('Test runner aborted for excessive failures.')
-        break;
-
-    if failed:
-      break;
-
-  print '[==========] %d tests ran.' % len(tests)
-  print '[  PASSED  ] %d tests.' % (len(tests) - len(failed))
-  if failed:
-    print '[  FAILED  ] %d tests, listed below:' % len(failed)
-    for failure in failed:
-      print '[  FAILED  ] %s' % failure
-
-  if args.write_full_results_to:
-    _WriteJSONResults(tests, failed, args.write_full_results_to)
-  if args.test_launcher_summary_output:
-    _WriteSwarmingJSONResults(tests, failed, args.test_launcher_summary_output)
-
-  return 1 if failed else 0
-
-
-def _WriteJSONResults(tests, failed, write_full_results_to):
-  '''Write the apptest results in the Chromium JSON test results format.
-     See <http://www.chromium.org/developers/the-json-test-results-format>
-     TODO(msw): Use Chromium and TYP testing infrastructure.
-     TODO(msw): Use GTest Suite.Fixture names, not the apptest names.
-     Adapted from chrome/test/mini_installer/test_installer.py
-  '''
-  results = {
-    'interrupted': False,
-    'path_delimiter': '.',
-    'version': 3,
-    'seconds_since_epoch': time.time(),
-    'num_failures_by_type': {
-      'FAIL': len(failed),
-      'PASS': len(tests) - len(failed),
-    },
-    'tests': {}
-  }
-
-  for test in tests:
-    value = {
-      'expected': 'PASS',
-      'actual': 'FAIL' if test in failed else 'PASS',
-      'is_unexpected': True if test in failed else False,
-    }
-    _AddPathToTrie(results['tests'], test, value)
-
-  with open(write_full_results_to, 'w') as fp:
-    json.dump(results, fp, indent=2)
-    fp.write('\n')
-
-  return results
-
-
-def _AddPathToTrie(trie, path, value):
-  if '.' not in path:
-    trie[path] = value
-    return
-  directory, rest = path.split('.', 1)
-  if directory not in trie:
-    trie[directory] = {}
-  _AddPathToTrie(trie[directory], rest, value)
-
-
-def _WriteSwarmingJSONResults(tests, failed, write_full_results_to):
-  '''Writes the test results in the JSON test result format used by swarming.'''
-  results = {
-    'all_tests': tests,
-    'disabled_tests': [],
-    'global_tags': [],
-  }
-
-  test_results = []
-  for test in sorted(tests):
-    value = [{
-      'status': 'FAILURE' if test in failed else 'SUCCESS',
-      'output_snippet': '',
-      'output_snippet_base64': '',
-    }]
-    test_results.append({test: value})
-  results['per_iteration_data'] = test_results
-
-  with open(write_full_results_to, 'w') as fp:
-    json.dump(results, fp, indent=2)
-    fp.write('\n')
-
-  return results
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests
deleted file mode 100644
index 8e6ceef..0000000
--- a/mojo/tools/data/apptests
+++ /dev/null
@@ -1,47 +0,0 @@
-# This file contains a list of Mojo gtest unit tests.
-#
-# This must be valid Python. It may use the |config| global that will be a
-# mopy.config.Config object, and must set a |tests| global that will contain the
-# list of tests to run.
-#
-# The entries in |tests| are dictionaries of the following form:
-#   {
-#     # Required URL for apptest.
-#     'test': 'mojo:test_app_url',
-#     # Optional display name (otherwise the entry for 'test' above is used).
-#     'name': 'mojo:test_app_url (more details)',
-#     # Optional test type. Valid values:
-#     #   * 'gtest': (default)
-#     #   * 'gtest_isolated': like 'gtest', but run with fixture isolation.
-#     #                       i.e., each test in a fresh mojo_shell
-#     'type': 'gtest',
-#     # Optional arguments for the shell or test.
-#     'args': ['--some-flag-for-the-shell', '--some-flag-for-the-test'],
-#   }
-#
-# TODO(vtl|msw): Add a way of specifying data dependencies.
-
-# WARNING: If you add a test make sure the gn target mojo_apptests depends upon
-# it.
-tests = [
-  # TODO(msw|jam): Fix and enable the runner_apptests: http://crbug.com/479316
-  #{
-  #  'test': 'mojo:runner_apptests',
-  #  'type': 'gtest_isolated',
-  #},
-]
-
-# TODO(msw): Get these tests passing on Android too. http://crbug.com/486220
-if config.target_os != config.OS_ANDROID:
-  tests += [
-    {
-      'test': 'mojo:media_apptests',
-      'type': 'gtest_isolated',
-    },
-    # TODO(media): these tests fail in debug, which has no bot-coverage.
-    # http://crbug.com/587523
-#    {
-#      'test': 'mojo:media_pipeline_integration_apptests',
-#      'type': 'gtest_isolated',
-#    },
-  ]
diff --git a/mojo/tools/mopy/__init__.py b/mojo/tools/mopy/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/mojo/tools/mopy/__init__.py
+++ /dev/null
diff --git a/mojo/tools/mopy/android.py b/mojo/tools/mopy/android.py
deleted file mode 100644
index 2a886f7..0000000
--- a/mojo/tools/mopy/android.py
+++ /dev/null
@@ -1,286 +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 atexit
-import logging
-import os
-import signal
-import subprocess
-import sys
-import threading
-import time
-
-from .paths import Paths
-
-sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)),
-                             '..', '..', '..', 'build', 'android'))
-from pylib import constants
-from pylib.device import device_errors
-from pylib.device import device_utils
-from pylib.utils import base_error
-from pylib.utils import apk_helper
-
-
-# Tags used by the mojo shell application logs.
-LOGCAT_TAGS = [
-    'AndroidHandler',
-    'MojoFileHelper',
-    'MojoMain',
-    'MojoShellActivity',
-    'MojoShellApplication',
-    'chromium',
-]
-
-MAPPING_PREFIX = '--map-origin='
-
-
-def _ExitIfNeeded(process):
-  '''Exits |process| if it is still alive.'''
-  if process.poll() is None:
-    process.kill()
-
-
-class AndroidShell(object):
-  '''
-  Used to set up and run a given mojo shell binary on an Android device.
-  |config| is the mopy.config.Config for the build.
-  '''
-  def __init__(self, config):
-    self.adb_path = constants.GetAdbPath()
-    self.config = config
-    self.paths = Paths(config)
-    self.device = None
-    self.shell_args = []
-    self.target_package = apk_helper.GetPackageName(self.paths.apk_path)
-    self.temp_gdb_dir = None
-    # This is used by decive_utils.Install to check if the apk needs updating.
-    constants.SetOutputDirectory(self.paths.build_dir)
-
-  # TODO(msw): Use pylib's adb_wrapper and device_utils instead.
-  def _CreateADBCommand(self, args):
-    adb_command = [self.adb_path, '-s', self.device.adb.GetDeviceSerial()]
-    adb_command.extend(args)
-    logging.getLogger().debug('Command: %s', ' '.join(adb_command))
-    return adb_command
-
-  def _ReadFifo(self, path, pipe, on_fifo_closed, max_attempts=5):
-    '''
-    Reads the fifo at |path| on the device and write the contents to |pipe|.
-    Calls |on_fifo_closed| when the fifo is closed. This method will try to find
-    the path up to |max_attempts|, waiting 1 second between each attempt. If it
-    cannot find |path|, a exception will be raised.
-    '''
-    def Run():
-      def _WaitForFifo():
-        for _ in xrange(max_attempts):
-          if self.device.FileExists(path):
-            return
-          time.sleep(1)
-        on_fifo_closed()
-        raise Exception('Unable to find fifo: %s' % path)
-      _WaitForFifo()
-      stdout_cat = subprocess.Popen(self._CreateADBCommand([
-                                      'shell',
-                                      'cat',
-                                      path]),
-                                    stdout=pipe)
-      atexit.register(_ExitIfNeeded, stdout_cat)
-      stdout_cat.wait()
-      on_fifo_closed()
-
-    thread = threading.Thread(target=Run, name='StdoutRedirector')
-    thread.start()
-
-
-  def InitShell(self, device=None):
-    '''
-    Runs adb as root, and installs the apk as needed.  |device| is the target
-    device to run on, if multiple devices are connected. Returns 0 on success or
-    a non-zero exit code on a terminal failure.
-    '''
-    try:
-      devices = device_utils.DeviceUtils.HealthyDevices()
-      if device:
-        self.device = next((d for d in devices if d == device), None)
-        if not self.device:
-          raise device_errors.DeviceUnreachableError(device)
-      elif devices:
-        self.device = devices[0]
-      else:
-        raise device_errors.NoDevicesError()
-
-      logging.getLogger().debug('Using device: %s', self.device)
-      # Clean the logs on the device to avoid displaying prior activity.
-      subprocess.check_call(self._CreateADBCommand(['logcat', '-c']))
-      self.device.EnableRoot()
-      self.device.Install(self.paths.apk_path)
-    except base_error.BaseError as e:
-      # Report 'device not found' as infra failures. See http://crbug.com/493900
-      print 'Exception in AndroidShell.InitShell:\n%s' % str(e)
-      if e.is_infra_error or 'error: device not found' in str(e):
-        return constants.INFRA_EXIT_CODE
-      return constants.ERROR_EXIT_CODE
-
-    return 0
-
-  def _GetProcessId(self, process):
-    '''Returns the process id of the process on the remote device.'''
-    while True:
-      line = process.stdout.readline()
-      pid_command = 'launcher waiting for GDB. pid: '
-      index = line.find(pid_command)
-      if index != -1:
-        return line[index + len(pid_command):].strip()
-    return 0
-
-  def _GetLocalGdbPath(self):
-    '''Returns the path to the android gdb.'''
-    if self.config.target_cpu == 'arm':
-      return os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains',
-                          'arm-linux-androideabi-4.9', 'prebuilt',
-                          'linux-x86_64', 'bin', 'arm-linux-androideabi-gdb')
-    elif self.config.target_cpu == 'x86':
-      return os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains',
-                          'x86-4.9', 'prebuilt', 'linux-x86_64', 'bin',
-                          'i686-linux-android-gdb')
-    elif self.config.target_cpu == 'x64':
-      return os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains',
-                          'x86_64-4.9', 'prebuilt', 'linux-x86_64', 'bin',
-                          'x86_64-linux-android-gdb')
-    else:
-      raise Exception('Unknown target_cpu: %s' % self.config.target_cpu)
-
-  def _WaitForProcessIdAndStartGdb(self, process):
-    '''
-    Waits until we see the process id from the remote device, starts up
-    gdbserver on the remote device, and gdb on the local device.
-    '''
-    # Wait until we see 'PID'
-    pid = self._GetProcessId(process)
-    assert pid != 0
-    # No longer need the logcat process.
-    process.kill()
-    # Disable python's processing of SIGINT while running gdb. Otherwise
-    # control-c doesn't work well in gdb.
-    signal.signal(signal.SIGINT, signal.SIG_IGN)
-    gdbserver_process = subprocess.Popen(self._CreateADBCommand(['shell',
-                                                                 'gdbserver',
-                                                                 '--attach',
-                                                                 ':5039',
-                                                                 pid]))
-    atexit.register(_ExitIfNeeded, gdbserver_process)
-
-    gdbinit_path = os.path.join(self.temp_gdb_dir, 'gdbinit')
-    _CreateGdbInit(self.temp_gdb_dir, gdbinit_path, self.paths.build_dir)
-
-    # Wait a second for gdb to start up on the device. Without this the local
-    # gdb starts before the remote side has registered the port.
-    # TODO(sky): maybe we should try a couple of times and then give up?
-    time.sleep(1)
-
-    local_gdb_process = subprocess.Popen([self._GetLocalGdbPath(),
-                                          '-x',
-                                          gdbinit_path],
-                                         cwd=self.temp_gdb_dir)
-    atexit.register(_ExitIfNeeded, local_gdb_process)
-    local_gdb_process.wait()
-    signal.signal(signal.SIGINT, signal.SIG_DFL)
-
-  def StartActivity(self,
-                    activity_name,
-                    arguments,
-                    stdout,
-                    on_fifo_closed,
-                    temp_gdb_dir=None):
-    '''
-    Starts the shell with the given |arguments|, directing output to |stdout|.
-    |on_fifo_closed| will be run if the FIFO can't be found or when it's closed.
-    |temp_gdb_dir| is set to a location with appropriate symlinks for gdb to
-    find when attached to the device's remote process on startup.
-    '''
-    assert self.device
-    arguments += self.shell_args
-
-    cmd = self._CreateADBCommand([
-           'shell',
-           'am',
-           'start',
-           '-S',
-           '-a', 'android.intent.action.VIEW',
-           '-n', '%s/%s.%s' % (self.target_package,
-                               self.target_package,
-                               activity_name)])
-
-    logcat_process = None
-    if temp_gdb_dir:
-      self.temp_gdb_dir = temp_gdb_dir
-      arguments.append('--wait-for-debugger')
-      # Remote debugging needs a port forwarded.
-      self.device.adb.Forward('tcp:5039', 'tcp:5039')
-      logcat_process = self.ShowLogs(stdout=subprocess.PIPE)
-
-    fifo_path = '/data/data/%s/stdout.fifo' % self.target_package
-    subprocess.check_call(self._CreateADBCommand(
-        ['shell', 'rm', '-f', fifo_path]))
-    arguments.append('--fifo-path=%s' % fifo_path)
-    max_attempts = 200 if '--wait-for-debugger' in arguments else 5
-    self._ReadFifo(fifo_path, stdout, on_fifo_closed, max_attempts)
-
-    # Extract map-origin args and add the extras array with commas escaped.
-    parameters = [a for a in arguments if not a.startswith(MAPPING_PREFIX)]
-    parameters = [p.replace(',', '\,') for p in parameters]
-    cmd += ['--esa', '%s.extras' % self.target_package, ','.join(parameters)]
-
-    atexit.register(self.kill)
-    with open(os.devnull, 'w') as devnull:
-      cmd_process = subprocess.Popen(cmd, stdout=devnull)
-      if logcat_process:
-        self._WaitForProcessIdAndStartGdb(logcat_process)
-      cmd_process.wait()
-
-  def kill(self):
-    '''Stops the mojo shell; matches the Popen.kill method signature.'''
-    self.device.ForceStop(self.target_package)
-
-  def ShowLogs(self, stdout=sys.stdout):
-    '''Displays the mojo shell logs and returns the process reading the logs.'''
-    logcat = subprocess.Popen(self._CreateADBCommand([
-                               'logcat',
-                               '-s',
-                               ' '.join(LOGCAT_TAGS)]),
-                              stdout=stdout)
-    atexit.register(_ExitIfNeeded, logcat)
-    return logcat
-
-
-def _CreateGdbInit(tmp_dir, gdb_init_path, build_dir):
-  '''
-  Creates the gdbinit file.
-
-  Args:
-    tmp_dir: the directory where the gdbinit and other files lives.
-    gdb_init_path: path to gdbinit
-    build_dir: path where build files are located.
-  '''
-  gdbinit = ('target remote localhost:5039\n'
-             'def reload-symbols\n'
-             '  set solib-search-path %s:%s\n'
-             'end\n'
-             'def info-symbols\n'
-             '  info sharedlibrary\n'
-             'end\n'
-             'reload-symbols\n'
-             'echo \\n\\n'
-             'You are now in gdb and need to type continue (or c) to continue '
-             'execution.\\n'
-             'gdb is in the directory %s\\n'
-             'The following functions have been defined:\\n'
-             'reload-symbols: forces reloading symbols. If after a crash you\\n'
-             'still do not see symbols you likely need to create a link in\\n'
-             'the directory you are in.\\n'
-             'info-symbols: shows status of current shared libraries.\\n'
-             'NOTE: you may need to type reload-symbols again after a '
-             'crash.\\n\\n' % (tmp_dir, build_dir, tmp_dir))
-  with open(gdb_init_path, 'w') as f:
-    f.write(gdbinit)
diff --git a/mojo/tools/mopy/config.py b/mojo/tools/mopy/config.py
deleted file mode 100644
index 0a50f3f..0000000
--- a/mojo/tools/mopy/config.py
+++ /dev/null
@@ -1,140 +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 ast
-import os.path
-import platform
-import re
-import sys
-
-
-class Config(object):
-  '''A Config contains a dictionary that species a build configuration.'''
-
-  # Valid values for target_os:
-  OS_ANDROID = 'android'
-  OS_CHROMEOS = 'chromeos'
-  OS_LINUX = 'linux'
-  OS_MAC = 'mac'
-  OS_WINDOWS = 'windows'
-
-  # Valid values for target_cpu:
-  ARCH_X86 = 'x86'
-  ARCH_X64 = 'x64'
-  ARCH_ARM = 'arm'
-
-  def __init__(self, build_dir=None, target_os=None, target_cpu=None,
-               is_debug=None, is_verbose=None, apk_name='MojoRunner.apk'):
-    '''Function arguments take precedence over GN args and default values.'''
-    assert target_os in (None, Config.OS_ANDROID, Config.OS_CHROMEOS,
-                         Config.OS_LINUX, Config.OS_MAC, Config.OS_WINDOWS)
-    assert target_cpu in (None, Config.ARCH_X86, Config.ARCH_X64,
-                          Config.ARCH_ARM)
-    assert is_debug in (None, True, False)
-    assert is_verbose in (None, True, False)
-
-    self.values = {
-      'build_dir': build_dir,
-      'target_os': self.GetHostOS(),
-      'target_cpu': self.GetHostCPU(),
-      'is_debug': True,
-      'is_verbose': True,
-      'dcheck_always_on': False,
-      'is_asan': False,
-      'apk_name': apk_name,
-    }
-
-    self._ParseGNArgs()
-    if target_os is not None:
-      self.values['target_os'] = target_os
-    if target_cpu is not None:
-      self.values['target_cpu'] = target_cpu
-    if is_debug is not None:
-      self.values['is_debug'] = is_debug
-    if is_verbose is not None:
-      self.values['is_verbose'] = is_verbose
-
-  @staticmethod
-  def GetHostOS():
-    if sys.platform == 'linux2':
-      return Config.OS_LINUX
-    if sys.platform == 'darwin':
-      return Config.OS_MAC
-    if sys.platform == 'win32':
-      return Config.OS_WINDOWS
-    raise NotImplementedError('Unsupported host OS')
-
-  @staticmethod
-  def GetHostCPU():
-    # Derived from //native_client/pynacl/platform.py
-    machine = platform.machine()
-    if machine in ('x86', 'x86-32', 'x86_32', 'x8632', 'i386', 'i686', 'ia32',
-                   '32'):
-      return Config.ARCH_X86
-    if machine in ('x86-64', 'amd64', 'AMD64', 'x86_64', 'x8664', '64'):
-      return Config.ARCH_X64
-    if machine.startswith('arm'):
-      return Config.ARCH_ARM
-    raise Exception('Cannot identify CPU arch: %s' % machine)
-
-  def _ParseGNArgs(self):
-    '''Parse the gn config file from the build directory, if it exists.'''
-    TRANSLATIONS = { 'true': 'True', 'false': 'False', }
-    if self.values['build_dir'] is None:
-      return
-    gn_file = os.path.join(self.values['build_dir'], 'args.gn')
-    if not os.path.isfile(gn_file):
-      return
-
-    with open(gn_file, 'r') as f:
-      for line in f:
-        line = re.sub('\s*#.*', '', line)
-        result = re.match('^\s*(\w+)\s*=\s*(.*)\s*$', line)
-        if result:
-          key = result.group(1)
-          value = result.group(2)
-          self.values[key] = ast.literal_eval(TRANSLATIONS.get(value, value))
-
-  # Getters for standard fields ------------------------------------------------
-
-  @property
-  def build_dir(self):
-    '''Build directory path.'''
-    return self.values['build_dir']
-
-  @property
-  def target_os(self):
-    '''OS of the build/test target.'''
-    return self.values['target_os']
-
-  @property
-  def target_cpu(self):
-    '''CPU arch of the build/test target.'''
-    return self.values['target_cpu']
-
-  @property
-  def is_debug(self):
-    '''Is Debug build?'''
-    return self.values['is_debug']
-
-  @property
-  def is_verbose(self):
-    '''Should print additional logging information?'''
-    return self.values['is_verbose']
-
-  @property
-  def dcheck_always_on(self):
-    '''DCHECK is fatal even in release builds'''
-    return self.values['dcheck_always_on']
-
-  @property
-  def is_asan(self):
-    '''Is ASAN build?'''
-    return self.values['is_asan']
-
-  @property
-  def apk_name(self):
-    '''Name of the APK file to run'''
-    return self.values['apk_name']
diff --git a/mojo/tools/mopy/gtest.py b/mojo/tools/mopy/gtest.py
deleted file mode 100644
index 95b5d004..0000000
--- a/mojo/tools/mopy/gtest.py
+++ /dev/null
@@ -1,277 +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 logging
-import os
-import Queue
-import re
-import subprocess
-import sys
-import threading
-import time
-
-from mopy.config import Config
-from mopy.paths import Paths
-
-THIS_DIR = os.path.dirname(os.path.abspath(__file__))
-
-sys.path.append(os.path.join(THIS_DIR, '..', '..', '..', 'testing'))
-import xvfb
-
-sys.path.append(os.path.join(THIS_DIR, '..', '..', '..', 'tools',
-                             'swarming_client', 'utils'))
-import subprocess42
-
-# The DISPLAY ID number used for xvfb, incremented with each use.
-XVFB_DISPLAY_ID = 9
-
-
-def run_apptest(config, shell, args, apptest, isolate):
-  '''Run the apptest; optionally isolating fixtures across shell invocations.
-
-  Returns the list of test fixtures run and the list of failed test fixtures.
-  TODO(msw): Also return the list of DISABLED test fixtures.
-
-  Args:
-    config: The mopy.config.Config for the build.
-    shell: The mopy.android.AndroidShell, if Android is the target platform.
-    args: The arguments for the shell or apptest.
-    apptest: The application test URL.
-    isolate: True if the test fixtures should be run in isolation.
-  '''
-  if not isolate:
-    return _run_apptest_with_retry(config, shell, args, apptest)
-
-  fixtures = _get_fixtures(config, shell, args, apptest)
-  fixtures = [f for f in fixtures if not '.DISABLED_' in f]
-  failed = []
-  for fixture in fixtures:
-    arguments = args + ['--gtest_filter=%s' % fixture]
-    failures = _run_apptest_with_retry(config, shell, arguments, apptest)[1]
-    failed.extend(failures if failures != [apptest] else [fixture])
-    # Abort when 20 fixtures, or a tenth of the apptest fixtures, have failed.
-    # base::TestLauncher does this for timeouts and unknown results.
-    if len(failed) >= max(20, len(fixtures) / 10):
-      print 'Too many failing fixtures (%d), exiting now.' % len(failed)
-      return (fixtures, failed + [apptest + ' aborted for excessive failures.'])
-  return (fixtures, failed)
-
-
-# TODO(msw): Determine proper test retry counts; allow configuration.
-def _run_apptest_with_retry(config, shell, args, apptest, retry_count=2):
-  '''Runs an apptest, retrying on failure; returns the fixtures and failures.'''
-  (tests, failed) = _run_apptest(config, shell, args, apptest)
-  while failed and retry_count:
-    print 'Retrying failed tests (%d attempts remaining)' % retry_count
-    arguments = args
-    # Retry only the failing fixtures if there is no existing filter specified.
-    if (failed and ':'.join(failed) is not apptest and
-        not any(a.startswith('--gtest_filter') for a in args)):
-      arguments += ['--gtest_filter=%s' % ':'.join(failed)]
-    failed = _run_apptest(config, shell, arguments, apptest)[1]
-    retry_count -= 1
-  return (tests, failed)
-
-
-def _run_apptest(config, shell, args, apptest):
-  '''Runs an apptest; returns the list of fixtures and the list of failures.'''
-  command = _build_command_line(config, args, apptest)
-  logging.getLogger().debug('Command: %s' % ' '.join(command))
-  start_time = time.time()
-
-  try:
-    out = _run_test_with_xvfb(config, shell, args, apptest)
-  except Exception as e:
-    _print_exception(command, e, int(round(1000 * (time.time() - start_time))))
-    return ([apptest], [apptest])
-
-  # Find all fixtures begun from gtest's '[ RUN      ] <Suite.Fixture>' output.
-  tests = [x for x in out.split('\n') if x.find('[ RUN      ] ') != -1]
-  tests = [x.strip(' \t\n\r')[x.find('[ RUN      ] ') + 13:] for x in tests]
-  tests = tests or [apptest]
-
-  # Fail on output with gtest's '[  FAILED  ]' or a lack of '[       OK ]'.
-  # The latter check ensures failure on broken command lines, hung output, etc.
-  # Check output instead of exit codes because mojo shell always exits with 0.
-  failed = [x for x in tests if (re.search('\[  FAILED  \].*' + x, out) or
-                                 not re.search('\[       OK \].*' + x, out))]
-
-  ms = int(round(1000 * (time.time() - start_time)))
-  if failed:
-    _print_exception(command, out, ms)
-  else:
-    logging.getLogger().debug('Passed (in %d ms) with output:\n%s' % (ms, out))
-  return (tests, failed)
-
-
-def _get_fixtures(config, shell, args, apptest):
-  '''Returns an apptest's 'Suite.Fixture' list via --gtest_list_tests output.'''
-  arguments = args + ['--gtest_list_tests']
-  command = _build_command_line(config, arguments, apptest)
-  logging.getLogger().debug('Command: %s' % ' '.join(command))
-  try:
-    tests = _run_test_with_xvfb(config, shell, arguments, apptest)
-    # Remove log lines from the output and ensure it matches known formatting.
-    # Ignore empty fixture lists when the command line has a gtest filter flag.
-    tests = re.sub('^(\[|WARNING: linker:).*\n', '', tests, flags=re.MULTILINE)
-    if (not re.match('^(\w*\.\r?\n(  \w*\r?\n)+)+', tests) and
-        not [a for a in args if a.startswith('--gtest_filter')]):
-      raise Exception('Unrecognized --gtest_list_tests output:\n%s' % tests)
-    test_list = []
-    for line in tests.split('\n'):
-      if not line:
-        continue
-      if line[0] != ' ':
-        suite = line.strip()
-        continue
-      test_list.append(suite + line.strip())
-    logging.getLogger().debug('Tests for %s: %s' % (apptest, test_list))
-    return test_list
-  except Exception as e:
-    _print_exception(command, e)
-  return []
-
-
-def _print_exception(command_line, exception, milliseconds=None):
-  '''Print a formatted exception raised from a failed command execution.'''
-  details = (' (in %d ms)' % milliseconds) if milliseconds else ''
-  if hasattr(exception, 'returncode'):
-    details += ' (with exit code %d)' % exception.returncode
-  print '\n[  FAILED  ] Command%s: %s' % (details, ' '.join(command_line))
-  print 72 * '-'
-  if hasattr(exception, 'output'):
-    print exception.output
-  print str(exception)
-  print 72 * '-'
-
-
-def _build_command_line(config, args, apptest):
-  '''Build the apptest command line. This value isn't executed on Android.'''
-  not_list_tests = not '--gtest_list_tests' in args
-  data_dir = ['--use-temporary-user-data-dir'] if not_list_tests else []
-  return Paths(config).mojo_runner + data_dir + args + [apptest]
-
-
-def _run_test_with_xvfb(config, shell, args, apptest):
-  '''Run the test with xvfb; return the output or raise an exception.'''
-  env = os.environ.copy()
-  # Make sure gtest doesn't try to add color to the output. Color is done via
-  # escape sequences which confuses the code that searches the gtest output.
-  env['GTEST_COLOR'] = 'no'
-  if (config.target_os != Config.OS_LINUX or '--gtest_list_tests' in args
-      or not xvfb.should_start_xvfb(env)):
-    return _run_test_with_timeout(config, shell, args, apptest, env)
-
-  try:
-    # Simply prepending xvfb.py to the command line precludes direct control of
-    # test subprocesses, and prevents easily getting output when tests timeout.
-    xvfb_proc = None
-    openbox_proc = None
-    global XVFB_DISPLAY_ID
-    display_string = ':' + str(XVFB_DISPLAY_ID)
-    (xvfb_proc, openbox_proc) = xvfb.start_xvfb(env, Paths(config).build_dir,
-                                                display=display_string)
-    XVFB_DISPLAY_ID = (XVFB_DISPLAY_ID + 1) % 50000
-    if not xvfb_proc or not xvfb_proc.pid:
-      raise Exception('Xvfb failed to start; aborting test run.')
-    if not openbox_proc or not openbox_proc.pid:
-      raise Exception('Openbox failed to start; aborting test run.')
-    logging.getLogger().debug('Running Xvfb %s (pid %d) and Openbox (pid %d).' %
-                              (display_string, xvfb_proc.pid, openbox_proc.pid))
-    return _run_test_with_timeout(config, shell, args, apptest, env)
-  finally:
-    xvfb.kill(xvfb_proc)
-    xvfb.kill(openbox_proc)
-
-
-# TODO(msw): Determine proper test timeout durations (starting small).
-def _run_test_with_timeout(config, shell, args, apptest, env, seconds=10):
-  '''Run the test with a timeout; return the output or raise an exception.'''
-  if config.target_os == Config.OS_ANDROID:
-    return _run_test_with_timeout_on_android(shell, args, apptest, seconds)
-
-  output = ''
-  error = []
-  command = _build_command_line(config, args, apptest)
-  proc = subprocess42.Popen(command, detached=True, stdout=subprocess42.PIPE,
-                            stderr=subprocess42.STDOUT, env=env)
-  try:
-    output = proc.communicate(timeout=seconds)[0] or ''
-    if proc.duration() > seconds:
-      error.append('ERROR: Test timeout with duration: %s.' % proc.duration())
-      raise subprocess42.TimeoutExpired(proc.args, seconds, output, None)
-  except subprocess42.TimeoutExpired as e:
-    output = e.output or ''
-    logging.getLogger().debug('Terminating the test for timeout.')
-    error.append('ERROR: Test timeout after %d seconds.' % proc.duration())
-    proc.terminate()
-    try:
-      output += proc.communicate(timeout=30)[0] or ''
-    except subprocess42.TimeoutExpired as e:
-      output += e.output or ''
-      logging.getLogger().debug('Test termination failed; attempting to kill.')
-      proc.kill()
-    try:
-      output += proc.communicate(timeout=30)[0] or ''
-    except subprocess42.TimeoutExpired as e:
-      output += e.output or ''
-      logging.getLogger().debug('Failed to kill the test process!')
-
-  if proc.returncode:
-    error.append('ERROR: Test exited with code: %d.' % proc.returncode)
-  elif proc.returncode is None:
-    error.append('ERROR: Failed to kill the test process!')
-
-  if not output:
-    error.append('ERROR: Test exited with no output.')
-  elif output.startswith('This program contains tests'):
-    error.append('ERROR: GTest printed help; check the command line.')
-
-  if error:
-    raise Exception(output + '\n'.join(error))
-  return output
-
-
-def _run_test_with_timeout_on_android(shell, args, apptest, seconds):
-  '''Run the test with a timeout; return the output or raise an exception.'''
-  assert shell
-  result = Queue.Queue()
-  thread = threading.Thread(target=_run_test_on_android,
-                            args=(shell, args, apptest, result))
-  thread.start()
-  thread.join(seconds)
-  timeout_exception = ''
-
-  if thread.is_alive():
-    timeout_exception = '\nERROR: Test timeout after %d seconds.' % seconds
-    logging.getLogger().debug('Killing the Android shell for timeout.')
-    shell.kill()
-    thread.join(seconds)
-
-  if thread.is_alive():
-    raise Exception('ERROR: Failed to kill the test process!')
-  if result.empty():
-    raise Exception('ERROR: Test exited with no output.')
-  (output, exception) = result.get()
-  exception += timeout_exception
-  if exception:
-    raise Exception('%s%s%s' % (output, '\n' if output else '', exception))
-  return output
-
-
-def _run_test_on_android(shell, args, apptest, result):
-  '''Run the test on Android; put output and any exception in |result|.'''
-  output = ''
-  exception = ''
-  try:
-    (r, w) = os.pipe()
-    with os.fdopen(r, 'r') as rf:
-      with os.fdopen(w, 'w') as wf:
-        arguments = args + [apptest]
-        shell.StartActivity('MojoShellActivity', arguments, wf, wf.close)
-        output = rf.read()
-  except Exception as e:
-    output += (e.output + '\n') if hasattr(e, 'output') else ''
-    exception += str(e)
-  result.put((output, exception))
diff --git a/mojo/tools/mopy/paths.py b/mojo/tools/mopy/paths.py
deleted file mode 100644
index 6ed6a906..0000000
--- a/mojo/tools/mopy/paths.py
+++ /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.
-
-import os
-
-from .config import Config
-
-
-SRC_ROOT = os.path.abspath(os.path.join(__file__, '..', '..', '..', '..'))
-
-class Paths(object):
-  '''Provides commonly used paths'''
-
-  def __init__(self, config):
-    '''Generate paths to binary artifacts from a Config object.'''
-    self.src_root = SRC_ROOT
-    self.mojo_dir = os.path.join(self.src_root, 'mojo')
-
-    self.build_dir = config.build_dir
-    if self.build_dir is None:
-      subdir = ''
-      if config.target_os == Config.OS_ANDROID:
-        subdir += 'android_'
-        if config.target_cpu != Config.ARCH_ARM:
-          subdir += config.target_cpu + '_'
-      elif config.target_os == Config.OS_CHROMEOS:
-        subdir += 'chromeos_'
-      subdir += 'Debug' if config.is_debug else 'Release'
-      if config.is_asan:
-        subdir += '_asan'
-      if not(config.is_debug) and config.dcheck_always_on:
-        subdir += '_dcheck'
-      self.build_dir = os.path.join(self.src_root, 'out', subdir)
-
-    self.mojo_runner = [os.path.join(self.build_dir, 'mojo_runner')]
-    if config.target_os == Config.OS_WINDOWS:
-      self.mojo_runner[0] += '.exe'
-    if config.target_os == Config.OS_ANDROID:
-      self.apk_path = os.path.join(self.build_dir, 'apks', config.apk_name)
-      self.mojo_runner = [os.path.join(self.src_root, 'mojo', 'tools',
-                                       'android_mojo_shell.py'),
-                          '--apk', self.apk_path]
-
-  def RelPath(self, path):
-    '''Returns the given path, relative to the current directory.'''
-    return os.path.relpath(path)
-
-  def SrcRelPath(self, path):
-    '''Returns the given path, relative to self.src_root.'''
-    return os.path.relpath(path, self.src_root)
diff --git a/net/BUILD.gn b/net/BUILD.gn
index c56e7fb..3bbbe77 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1704,6 +1704,18 @@
   ]
 }
 
+fuzzer_test("net_data_job_fuzzer") {
+  sources = [
+    "url_request/url_request_data_job_fuzzer.cc",
+  ]
+  deps = [
+    ":net_fuzzer_test_support",
+    ":test_support",
+    "//base",
+    "//net",
+  ]
+}
+
 fuzzer_test("net_mime_sniffer_fuzzer") {
   sources = [
     "base/mime_sniffer_fuzzer.cc",
diff --git a/net/base/fuzzed_data_provider.h b/net/base/fuzzed_data_provider.h
index bd8ddbeb..47a45061 100644
--- a/net/base/fuzzed_data_provider.h
+++ b/net/base/fuzzed_data_provider.h
@@ -51,6 +51,9 @@
   // ConsumeValueInRange(0, 0xFFFF).
   uint16_t ConsumeUint16();
 
+  // Reports the remaining bytes available for fuzzed input.
+  size_t remaining_bytes() { return remaining_data_.length(); }
+
  private:
   base::StringPiece remaining_data_;
 
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index 2b4a7ab..c2f83ebe 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -119,8 +119,8 @@
 // VerifyCertificateChain() verifies a certificate path (chain) based on the
 // rules in RFC 5280.
 //
-// WARNING: This implementation is in progress, and is currently
-// incomplete. DO NOT USE IT unless its limitations are acceptable for your use.
+// WARNING: This implementation is in progress, and is currently incomplete.
+// Consult an OWNER before using it.
 //
 // ---------
 // Inputs
@@ -132,7 +132,8 @@
 //
 //      * cert_chain[0] is the target certificate to verify.
 //      * cert_chain[i+1] holds the certificate that issued cert_chain[i].
-//      * cert_chain[N-1] must have been issued by a trust anchor
+//      * cert_chain[N-1] must be the trust anchor, or have been directly
+//        issued by a trust anchor.
 //
 //   trust_store:
 //     Contains the set of trusted public keys (and their names).
diff --git a/net/data/proxy_resolver_v8_tracing_unittest/alert_url.js b/net/data/proxy_resolver_v8_tracing_unittest/alert_url.js
deleted file mode 100644
index 3cc14c94..0000000
--- a/net/data/proxy_resolver_v8_tracing_unittest/alert_url.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2016 The Chromium 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 FindProxyForURL(url, host) {
-  alert(url);
-  return "PROXY foobar:99";
-}
diff --git a/net/data/proxy_resolver_v8_tracing_unittest/dns_depending_on_url.js b/net/data/proxy_resolver_v8_tracing_unittest/dns_depending_on_url.js
deleted file mode 100644
index ac661107..0000000
--- a/net/data/proxy_resolver_v8_tracing_unittest/dns_depending_on_url.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2016 The Chromium 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 proxy script has different DNS dependencies based on whether the URL
-// contains "UseMyIpAddress". The final proxy list however is the same.
-function FindProxyForURL(url, host) {
-  if (url.indexOf("UseMyIpAddress") == -1) {
-    if (!myIpAddress())
-      return null;
-  } else {
-    if (!dnsResolve(host))
-      return null;
-  }
-
-  return "PROXY foopy:47";
-}
diff --git a/net/data/proxy_resolver_v8_tracing_unittest/error_depending_on_url.js b/net/data/proxy_resolver_v8_tracing_unittest/error_depending_on_url.js
deleted file mode 100644
index 42256ebc..0000000
--- a/net/data/proxy_resolver_v8_tracing_unittest/error_depending_on_url.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2016 The Chromium 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 proxy script throws an error if the URL does not contain the substring
-// "DontThrowError".
-function FindProxyForURL(url, host) {
-  if (url.indexOf("DontThrowError") == -1) {
-    ErrorUndefinedFunction();
-    return -1;
-  }
-  return "PROXY foopy:42";
-}
diff --git a/net/data/proxy_resolver_v8_tracing_unittest/return_url_as_proxy.js b/net/data/proxy_resolver_v8_tracing_unittest/return_url_as_proxy.js
deleted file mode 100644
index fad3c85..0000000
--- a/net/data/proxy_resolver_v8_tracing_unittest/return_url_as_proxy.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 The Chromium 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 proxy script returns a proxy list that encodes the URL that was passed
-// in.
-
-function convertUrlToHostname(url) {
-  // Turn the URL into something that resembles a hostname.
-  var result = encodeURIComponent(url);
-  return result.replace(/%/g, "x");
-}
-
-function FindProxyForURL(url, host) {
-  return "PROXY " + convertUrlToHostname(url) + ":99";
-}
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index bfc7922..fcab5069 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -265,15 +265,15 @@
       continue;
 
     // Check whether there is an existing QUIC session to use for this origin.
+    HostPortPair mapped_origin(origin.host(), origin.port());
+    ignore_result(ApplyHostMappingRules(original_url, &mapped_origin));
+    QuicServerId server_id(mapped_origin, request_info.privacy_mode);
+
     HostPortPair destination(alternative_service.host_port_pair());
     ignore_result(ApplyHostMappingRules(original_url, &destination));
-    QuicServerId server_id(destination, request_info.privacy_mode);
 
-    HostPortPair origin_copy(origin.host(), origin.port());
-    ignore_result(ApplyHostMappingRules(original_url, &origin_copy));
-
-    if (session_->quic_stream_factory()->CanUseExistingSession(
-            server_id, origin_copy.host())) {
+    if (session_->quic_stream_factory()->CanUseExistingSession(server_id,
+                                                               destination)) {
       return alternative_service;
     }
 
diff --git a/net/proxy/proxy_resolver_v8_tracing.cc b/net/proxy/proxy_resolver_v8_tracing.cc
index 0708593..1b59d6a 100644
--- a/net/proxy/proxy_resolver_v8_tracing.cc
+++ b/net/proxy/proxy_resolver_v8_tracing.cc
@@ -9,11 +9,9 @@
 #include <utility>
 #include <vector>
 
-#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/cancellation_flag.h"
@@ -29,7 +27,6 @@
 #include "net/proxy/proxy_info.h"
 #include "net/proxy/proxy_resolver_error_observer.h"
 #include "net/proxy/proxy_resolver_v8.h"
-#include "url/url_canon.h"
 
 // The intent of this class is explained in the design document:
 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit
@@ -64,24 +61,6 @@
 // hit this. (In fact normal scripts should not even have alerts() or errors).
 const size_t kMaxAlertsAndErrorsBytes = 2048;
 
-// Strips path information from the URL and replaces it with a
-// recognizable placeholder.
-// TODO(eroman): Remove when done gathering data for crbug.com/593759
-GURL StripUrlForBug593759(const GURL& url) {
-  GURL::Replacements replacements;
-  replacements.SetPathStr("PathIsHiddenForCrbug593759");
-  replacements.ClearQuery();
-  replacements.ClearRef();
-  return url.ReplaceComponents(replacements);
-}
-
-// TODO(eroman): Remove when done gathering data for crbug.com/593759
-void LogHistogramForBug593759(PacResultForStrippedUrl value) {
-  UMA_HISTOGRAM_ENUMERATION(
-      kHistogramPacResultForStrippedUrl, static_cast<int>(value),
-      static_cast<int>(PacResultForStrippedUrl::MAX_VALUE));
-}
-
 // The Job class is responsible for executing GetProxyForURL() and
 // creating ProxyResolverV8 instances, since both of these operations share
 // similar code.
@@ -172,9 +151,6 @@
   void ExecuteNonBlocking();
   int ExecuteProxyResolver();
 
-  // TODO(eroman): Remove when done gathering data for crbug.com/593759
-  void LogMetricsForBug593759(int original_error);
-
   // Implementation of ProxyResolverv8::JSBindings
   bool ResolveDns(const std::string& host,
                   ResolveDnsOperation op,
@@ -301,9 +277,6 @@
   // Whether the current execution needs to be restarted in blocking mode.
   bool should_restart_with_blocking_dns_;
 
-  // TODO(eroman): Remove when done gathering data for crbug.com/593759
-  bool dont_start_dns_ = false;
-
   // ---------------------------------------------------------------------------
   // State for pending DNS request.
   // ---------------------------------------------------------------------------
@@ -562,9 +535,6 @@
 
   int result = ExecuteProxyResolver();
 
-  // TODO(eroman): Remove when done gathering data for crbug.com/593759
-  LogMetricsForBug593759(result);
-
   if (should_restart_with_blocking_dns_) {
     DCHECK(!blocking_dns_);
     DCHECK(abandoned_);
@@ -605,110 +575,6 @@
   return result;
 }
 
-// Gathers data on how often PAC scripts execute differently depending
-// on the URL path and parameters.
-//
-// TODO(eroman): Remove when done gathering data for crbug.com/593759
-void Job::LogMetricsForBug593759(int original_error) {
-  CheckIsOnWorkerThread();
-
-  DCHECK(!blocking_dns_);
-
-  if (operation_ != GET_PROXY_FOR_URL || !url_.SchemeIsCryptographic()) {
-    // Only interested in FindProxyForURL() invocations for cryptographic URL
-    // schemes (https:// and wss://).
-    return;
-  }
-
-  if (should_restart_with_blocking_dns_) {
-    // The current instrumentation is limited to non-blocking DNS mode, for
-    // simplicity. Fallback to blocking mode is possible for unusual PAC
-    // scripts (non-deterministic, or relies on global state).
-    LogHistogramForBug593759(
-        PacResultForStrippedUrl::SKIPPED_FALLBACK_BLOCKING_DNS);
-    return;
-  }
-
-  if (abandoned_) {
-    // If the FindProxyForURL() attempt was abandoned, it was either cancelled
-    // or it encountered a missing DNS dependency. In the latter case the job
-    // will be re-started once the DNS has been resolved, so just wait until
-    // then.
-    return;
-  }
-
-  if (original_error != OK) {
-    // Only run the extra diagnostics for successful invocations of
-    // FindProxyForURL(). In other words, the instrumentation will
-    // not check whether the script succeeds when using a stripped
-    // path after having already failed on the original URL. A script error
-    // means the PAC script is already broken, so this would just skew the data.
-    return;
-  }
-
-  // Save some state variables to compare the original run against the new run
-  // using a stripped URL.
-  auto original_num_dns = num_dns_;
-  auto original_alerts_and_errors_byte_cost_ = alerts_and_errors_byte_cost_;
-  auto original_results = results_.ToPacString();
-
-  // Reset state variables used by ExecuteProxyResolver().
-  //
-  // This is a bit messy, but it keeps the diagnostics code local
-  // to LogMetricsForBug593759() without having to refactor the existing
-  // code.
-  //
-  // The intent is that after returning from this function all of the
-  // internal state is re-set to reflect the original completion of
-  // ExecuteProxyResolver(), not the second diagnostic one.
-  //
-  // Any global modifications made to the script state however are
-  // not undone, since creating a new script context just for this
-  // test would be expensive.
-  //
-  // Lastly, DNS resolution is disabled before calling
-  // ExecuteProxyResolver(), so only previously cached results can be
-  // used.
-  base::AutoReset<GURL> reset_url(&url_, StripUrlForBug593759(url_));
-  base::AutoReset<int> reset_num_dns(&num_dns_, 0);
-  base::AutoReset<std::vector<AlertOrError>> reset_alerts_and_errors(
-      &alerts_and_errors_, std::vector<AlertOrError>());
-  base::AutoReset<size_t> reset_alerts_and_errors_byte_cost(
-      &alerts_and_errors_byte_cost_, 0);
-  base::AutoReset<bool> reset_should_restart_with_blocking_dns(
-      &should_restart_with_blocking_dns_, false);
-  base::AutoReset<ProxyInfo> reset_results(&results_, ProxyInfo());
-  base::AutoReset<bool> reset_dont_start_dns(&dont_start_dns_, true);
-  base::AutoReset<bool> reset_abandoned(&abandoned_, false);
-
-  // Re-run FindProxyForURL().
-  auto result = ExecuteProxyResolver();
-
-  // Log the result of having run FindProxyForURL() with the modified
-  // URL to an UMA histogram.
-  if (should_restart_with_blocking_dns_) {
-    LogHistogramForBug593759(
-        PacResultForStrippedUrl::FAIL_FALLBACK_BLOCKING_DNS);
-  } else if (abandoned_) {
-    LogHistogramForBug593759(PacResultForStrippedUrl::FAIL_ABANDONED);
-  } else if (result != OK) {
-    LogHistogramForBug593759(PacResultForStrippedUrl::FAIL_ERROR);
-  } else if (original_results != results_.ToPacString()) {
-    LogHistogramForBug593759(
-        PacResultForStrippedUrl::FAIL_DIFFERENT_PROXY_LIST);
-  } else if (original_alerts_and_errors_byte_cost_ !=
-             alerts_and_errors_byte_cost_) {
-    // Here alerts_and_errors_byte_cost_ is being used as a cheap (albeit
-    // imprecise) fingerprint for the calls that were made to alert().
-    LogHistogramForBug593759(PacResultForStrippedUrl::SUCCESS_DIFFERENT_ALERTS);
-  } else if (original_num_dns != num_dns_) {
-    LogHistogramForBug593759(
-        PacResultForStrippedUrl::SUCCESS_DIFFERENT_NUM_DNS);
-  } else {
-    LogHistogramForBug593759(PacResultForStrippedUrl::SUCCESS);
-  }
-}
-
 bool Job::ResolveDns(const std::string& host,
                      ResolveDnsOperation op,
                      std::string* output,
@@ -783,12 +649,6 @@
     return rv;
   }
 
-  // TODO(eroman): Remove when done gathering data for crbug.com/593759
-  if (dont_start_dns_) {
-    abandoned_ = true;
-    return false;
-  }
-
   if (num_dns_ <= last_num_dns_) {
     // The sequence of DNS operations is different from last time!
     ScheduleRestartWithBlockingDns();
@@ -1242,6 +1102,4 @@
   return base::WrapUnique(new ProxyResolverV8TracingFactoryImpl());
 }
 
-const char kHistogramPacResultForStrippedUrl[] = "Net.PacResultForStrippedUrl";
-
 }  // namespace net
diff --git a/net/proxy/proxy_resolver_v8_tracing.h b/net/proxy/proxy_resolver_v8_tracing.h
index c81df87..6bca78b 100644
--- a/net/proxy/proxy_resolver_v8_tracing.h
+++ b/net/proxy/proxy_resolver_v8_tracing.h
@@ -90,55 +90,6 @@
   DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingFactory);
 };
 
-// This enum is used by an UMA histogram, so the values shouldn't be reordered
-// or renumbered.
-//
-// TODO(eroman): Remove when done gathering data for crbug.com/593759
-enum class PacResultForStrippedUrl {
-  // Did NOT measure the impact of running FindProxyForURL() with a modified
-  // URL path, because the original URL could not complete using the
-  // non-blocking DNS mode.
-  SKIPPED_FALLBACK_BLOCKING_DNS = 0,
-
-  // The result of running FindProxyForURL() with a modified URL path appears
-  // to be indistinguishable. (Although there may have been sideffects to the
-  // script state that won't manifest until later invocations).
-  SUCCESS = 1,
-
-  // Calling FindProxyForURL() with a modified URL path returned the same proxy
-  // list, but had measurable sideffects in calls to alert().
-  SUCCESS_DIFFERENT_ALERTS = 2,
-
-  // Calling FindProxyForURL() with a modified URL path returned the same proxy
-  // list, but invoked a different sequence of DNS resolutions. This would
-  // require a rather unusual script to trigger.
-  SUCCESS_DIFFERENT_NUM_DNS = 3,
-
-  // Calling FindProxyForURL() with a modified URL path resulted in a different
-  // set of DNS dependencies.
-  FAIL_ABANDONED = 4,
-
-  // Calling FindProxyForURL() with a modified URL path caused a different
-  // execution flow. Whereas with the original URL it succeeded with
-  // non-blocking DNS, this attempt requires fallback to blocking DNS (and was
-  // not attempted).
-  FAIL_FALLBACK_BLOCKING_DNS = 5,
-
-  // Calling FindProxyForURL() with a modified URL path caused a script error.
-  FAIL_ERROR = 6,
-
-  // Calling FindProxyForURL() with a modified URL path returned a different
-  // proxy list.
-  FAIL_DIFFERENT_PROXY_LIST = 7,
-
-  MAX_VALUE,
-};
-
-// TODO(eroman): Remove when done gathering data for crbug.com/593759
-//
-// This histogram name is exported only for the sake of unit-tests.
-extern NET_EXPORT_PRIVATE const char kHistogramPacResultForStrippedUrl[];
-
 }  // namespace net
 
 #endif  // NET_PROXY_PROXY_RESOLVER_V8_TRACING_H_
diff --git a/net/proxy/proxy_resolver_v8_tracing_unittest.cc b/net/proxy/proxy_resolver_v8_tracing_unittest.cc
index 79534eb..e28edd1d 100644
--- a/net/proxy/proxy_resolver_v8_tracing_unittest.cc
+++ b/net/proxy/proxy_resolver_v8_tracing_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/test/histogram_tester.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_checker.h"
 #include "base/values.h"
@@ -39,23 +38,6 @@
     // spilling into the next test's execution.
     base::RunLoop().RunUntilIdle();
   }
-
- protected:
-  // TODO(eroman): Remove when done gathering data for crbug.com/593759
-  void ExpectHistogramBucketCount(PacResultForStrippedUrl bucket,
-                                  size_t expected_total) {
-    histograms_.ExpectUniqueSample(kHistogramPacResultForStrippedUrl,
-                                   static_cast<int>(bucket), expected_total);
-  }
-
-  // TODO(eroman): Remove when done gathering data for crbug.com/593759
-  void ExpectHistogramTotal(size_t expected_total) {
-    histograms_.ExpectTotalCount(kHistogramPacResultForStrippedUrl,
-                                 expected_total);
-  }
-
- private:
-  base::HistogramTester histograms_;
 };
 
 scoped_refptr<ProxyResolverScriptData> LoadScriptData(const char* filename) {
@@ -179,15 +161,12 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foo/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foo/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
 
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::SUCCESS, 1);
-  ExpectHistogramTotal(1);
-
   EXPECT_EQ("foo:99", proxy_info.proxy_server().ToURI());
 
   EXPECT_EQ(0u, host_resolver.num_resolve());
@@ -197,36 +176,6 @@
   EXPECT_TRUE(mock_bindings.GetErrors().empty());
 }
 
-TEST_F(ProxyResolverV8TracingTest, AlertUrl) {
-  MockCachingHostResolver host_resolver;
-  MockBindings mock_bindings(&host_resolver);
-
-  std::unique_ptr<ProxyResolverV8Tracing> resolver =
-      CreateResolver(mock_bindings.CreateBindings(), "alert_url.js");
-
-  TestCompletionCallback callback;
-  ProxyInfo proxy_info;
-
-  resolver->GetProxyForURL(GURL("https://foo/path"), &proxy_info,
-                           callback.callback(), NULL,
-                           mock_bindings.CreateBindings());
-
-  EXPECT_EQ(OK, callback.WaitForResult());
-
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::SUCCESS_DIFFERENT_ALERTS,
-                             1);
-  ExpectHistogramTotal(1);
-
-  EXPECT_EQ("foobar:99", proxy_info.proxy_server().ToURI());
-
-  EXPECT_EQ(0u, host_resolver.num_resolve());
-
-  // There was 1 alerts and no errors.
-  EXPECT_EQ(1u, mock_bindings.GetAlerts().size());
-  EXPECT_EQ("https://foo/path", mock_bindings.GetAlerts()[0]);
-  EXPECT_TRUE(mock_bindings.GetErrors().empty());
-}
-
 TEST_F(ProxyResolverV8TracingTest, JavascriptError) {
   MockCachingHostResolver host_resolver;
   MockBindings mock_bindings(&host_resolver);
@@ -237,14 +186,12 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://throw-an-error/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://throw-an-error/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
 
   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult());
 
-  ExpectHistogramTotal(0);
-
   EXPECT_EQ(0u, host_resolver.num_resolve());
 
   // Check the output -- there was 1 alert and 1 javascript error.
@@ -266,16 +213,12 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foo/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foo/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
 
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  ExpectHistogramBucketCount(
-      PacResultForStrippedUrl::SKIPPED_FALLBACK_BLOCKING_DNS, 1);
-  ExpectHistogramTotal(1);
-
   // Iteration1 does a DNS resolve
   // Iteration2 exceeds the alert buffer
   // Iteration3 runs in blocking mode and completes
@@ -306,16 +249,12 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foo/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foo/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
 
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  ExpectHistogramBucketCount(
-      PacResultForStrippedUrl::SKIPPED_FALLBACK_BLOCKING_DNS, 1);
-  ExpectHistogramTotal(1);
-
   EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI());
 
   EXPECT_EQ(1u, host_resolver.num_resolve());
@@ -356,15 +295,12 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foo/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foo/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
 
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::SUCCESS, 1);
-  ExpectHistogramTotal(1);
-
   // The test does 13 DNS resolution, however only 7 of them are unique.
   EXPECT_EQ(7u, host_resolver.num_resolve());
 
@@ -411,18 +347,12 @@
   TestCompletionCallback callback2;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foopy/req1"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foopy/req1"), &proxy_info,
                            callback1.callback(), NULL,
                            mock_bindings.CreateBindings());
 
   EXPECT_EQ(OK, callback1.WaitForResult());
 
-  // This fails because executing FindProxyForURL() PAC script modifies global
-  // state each time, changing the result that is returned.
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::FAIL_DIFFERENT_PROXY_LIST,
-                             1);
-  ExpectHistogramTotal(1);
-
   // The test does 2 DNS resolutions.
   EXPECT_EQ(2u, host_resolver.num_resolve());
 
@@ -435,19 +365,10 @@
 
   EXPECT_EQ(OK, callback2.WaitForResult());
 
-  // The histograms are unchanged because the second invocation is for an
-  // http:// URL.
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::FAIL_DIFFERENT_PROXY_LIST,
-                             1);
-  ExpectHistogramTotal(1);
-
   EXPECT_EQ(4u, host_resolver.num_resolve());
 
   // This time no restarts were required, so g_iteration incremented by 1.
-  // TODO(eroman): Additionally the counter was incremented once by the
-  // diagnostics code that ran FindProxyForURL() with a stripped URL
-  // (should really be :4 and not :5).
-  EXPECT_EQ("166.155.144.11:5", proxy_info.proxy_server().ToURI());
+  EXPECT_EQ("166.155.144.11:4", proxy_info.proxy_server().ToURI());
 
   // There were no alerts or errors.
   EXPECT_TRUE(mock_bindings.GetAlerts().empty());
@@ -471,15 +392,11 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foo/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foo/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  ExpectHistogramBucketCount(
-      PacResultForStrippedUrl::SKIPPED_FALLBACK_BLOCKING_DNS, 1);
-  ExpectHistogramTotal(1);
-
   // The script itself only does 2 DNS resolves per execution, however it
   // constructs the hostname using a global counter which changes on each
   // invocation.
@@ -514,15 +431,11 @@
   TestCompletionCallback callback;
   ProxyInfo proxy_info;
 
-  resolver->GetProxyForURL(GURL("https://foo/"), &proxy_info,
+  resolver->GetProxyForURL(GURL("http://foo/"), &proxy_info,
                            callback.callback(), NULL,
                            mock_bindings.CreateBindings());
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  ExpectHistogramBucketCount(
-      PacResultForStrippedUrl::SKIPPED_FALLBACK_BLOCKING_DNS, 1);
-  ExpectHistogramTotal(1);
-
   EXPECT_EQ(3u, host_resolver.num_resolve());
 
   EXPECT_EQ("166.155.144.44:100", proxy_info.proxy_server().ToURI());
@@ -554,9 +467,6 @@
                            mock_bindings.CreateBindings());
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  // Was not called because this is an http:// URL.
-  ExpectHistogramTotal(0);
-
   EXPECT_EQ(20u, host_resolver.num_resolve());
 
   EXPECT_EQ(
@@ -597,9 +507,6 @@
                            mock_bindings.CreateBindings());
   EXPECT_EQ(OK, callback.WaitForResult());
 
-  // Was not called because this is an http:// URL.
-  ExpectHistogramTotal(0);
-
   EXPECT_EQ(20u, host_resolver.num_resolve());
 
   EXPECT_EQ("null21:34", proxy_info.proxy_server().ToURI());
@@ -612,94 +519,6 @@
   EXPECT_EQ("iteration: 21", mock_bindings.GetAlerts()[0]);
 }
 
-TEST_F(ProxyResolverV8TracingTest, DifferentResultBasedOnUrl) {
-  MockCachingHostResolver host_resolver;
-  MockBindings mock_bindings(&host_resolver);
-
-  std::unique_ptr<ProxyResolverV8Tracing> resolver =
-      CreateResolver(mock_bindings.CreateBindings(), "return_url_as_proxy.js");
-
-  TestCompletionCallback callback;
-  ProxyInfo proxy_info;
-
-  resolver->GetProxyForURL(GURL("https://foo/path1"), &proxy_info,
-                           callback.callback(), NULL,
-                           mock_bindings.CreateBindings());
-
-  EXPECT_EQ(OK, callback.WaitForResult());
-
-  ExpectHistogramTotal(1);
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::FAIL_DIFFERENT_PROXY_LIST,
-                             1);
-
-  EXPECT_EQ("httpsx3Ax2Fx2Ffoox2Fpath1:99", proxy_info.proxy_server().ToURI());
-
-  EXPECT_EQ(0u, host_resolver.num_resolve());
-
-  // There were no alerts or errors.
-  EXPECT_TRUE(mock_bindings.GetAlerts().empty());
-  EXPECT_TRUE(mock_bindings.GetErrors().empty());
-}
-
-TEST_F(ProxyResolverV8TracingTest, ErrorDependingOnUrl) {
-  MockCachingHostResolver host_resolver;
-  MockBindings mock_bindings(&host_resolver);
-
-  std::unique_ptr<ProxyResolverV8Tracing> resolver = CreateResolver(
-      mock_bindings.CreateBindings(), "error_depending_on_url.js");
-
-  TestCompletionCallback callback;
-  ProxyInfo proxy_info;
-
-  resolver->GetProxyForURL(GURL("https://foo/DontThrowError"), &proxy_info,
-                           callback.callback(), NULL,
-                           mock_bindings.CreateBindings());
-
-  EXPECT_EQ(OK, callback.WaitForResult());
-
-  ExpectHistogramTotal(1);
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::FAIL_ERROR, 1);
-
-  EXPECT_EQ("foopy:42", proxy_info.proxy_server().ToURI());
-
-  EXPECT_EQ(0u, host_resolver.num_resolve());
-
-  // There were no alerts or errors.
-  EXPECT_TRUE(mock_bindings.GetAlerts().empty());
-  EXPECT_TRUE(mock_bindings.GetErrors().empty());
-}
-
-TEST_F(ProxyResolverV8TracingTest, DnsDependingOnUrl) {
-  MockCachingHostResolver host_resolver;
-  MockBindings mock_bindings(&host_resolver);
-
-  host_resolver.rules()->AddRule("host", "166.155.144.55");
-
-  // Catch-all that will be used for myIpAddress().
-  host_resolver.rules()->AddRule("*", "133.122.100.200");
-
-  std::unique_ptr<ProxyResolverV8Tracing> resolver =
-      CreateResolver(mock_bindings.CreateBindings(), "dns_depending_on_url.js");
-
-  TestCompletionCallback callback;
-  ProxyInfo proxy_info;
-
-  resolver->GetProxyForURL(GURL("https://foo/UseMyIpAddress"), &proxy_info,
-                           callback.callback(), NULL,
-                           mock_bindings.CreateBindings());
-
-  EXPECT_EQ(OK, callback.WaitForResult());
-
-  ExpectHistogramBucketCount(PacResultForStrippedUrl::FAIL_ABANDONED, 1);
-  ExpectHistogramTotal(1);
-
-  EXPECT_EQ("foopy:47", proxy_info.proxy_server().ToURI());
-
-  // No errors.
-  EXPECT_TRUE(mock_bindings.GetErrors().empty());
-  ASSERT_EQ(0u, mock_bindings.GetAlerts().size());
-}
-
 void DnsDuringInitHelper(bool synchronous_host_resolver) {
   MockCachingHostResolver host_resolver;
   MockBindings mock_bindings(&host_resolver);
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index cf7d65ed..31e6b04 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <ostream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -62,6 +63,14 @@
 
 namespace {
 
+enum DestinationType {
+  // In pooling tests with two requests for different origins to the same
+  // destination, the destination should be
+  SAME_AS_FIRST,   // the same as the first origin,
+  SAME_AS_SECOND,  // the same as the second origin, or
+  DIFFERENT,       // different from both.
+};
+
 static const char kQuicAlternateProtocolHeader[] =
     "Alternate-Protocol: 443:quic\r\n\r\n";
 static const char kQuicAlternateProtocolWithProbabilityHeader[] =
@@ -76,6 +85,44 @@
     "Alt-Svc: quic=\":137\"\r\n\r\n";
 
 const char kDefaultServerHostName[] = "mail.example.org";
+const char kDifferentHostname[] = "different.example.com";
+
+// Run QuicNetworkTransactionWithDestinationTest instances with all value
+// combinations of version and destination_type.
+struct PoolingTestParams {
+  friend std::ostream& operator<<(std::ostream& os,
+                                  const PoolingTestParams& p) {
+    os << "{ version: " << QuicVersionToString(p.version)
+       << ", destination_type: ";
+    switch (p.destination_type) {
+      case SAME_AS_FIRST:
+        os << "SAME_AS_FIRST";
+        break;
+      case SAME_AS_SECOND:
+        os << "SAME_AS_SECOND";
+        break;
+      case DIFFERENT:
+        os << "DIFFERENT";
+        break;
+    }
+    os << " }";
+    return os;
+  }
+
+  QuicVersion version;
+  DestinationType destination_type;
+};
+
+std::vector<PoolingTestParams> GetPoolingTestParams() {
+  std::vector<PoolingTestParams> params;
+  QuicVersionVector all_supported_versions = QuicSupportedVersions();
+  for (const QuicVersion version : all_supported_versions) {
+    params.push_back(PoolingTestParams{version, SAME_AS_FIRST});
+    params.push_back(PoolingTestParams{version, SAME_AS_SECOND});
+    params.push_back(PoolingTestParams{version, DIFFERENT});
+  }
+  return params;
+}
 
 }  // namespace
 
@@ -524,11 +571,6 @@
     SendRequestAndExpectQuicResponseMaybeFromProxy(expected, false, 443);
   }
 
-  void SendRequestAndExpectQuicResponseOnPort(const std::string& expected,
-                                              uint16_t port) {
-    SendRequestAndExpectQuicResponseMaybeFromProxy(expected, false, port);
-  }
-
   void SendRequestAndExpectQuicResponseFromProxyOnPort(
       const std::string& expected,
       uint16_t port) {
@@ -1177,94 +1219,145 @@
 
   SendRequestAndExpectHttpResponse("hello world");
 
-  SendRequestAndExpectQuicResponseOnPort("hello!", 443);
-  SendRequestAndExpectQuicResponseOnPort("hello!", 443);
+  SendRequestAndExpectQuicResponse("hello!");
+  SendRequestAndExpectQuicResponse("hello!");
 }
 
-// When multiple alternative services that has existing QUIC session.
-// HttpStreamFactoryImpl::RequestStreamInternal() should select the first
-// alternative service which uses existing QUIC session.
-TEST_P(QuicNetworkTransactionTest, UseFirstExistingAlternativeServiceForQuic) {
-  MockRead http_reads[] = {
-      MockRead("HTTP/1.1 200 OK\r\n"),
-      MockRead("Alt-Svc: quic=\"foo.example.org:443\", quic=\":446\"\r\n\r\n"),
-      MockRead("hello world"),
-      MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
-      MockRead(ASYNC, OK)};
-
-  StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr,
-                                     0);
-  socket_factory_.AddSocketDataProvider(&http_data);
-  socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
-
-  QuicStreamOffset request_header_offset = 0;
-  QuicStreamOffset response_header_offset = 0;
-
-  QuicTestPacketMaker maker(GetParam(), 0, clock_, kDefaultServerHostName);
-
+// Pool to existing session with matching QuicServerId
+// even if alternative service destination is different.
+TEST_P(QuicNetworkTransactionTest, PoolByOrigin) {
   MockQuicData mock_quic_data;
-  MockQuicData mock_quic_data2;
-  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_);
-  // First QUIC request data.
-  // Open a QUIC session to foo.example.org:443.
+  QuicStreamOffset request_header_offset(0);
+  QuicStreamOffset response_header_offset(0);
+
+  // First request.
   mock_quic_data.AddWrite(ConstructRequestHeadersPacket(
       1, kClientDataStreamId1, true, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
-
-  std::string alt_svc_list =
-      "quic=\"bar.example.org:444\", quic=\"frog.example.org:445\", "
-      "quic=\"mail.example.org:446\"";
-  // Response header from the server resets the alt_svc list for the origin.
   mock_quic_data.AddRead(ConstructResponseHeadersPacket(
-      1, kClientDataStreamId1, false, false,
-      GetResponseHeaders("200 OK", alt_svc_list), &response_header_offset));
-  mock_quic_data.AddRead(ConstructDataPacket(2, kClientDataStreamId1, false,
-                                             true, 0, "hello from foo!"));
+      1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"),
+      &response_header_offset));
+  mock_quic_data.AddRead(
+      ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
   mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
 
-  // Second QUIC request data.
-  // Existing QUIC session to foo.example.org is not viable from the updated
-  // alt_svc. Unable to pool the existing QUIC session.
-  // Open a new QUIC session to bar.example.org:443.
-  mock_quic_data2.AddWrite(ConstructRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/"), &maker));
-  alt_svc_list =
-      "quic=\"foo.example.org:443\", quic=\"mail.example.org:446\", "
-      "quic=\"bar.example.org:444\"";
-  // Response header from the server resets the alt_svc list for the origin.
-  mock_quic_data2.AddRead(ConstructResponseHeadersPacket(
-      1, kClientDataStreamId1, false, false,
-      GetResponseHeaders("200 OK", alt_svc_list), &maker));
-  mock_quic_data2.AddRead(ConstructDataPacket(2, kClientDataStreamId1, false,
-                                              true, 0, "hello from bar!"));
-  mock_quic_data2.AddWrite(ConstructAckPacket(2, 1, &maker));
-  mock_quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
-  mock_quic_data2.AddRead(ASYNC, 0);               // EOF
-
-  // Third QUIC request data.
-  // Connection pooling, using the first existing session to foo.example.org
+  // Second request.
   mock_quic_data.AddWrite(ConstructRequestHeadersPacket(
       3, kClientDataStreamId2, false, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
   mock_quic_data.AddRead(ConstructResponseHeadersPacket(
       3, kClientDataStreamId2, false, false, GetResponseHeaders("200 OK"),
       &response_header_offset));
-  mock_quic_data.AddRead(ConstructDataPacket(4, kClientDataStreamId2, false,
-                                             true, 0, "hello from foo!"));
+  mock_quic_data.AddRead(
+      ConstructDataPacket(4, kClientDataStreamId2, false, true, 0, "hello!"));
   mock_quic_data.AddWrite(ConstructAckAndConnectionClosePacket(4, 4, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
-  mock_quic_data2.AddSocketDataToFactory(&socket_factory_);
+
+  AddHangingNonAlternateProtocolSocketData();
+  AddHangingNonAlternateProtocolSocketData();
 
   CreateSession();
 
-  SendRequestAndExpectHttpResponse("hello world");
-  SendRequestAndExpectQuicResponseOnPort("hello from foo!", 443);
-  SendRequestAndExpectQuicResponseOnPort("hello from bar!", 444);
-  SendRequestAndExpectQuicResponseOnPort("hello from foo!", 443);
+  const char destination1[] = "first.example.com";
+  const char destination2[] = "second.example.com";
+
+  // Set up alternative service entry to destination1.
+  url::SchemeHostPort server(request_.url);
+  AlternativeService alternative_service(QUIC, destination1, 443);
+  base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+  http_server_properties_.SetAlternativeService(server, alternative_service,
+                                                expiration);
+  // First request opens connection to |destination1|
+  // with QuicServerId.host() == kDefaultServerHostName.
+  SendRequestAndExpectQuicResponse("hello!");
+
+  // Set up alternative service entry to a different destination.
+  alternative_service = AlternativeService(QUIC, destination2, 443);
+  http_server_properties_.SetAlternativeService(server, alternative_service,
+                                                expiration);
+  // Second request pools to existing connection with same QuicServerId,
+  // even though alternative service destination is different.
+  SendRequestAndExpectQuicResponse("hello!");
+}
+
+// Pool to existing session with matching destination and matching certificate
+// even if origin is different, and even if the alternative service with
+// matching destination is not the first one on the list.
+TEST_P(QuicNetworkTransactionTest, PoolByDestination) {
+  GURL origin1 = request_.url;
+  GURL origin2("https://www.example.org/");
+  ASSERT_NE(origin1.host(), origin2.host());
+
+  MockQuicData mock_quic_data;
+  QuicStreamOffset request_header_offset(0);
+  QuicStreamOffset response_header_offset(0);
+
+  // First request.
+  mock_quic_data.AddWrite(ConstructRequestHeadersPacket(
+      1, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &request_header_offset));
+  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
+      1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"),
+      &response_header_offset));
+  mock_quic_data.AddRead(
+      ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+  mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+
+  // Second request.
+  QuicTestPacketMaker maker2(GetParam(), 0, clock_, origin2.host());
+  mock_quic_data.AddWrite(ConstructRequestHeadersPacket(
+      3, kClientDataStreamId2, false, true,
+      GetRequestHeaders("GET", "https", "/", &maker2), &request_header_offset,
+      &maker2));
+  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
+      3, kClientDataStreamId2, false, false, GetResponseHeaders("200 OK"),
+      &response_header_offset, &maker2));
+  mock_quic_data.AddRead(
+      ConstructDataPacket(4, kClientDataStreamId2, false, true, 0, "hello!"));
+  mock_quic_data.AddWrite(ConstructAckAndConnectionClosePacket(4, 4, 3, 1));
+  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
+  mock_quic_data.AddRead(ASYNC, 0);               // EOF
+
+  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  AddHangingNonAlternateProtocolSocketData();
+  AddHangingNonAlternateProtocolSocketData();
+
+  CreateSession();
+
+  const char destination1[] = "first.example.com";
+  const char destination2[] = "second.example.com";
+
+  // Set up alternative service for |origin1|.
+  AlternativeService alternative_service1(QUIC, destination1, 443);
+  base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+  http_server_properties_.SetAlternativeService(
+      url::SchemeHostPort(origin1), alternative_service1, expiration);
+
+  // Set up multiple alternative service entries for |origin2|,
+  // the first one with a different destination as for |origin1|,
+  // the second one with the same.  The second one should be used,
+  // because the request can be pooled to that one.
+  AlternativeService alternative_service2(QUIC, destination2, 443);
+  AlternativeServiceInfoVector alternative_services;
+  alternative_services.push_back(
+      AlternativeServiceInfo(alternative_service2, expiration));
+  alternative_services.push_back(
+      AlternativeServiceInfo(alternative_service1, expiration));
+  http_server_properties_.SetAlternativeServices(url::SchemeHostPort(origin2),
+                                                 alternative_services);
+
+  // First request opens connection to |destination1|
+  // with QuicServerId.host() == origin1.host().
+  SendRequestAndExpectQuicResponse("hello!");
+
+  // Second request pools to existing connection with same destination,
+  // because certificate matches, even though QuicServerId is different.
+  request_.url = origin2;
+  SendRequestAndExpectQuicResponse("hello!");
 }
 
 // Multiple origins have listed the same alternative services. When there's a
@@ -1346,103 +1439,11 @@
   // Open a QUIC session to mail.example.org:443 when making request
   // to mail.example.org.
   request_.url = GURL("https://www.example.org/");
-  SendRequestAndExpectQuicResponseOnPort("hello from mail QUIC!", 443);
+  SendRequestAndExpectQuicResponse("hello from mail QUIC!");
 
   // Uses the existing QUIC session when making request to www.example.org.
   request_.url = GURL("https://mail.example.org/");
-  SendRequestAndExpectQuicResponseOnPort("hello from mail QUIC!", 443);
-}
-
-// Multiple origins have listed the same alternative services. When there's a
-// existing QUIC session opened by a request to other origin,
-// if the cert is NOT valid, should ignore this QUIC session.
-TEST_P(QuicNetworkTransactionTest,
-       DoNotUseSharedExistingAlternativeServiceForQuicWithInvalidCert) {
-  // Default cert is valid *.example.org
-  // NOT valid for mail.example.com.
-
-  // HTTP data for request to mail.example.org.
-  MockRead http_reads[] = {
-      MockRead("HTTP/1.1 200 OK\r\n"),
-      MockRead("Alt-Svc: quic=\":443\"\r\n\r\n"),
-      MockRead("hello world from mail.example.org"),
-      MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
-      MockRead(ASYNC, OK)};
-
-  StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr,
-                                     0);
-  socket_factory_.AddSocketDataProvider(&http_data);
-  socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
-
-  // HTTP data for request to mail.example.com.
-  MockRead http_reads2[] = {
-      MockRead("HTTP/1.1 200 OK\r\n"),
-      MockRead("Alt-Svc: quic=\":444\", quic=\"mail.example.org:443\"\r\n\r\n"),
-      MockRead("hello world from mail.example.com"),
-      MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
-      MockRead(ASYNC, OK)};
-
-  StaticSocketDataProvider http_data2(http_reads2, arraysize(http_reads2),
-                                      nullptr, 0);
-  socket_factory_.AddSocketDataProvider(&http_data2);
-  socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
-
-  QuicTestPacketMaker maker(GetParam(), 0, clock_, "mail.example.org");
-  maker.set_hostname("mail.example.com");
-  MockQuicData mock_quic_data;
-  MockQuicData mock_quic_data2;
-
-  // Adding a valid cert for *.example.org but not mail.example.com.
-  ProofVerifyDetailsChromium verify_details;
-  scoped_refptr<X509Certificate> cert(
-      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"));
-  verify_details.cert_verify_result.verified_cert = cert;
-  verify_details.cert_verify_result.is_issued_by_known_root = true;
-  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-
-  // First QUIC request data.
-  mock_quic_data.AddWrite(
-      ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
-                                    GetRequestHeaders("GET", "https", "/")));
-  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
-      1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
-  mock_quic_data.AddRead(ConstructDataPacket(2, kClientDataStreamId1, false,
-                                             true, 0, "hello from mail QUIC!"));
-  mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
-  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
-  mock_quic_data.AddRead(ASYNC, 0);               // EOF
-
-  // First QUIC request data.
-  mock_quic_data2.AddWrite(ConstructRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/", &maker), &maker));
-  mock_quic_data2.AddRead(
-      ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
-                                     GetResponseHeaders("200 OK"), &maker));
-  mock_quic_data2.AddRead(ConstructDataPacket(
-      2, kClientDataStreamId1, false, true, 0, "hello from docs QUIC!"));
-  mock_quic_data2.AddWrite(ConstructAckPacket(2, 1, &maker));
-  mock_quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
-  mock_quic_data2.AddRead(ASYNC, 0);               // EOF
-
-  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
-  mock_quic_data2.AddSocketDataToFactory(&socket_factory_);
-
-  CreateSession();
-
-  // Send HTTP requests, responses set up the alt-svc lists for the origins.
-  SendRequestAndExpectHttpResponse("hello world from mail.example.org");
-  request_.url = GURL("https://mail.example.com/");
-  SendRequestAndExpectHttpResponse("hello world from mail.example.com");
-
-  // Open a QUIC session to mail.example.org:443 when making request
-  // to mail.example.org.
-  request_.url = GURL("https://mail.example.org/");
-  SendRequestAndExpectQuicResponseOnPort("hello from mail QUIC!", 443);
-
-  // Open another new QUIC session to mail.example.com:444.
-  request_.url = GURL("https://mail.example.com/");
-  SendRequestAndExpectQuicResponseOnPort("hello from docs QUIC!", 444);
+  SendRequestAndExpectQuicResponse("hello from mail QUIC!");
 }
 
 TEST_P(QuicNetworkTransactionTest, AlternativeServiceDifferentPort) {
@@ -1458,24 +1459,18 @@
   socket_factory_.AddSocketDataProvider(&http_data);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
-  mock_quic_data.AddWrite(
-      ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
-                                    GetRequestHeaders("GET", "https", "/")));
-  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
-      1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
-  mock_quic_data.AddRead(
-      ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
-  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
-  mock_quic_data.AddRead(ASYNC, 0);               // EOF
-
-  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
-
   CreateSession();
 
   SendRequestAndExpectHttpResponse("hello world");
-  SendRequestAndExpectQuicResponseOnPort("hello!", 137);
+
+  url::SchemeHostPort http_server("https", kDefaultServerHostName, 443);
+  AlternativeServiceVector alternative_service_vector =
+      http_server_properties_.GetAlternativeServices(http_server);
+  ASSERT_EQ(1u, alternative_service_vector.size());
+  const AlternativeService alternative_service = alternative_service_vector[0];
+  EXPECT_EQ(QUIC, alternative_service_vector[0].protocol);
+  EXPECT_EQ(kDefaultServerHostName, alternative_service_vector[0].host);
+  EXPECT_EQ(137, alternative_service_vector[0].port);
 }
 
 TEST_P(QuicNetworkTransactionTest, ConfirmAlternativeService) {
@@ -1636,25 +1631,19 @@
   socket_factory_.AddSocketDataProvider(&http_data);
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
-  MockQuicData mock_quic_data;
-  mock_quic_data.AddWrite(
-      ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
-                                    GetRequestHeaders("GET", "https", "/")));
-  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
-      1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
-  mock_quic_data.AddRead(
-      ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
-  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
-  mock_quic_data.AddRead(ASYNC, 0);               // EOF
-
-  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
-
   params_.parse_alternative_services = false;
   CreateSession();
 
   SendRequestAndExpectHttpResponse("hello world");
-  SendRequestAndExpectQuicResponseOnPort("hello!", 137);
+
+  url::SchemeHostPort http_server("https", kDefaultServerHostName, 443);
+  AlternativeServiceVector alternative_service_vector =
+      http_server_properties_.GetAlternativeServices(http_server);
+  ASSERT_EQ(1u, alternative_service_vector.size());
+  const AlternativeService alternative_service = alternative_service_vector[0];
+  EXPECT_EQ(QUIC, alternative_service_vector[0].protocol);
+  EXPECT_EQ(kDefaultServerHostName, alternative_service_vector[0].host);
+  EXPECT_EQ(137, alternative_service_vector[0].port);
 }
 
 TEST_P(QuicNetworkTransactionTest, ConfirmAlternateProtocol) {
@@ -1735,89 +1724,6 @@
   SendRequestAndExpectHttpResponse("hello world");
 }
 
-class QuicAltSvcCertificateVerificationTest
-    : public QuicNetworkTransactionTest {
- public:
-  void Run(bool valid) {
-    url::SchemeHostPort server(GURL(valid ? "https://mail.example.org:443"
-                                          : "https://mail.example.com:443"));
-    HostPortPair alternative("www.example.org", 443);
-    std::string url("https://");
-    url.append(server.host());
-    url.append(":443");
-    request_.url = GURL(url);
-
-    maker_.set_hostname(server.host());
-    MockQuicData mock_quic_data;
-    mock_quic_data.AddWrite(
-        ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
-                                      GetRequestHeaders("GET", "https", "/")));
-    mock_quic_data.AddRead(ConstructResponseHeadersPacket(
-        1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
-    mock_quic_data.AddRead(
-        ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
-    mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
-    mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-    mock_quic_data.AddSocketDataToFactory(&socket_factory_);
-
-    scoped_refptr<X509Certificate> cert(
-        ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
-    ASSERT_TRUE(cert.get());
-    bool common_name_fallback_used;
-    EXPECT_EQ(valid,
-              cert->VerifyNameMatch(server.host(), &common_name_fallback_used));
-    EXPECT_TRUE(
-        cert->VerifyNameMatch(alternative.host(), &common_name_fallback_used));
-    ProofVerifyDetailsChromium verify_details;
-    verify_details.cert_verify_result.verified_cert = cert;
-    verify_details.cert_verify_result.is_issued_by_known_root = true;
-    crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-    crypto_client_stream_factory_.set_handshake_mode(
-        MockCryptoClientStream::CONFIRM_HANDSHAKE);
-
-    // Connection to |server| fails, so that success of |request| depends on
-    // connection to |alternate| only.
-    MockConnect refused_connect(ASYNC, ERR_CONNECTION_REFUSED);
-    StaticSocketDataProvider refused_data;
-    refused_data.set_connect_data(refused_connect);
-    socket_factory_.AddSocketDataProvider(&refused_data);
-
-    CreateSession();
-    AlternativeService alternative_service(QUIC, alternative);
-    base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    session_->http_server_properties()->SetAlternativeService(
-        server, alternative_service, expiration);
-    std::unique_ptr<HttpNetworkTransaction> trans(
-        new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
-    TestCompletionCallback callback;
-    int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
-    EXPECT_EQ(ERR_IO_PENDING, rv);
-    rv = callback.WaitForResult();
-    if (valid) {
-      EXPECT_EQ(OK, rv);
-      CheckWasQuicResponse(trans);
-      CheckResponsePort(trans, 443);
-      CheckResponseData(trans, "hello!");
-    } else {
-      EXPECT_EQ(ERR_CONNECTION_REFUSED, rv);
-    }
-  }
-};
-
-INSTANTIATE_TEST_CASE_P(Version,
-                        QuicAltSvcCertificateVerificationTest,
-                        ::testing::ValuesIn(QuicSupportedVersions()));
-
-TEST_P(QuicAltSvcCertificateVerificationTest,
-       RequestSucceedsWithValidCertificate) {
-  Run(true);
-}
-
-TEST_P(QuicAltSvcCertificateVerificationTest,
-       RequestFailsWithInvalidCertificate) {
-  Run(false);
-}
-
 TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) {
   params_.parse_alternative_services = false;
   crypto_client_stream_factory_.set_handshake_mode(
@@ -2371,5 +2277,407 @@
   EXPECT_NE(OK, callback.WaitForResult());
 }
 
+class QuicNetworkTransactionWithDestinationTest
+    : public PlatformTest,
+      public ::testing::WithParamInterface<PoolingTestParams> {
+ protected:
+  QuicNetworkTransactionWithDestinationTest()
+      : clock_(new MockClock),
+        version_(GetParam().version),
+        destination_type_(GetParam().destination_type),
+        cert_transparency_verifier_(new MultiLogCTVerifier()),
+        ssl_config_service_(new SSLConfigServiceDefaults),
+        proxy_service_(ProxyService::CreateDirect()),
+        auth_handler_factory_(
+            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+        random_generator_(0),
+        ssl_data_(ASYNC, OK) {}
+
+  void SetUp() override {
+    NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+    base::MessageLoop::current()->RunUntilIdle();
+
+    HttpNetworkSession::Params params;
+
+    clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+    params.quic_clock = clock_;
+
+    crypto_client_stream_factory_.set_handshake_mode(
+        MockCryptoClientStream::CONFIRM_HANDSHAKE);
+    params.quic_crypto_client_stream_factory = &crypto_client_stream_factory_;
+
+    params.enable_alternative_service_with_different_host = true;
+    params.enable_quic = true;
+    params.quic_random = &random_generator_;
+    params.client_socket_factory = &socket_factory_;
+    params.host_resolver = &host_resolver_;
+    params.cert_verifier = &cert_verifier_;
+    params.transport_security_state = &transport_security_state_;
+    params.cert_transparency_verifier = cert_transparency_verifier_.get();
+    params.socket_performance_watcher_factory =
+        &test_socket_performance_watcher_factory_;
+    params.ssl_config_service = ssl_config_service_.get();
+    params.proxy_service = proxy_service_.get();
+    params.http_auth_handler_factory = auth_handler_factory_.get();
+    params.http_server_properties = http_server_properties_.GetWeakPtr();
+    params.quic_supported_versions = SupportedVersions(version_);
+    params.quic_host_whitelist.insert("news.example.org");
+    params.quic_host_whitelist.insert("mail.example.org");
+    params.quic_host_whitelist.insert("mail.example.com");
+
+    session_.reset(new HttpNetworkSession(params));
+    session_->quic_stream_factory()->set_require_confirmation(true);
+    ASSERT_EQ(params.quic_socket_receive_buffer_size,
+              session_->quic_stream_factory()->socket_receive_buffer_size());
+  }
+
+  void TearDown() override {
+    NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+    // Empty the current queue.
+    base::MessageLoop::current()->RunUntilIdle();
+    PlatformTest::TearDown();
+    NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+    base::MessageLoop::current()->RunUntilIdle();
+  }
+
+  void SetAlternativeService(const std::string& origin) {
+    HostPortPair destination;
+    switch (destination_type_) {
+      case SAME_AS_FIRST:
+        destination = HostPortPair(origin1_, 443);
+        break;
+      case SAME_AS_SECOND:
+        destination = HostPortPair(origin2_, 443);
+        break;
+      case DIFFERENT:
+        destination = HostPortPair(kDifferentHostname, 443);
+        break;
+    }
+    AlternativeService alternative_service(QUIC, destination);
+    base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+    http_server_properties_.SetAlternativeService(
+        url::SchemeHostPort("https", origin, 443), alternative_service,
+        expiration);
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      QuicStreamOffset* offset,
+      QuicTestPacketMaker* maker) {
+    SpdyPriority priority =
+        ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
+    SpdyHeaderBlock headers(maker->GetRequestHeaders("GET", "https", "/"));
+    return maker->MakeRequestHeadersPacketWithOffsetTracking(
+        packet_number, stream_id, should_include_version, true, priority,
+        headers, offset);
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      QuicTestPacketMaker* maker) {
+    return ConstructRequestHeadersPacket(
+        packet_number, stream_id, should_include_version, nullptr, maker);
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      QuicStreamOffset* offset,
+      QuicTestPacketMaker* maker) {
+    SpdyHeaderBlock headers(maker->GetResponseHeaders("200 OK"));
+    return maker->MakeResponseHeadersPacketWithOffsetTracking(
+        packet_number, stream_id, false, false, headers, offset);
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      QuicTestPacketMaker* maker) {
+    return ConstructResponseHeadersPacket(packet_number, stream_id, nullptr,
+                                          maker);
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructDataPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      QuicTestPacketMaker* maker) {
+    return maker->MakeDataPacket(packet_number, stream_id, false, true, 0,
+                                 "hello");
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructAckPacket(
+      QuicPacketNumber packet_number,
+      QuicPacketNumber largest_received,
+      QuicPacketNumber ack_least_unacked,
+      QuicPacketNumber stop_least_unacked,
+      QuicTestPacketMaker* maker) {
+    return maker->MakeAckPacket(packet_number, largest_received,
+                                ack_least_unacked, stop_least_unacked, true);
+  }
+
+  void AddRefusedSocketData() {
+    std::unique_ptr<StaticSocketDataProvider> refused_data(
+        new StaticSocketDataProvider());
+    MockConnect refused_connect(SYNCHRONOUS, ERR_CONNECTION_REFUSED);
+    refused_data->set_connect_data(refused_connect);
+    socket_factory_.AddSocketDataProvider(refused_data.get());
+    static_socket_data_provider_vector_.push_back(std::move(refused_data));
+  }
+
+  void AddHangingSocketData() {
+    std::unique_ptr<StaticSocketDataProvider> hanging_data(
+        new StaticSocketDataProvider());
+    MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING);
+    hanging_data->set_connect_data(hanging_connect);
+    socket_factory_.AddSocketDataProvider(hanging_data.get());
+    static_socket_data_provider_vector_.push_back(std::move(hanging_data));
+    socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
+  }
+
+  bool AllDataConsumed() {
+    for (const auto& socket_data_ptr : static_socket_data_provider_vector_) {
+      if (!socket_data_ptr->AllReadDataConsumed() ||
+          !socket_data_ptr->AllWriteDataConsumed()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  void SendRequestAndExpectQuicResponse(const std::string& host) {
+    HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
+    HttpRequestInfo request;
+    std::string url("https://");
+    url.append(host);
+    request.url = GURL(url);
+    request.load_flags = 0;
+    request.method = "GET";
+    TestCompletionCallback callback;
+    int rv = trans.Start(&request, callback.callback(), net_log_.bound());
+    EXPECT_EQ(OK, callback.GetResult(rv));
+
+    std::string response_data;
+    ASSERT_EQ(OK, ReadTransaction(&trans, &response_data));
+    EXPECT_EQ("hello", response_data);
+
+    const HttpResponseInfo* response = trans.GetResponseInfo();
+    ASSERT_TRUE(response != nullptr);
+    ASSERT_TRUE(response->headers.get() != nullptr);
+    EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+    EXPECT_TRUE(response->was_fetched_via_spdy);
+    EXPECT_TRUE(response->was_npn_negotiated);
+    EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_QUIC1_SPDY3,
+              response->connection_info);
+    EXPECT_EQ(443, response->socket_address.port());
+  }
+
+  MockClock* clock_;
+  QuicVersion version_;
+  DestinationType destination_type_;
+  std::string origin1_;
+  std::string origin2_;
+  std::unique_ptr<HttpNetworkSession> session_;
+  MockClientSocketFactory socket_factory_;
+  MockHostResolver host_resolver_;
+  MockCertVerifier cert_verifier_;
+  TransportSecurityState transport_security_state_;
+  std::unique_ptr<CTVerifier> cert_transparency_verifier_;
+  TestSocketPerformanceWatcherFactory test_socket_performance_watcher_factory_;
+  scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
+  std::unique_ptr<ProxyService> proxy_service_;
+  std::unique_ptr<HttpAuthHandlerFactory> auth_handler_factory_;
+  MockRandom random_generator_;
+  HttpServerPropertiesImpl http_server_properties_;
+  BoundTestNetLog net_log_;
+  MockCryptoClientStreamFactory crypto_client_stream_factory_;
+  std::vector<std::unique_ptr<StaticSocketDataProvider>>
+      static_socket_data_provider_vector_;
+  SSLSocketDataProvider ssl_data_;
+};
+
+INSTANTIATE_TEST_CASE_P(Version,
+                        QuicNetworkTransactionWithDestinationTest,
+                        ::testing::ValuesIn(GetPoolingTestParams()));
+
+// A single QUIC request fails because the certificate does not match the origin
+// hostname, regardless of whether it matches the alternative service hostname.
+TEST_P(QuicNetworkTransactionWithDestinationTest, InvalidCertificate) {
+  if (destination_type_ == DIFFERENT)
+    return;
+
+  GURL url("https://mail.example.com/");
+  origin1_ = url.host();
+
+  // Not used for requests, but this provides a test case where the certificate
+  // is valid for the hostname of the alternative service.
+  origin2_ = "mail.example.org";
+
+  SetAlternativeService(origin1_);
+
+  scoped_refptr<X509Certificate> cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  bool unused;
+  ASSERT_FALSE(cert->VerifyNameMatch(origin1_, &unused));
+  ASSERT_TRUE(cert->VerifyNameMatch(origin2_, &unused));
+
+  ProofVerifyDetailsChromium verify_details;
+  verify_details.cert_verify_result.verified_cert = cert;
+  verify_details.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData mock_quic_data;
+  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);
+  mock_quic_data.AddRead(ASYNC, 0);
+
+  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  AddRefusedSocketData();
+
+  HttpRequestInfo request;
+  request.url = url;
+
+  HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
+  TestCompletionCallback callback;
+  int rv = trans.Start(&request, callback.callback(), net_log_.bound());
+  EXPECT_EQ(ERR_CONNECTION_REFUSED, callback.GetResult(rv));
+
+  EXPECT_TRUE(AllDataConsumed());
+}
+
+// First request opens QUIC session to alternative service.  Second request
+// pools to it, because destination matches and certificate is valid, even
+// though QuicServerId is different.
+TEST_P(QuicNetworkTransactionWithDestinationTest, PoolIfCertificateValid) {
+  origin1_ = "mail.example.org";
+  origin2_ = "news.example.org";
+
+  SetAlternativeService(origin1_);
+  SetAlternativeService(origin2_);
+
+  scoped_refptr<X509Certificate> cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  bool unused;
+  ASSERT_TRUE(cert->VerifyNameMatch(origin1_, &unused));
+  ASSERT_TRUE(cert->VerifyNameMatch(origin2_, &unused));
+  ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname, &unused));
+
+  ProofVerifyDetailsChromium verify_details;
+  verify_details.cert_verify_result.verified_cert = cert;
+  verify_details.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  QuicTestPacketMaker maker1(version_, 0, clock_, origin1_);
+
+  QuicStreamOffset request_header_offset(0);
+  QuicStreamOffset response_header_offset(0);
+
+  MockQuicData mock_quic_data;
+  mock_quic_data.AddWrite(ConstructRequestHeadersPacket(
+      1, kClientDataStreamId1, true, &request_header_offset, &maker1));
+  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
+      1, kClientDataStreamId1, &response_header_offset, &maker1));
+  mock_quic_data.AddRead(ConstructDataPacket(2, kClientDataStreamId1, &maker1));
+  mock_quic_data.AddWrite(ConstructAckPacket(2, 2, 1, 1, &maker1));
+
+  QuicTestPacketMaker maker2(version_, 0, clock_, origin2_);
+
+  mock_quic_data.AddWrite(ConstructRequestHeadersPacket(
+      3, kClientDataStreamId2, false, &request_header_offset, &maker2));
+  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
+      3, kClientDataStreamId2, &response_header_offset, &maker2));
+  mock_quic_data.AddRead(ConstructDataPacket(4, kClientDataStreamId2, &maker2));
+  mock_quic_data.AddWrite(ConstructAckPacket(4, 4, 3, 1, &maker2));
+  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
+  mock_quic_data.AddRead(ASYNC, 0);               // EOF
+
+  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  AddHangingSocketData();
+  AddHangingSocketData();
+
+  SendRequestAndExpectQuicResponse(origin1_);
+  SendRequestAndExpectQuicResponse(origin2_);
+
+  EXPECT_TRUE(AllDataConsumed());
+}
+
+// First request opens QUIC session to alternative service.  Second request does
+// not pool to it, even though destination matches, because certificate is not
+// valid.  Instead, a new QUIC session is opened to the same destination with a
+// different QuicServerId.
+TEST_P(QuicNetworkTransactionWithDestinationTest,
+       DoNotPoolIfCertificateInvalid) {
+  origin1_ = "news.example.org";
+  origin2_ = "mail.example.com";
+
+  SetAlternativeService(origin1_);
+  SetAlternativeService(origin2_);
+
+  scoped_refptr<X509Certificate> cert1(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  bool unused;
+  ASSERT_TRUE(cert1->VerifyNameMatch(origin1_, &unused));
+  ASSERT_FALSE(cert1->VerifyNameMatch(origin2_, &unused));
+  ASSERT_FALSE(cert1->VerifyNameMatch(kDifferentHostname, &unused));
+
+  scoped_refptr<X509Certificate> cert2(
+      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"));
+  ASSERT_TRUE(cert2->VerifyNameMatch(origin2_, &unused));
+  ASSERT_FALSE(cert2->VerifyNameMatch(kDifferentHostname, &unused));
+
+  ProofVerifyDetailsChromium verify_details1;
+  verify_details1.cert_verify_result.verified_cert = cert1;
+  verify_details1.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details1);
+
+  ProofVerifyDetailsChromium verify_details2;
+  verify_details2.cert_verify_result.verified_cert = cert2;
+  verify_details2.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details2);
+
+  QuicTestPacketMaker maker1(version_, 0, clock_, origin1_);
+
+  MockQuicData mock_quic_data1;
+  mock_quic_data1.AddWrite(
+      ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, &maker1));
+  mock_quic_data1.AddRead(
+      ConstructResponseHeadersPacket(1, kClientDataStreamId1, &maker1));
+  mock_quic_data1.AddRead(
+      ConstructDataPacket(2, kClientDataStreamId1, &maker1));
+  mock_quic_data1.AddWrite(ConstructAckPacket(2, 2, 1, 1, &maker1));
+  mock_quic_data1.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
+  mock_quic_data1.AddRead(ASYNC, 0);               // EOF
+
+  mock_quic_data1.AddSocketDataToFactory(&socket_factory_);
+
+  AddHangingSocketData();
+
+  QuicTestPacketMaker maker2(version_, 0, clock_, origin2_);
+
+  MockQuicData mock_quic_data2;
+  mock_quic_data2.AddWrite(
+      ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, &maker2));
+  mock_quic_data2.AddRead(
+      ConstructResponseHeadersPacket(1, kClientDataStreamId1, &maker2));
+  mock_quic_data2.AddRead(
+      ConstructDataPacket(2, kClientDataStreamId1, &maker2));
+  mock_quic_data2.AddWrite(ConstructAckPacket(2, 2, 1, 1, &maker2));
+  mock_quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
+  mock_quic_data2.AddRead(ASYNC, 0);               // EOF
+
+  mock_quic_data2.AddSocketDataToFactory(&socket_factory_);
+
+  AddHangingSocketData();
+
+  SendRequestAndExpectQuicResponse(origin1_);
+  SendRequestAndExpectQuicResponse(origin2_);
+
+  EXPECT_TRUE(AllDataConsumed());
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 7911f81..efcba5e 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -5,6 +5,7 @@
 #include "net/quic/quic_stream_factory.h"
 
 #include <algorithm>
+#include <tuple>
 #include <utility>
 
 #include <openssl/aead.h>
@@ -62,7 +63,6 @@
 #endif
 
 using std::min;
-using std::vector;
 using NetworkHandle = net::NetworkChangeNotifier::NetworkHandle;
 
 namespace net {
@@ -132,8 +132,7 @@
  public:
   Job(QuicStreamFactory* factory,
       HostResolver* host_resolver,
-      const QuicServerId& server_id,
-      bool server_and_origin_have_same_host,
+      const QuicSessionKey& key,
       bool was_alternative_service_recently_broken,
       int cert_verify_flags,
       bool is_post,
@@ -145,7 +144,7 @@
   Job(QuicStreamFactory* factory,
       HostResolver* host_resolver,
       QuicChromiumClientSession* session,
-      QuicServerId server_id);
+      const QuicSessionKey& key);
 
   ~Job();
 
@@ -168,7 +167,7 @@
 
   void CancelWaitForDataReadyCallback();
 
-  const QuicServerId server_id() const { return server_id_; }
+  const QuicSessionKey& key() const { return key_; }
 
   base::WeakPtr<Job> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
 
@@ -187,10 +186,8 @@
 
   QuicStreamFactory* factory_;
   SingleRequestHostResolver host_resolver_;
-  QuicServerId server_id_;
+  QuicSessionKey key_;
   int cert_verify_flags_;
-  // True if and only if server and origin have the same hostname.
-  bool server_and_origin_have_same_host_;
   bool is_post_;
   bool was_alternative_service_recently_broken_;
   std::unique_ptr<QuicServerInfo> server_info_;
@@ -208,8 +205,7 @@
 
 QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
                             HostResolver* host_resolver,
-                            const QuicServerId& server_id,
-                            bool server_and_origin_have_same_host,
+                            const QuicSessionKey& key,
                             bool was_alternative_service_recently_broken,
                             int cert_verify_flags,
                             bool is_post,
@@ -218,9 +214,8 @@
     : io_state_(STATE_RESOLVE_HOST),
       factory_(factory),
       host_resolver_(host_resolver),
-      server_id_(server_id),
+      key_(key),
       cert_verify_flags_(cert_verify_flags),
-      server_and_origin_have_same_host_(server_and_origin_have_same_host),
       is_post_(is_post),
       was_alternative_service_recently_broken_(
           was_alternative_service_recently_broken),
@@ -234,13 +229,12 @@
 QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
                             HostResolver* host_resolver,
                             QuicChromiumClientSession* session,
-                            QuicServerId server_id)
+                            const QuicSessionKey& key)
     : io_state_(STATE_RESUME_CONNECT),
       factory_(factory),
       host_resolver_(host_resolver),  // unused
-      server_id_(server_id),
+      key_(key),
       cert_verify_flags_(0),                            // unused
-      server_and_origin_have_same_host_(false),         // unused
       is_post_(false),                                  // unused
       was_alternative_service_recently_broken_(false),  // unused
       started_another_job_(false),                      // unused
@@ -341,7 +335,7 @@
   io_state_ = STATE_RESOLVE_HOST_COMPLETE;
   dns_resolution_start_time_ = base::TimeTicks::Now();
   return host_resolver_.Resolve(
-      HostResolver::RequestInfo(server_id_.host_port_pair()), DEFAULT_PRIORITY,
+      HostResolver::RequestInfo(key_.destination()), DEFAULT_PRIORITY,
       &address_list_,
       base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()),
       net_log_);
@@ -354,11 +348,11 @@
   if (rv != OK)
     return rv;
 
-  DCHECK(!factory_->HasActiveSession(server_id_));
+  DCHECK(!factory_->HasActiveSession(key_.server_id()));
 
   // Inform the factory of this resolution, which will set up
   // a session alias, if possible.
-  if (factory_->OnResolution(server_id_, address_list_)) {
+  if (factory_->OnResolution(key_, address_list_)) {
     return OK;
   }
 
@@ -383,7 +377,7 @@
         std::min(static_cast<int>(
                      (factory_->load_server_info_timeout_srtt_multiplier_ *
                       factory_->GetServerNetworkStatsSmoothedRttInMicroseconds(
-                          server_id_)) /
+                          key_.server_id())) /
                      1000),
                  kMaxLoadServerInfoTimeoutMs);
     if (load_server_info_timeout_ms > 0) {
@@ -401,9 +395,7 @@
     // If we are waiting to load server config from the disk cache, then start
     // another job.
     started_another_job_ = true;
-    factory_->CreateAuxilaryJob(server_id_, cert_verify_flags_,
-                                server_and_origin_have_same_host_, is_post_,
-                                net_log_);
+    factory_->CreateAuxilaryJob(key_, cert_verify_flags_, is_post_, net_log_);
   }
   return rv;
 }
@@ -417,7 +409,7 @@
 
   if (started_another_job_ &&
       (!server_info_ || server_info_->state().server_config.empty() ||
-       !factory_->CryptoConfigCacheIsEmpty(server_id_))) {
+       !factory_->CryptoConfigCacheIsEmpty(key_.server_id()))) {
     // If we have started another job and if we didn't load the server config
     // from the disk cache or if we have received a new server config from the
     // server, then cancel the current job.
@@ -433,7 +425,7 @@
   io_state_ = STATE_CONNECT_COMPLETE;
 
   int rv = factory_->CreateSession(
-      server_id_, cert_verify_flags_, std::move(server_info_), address_list_,
+      key_, cert_verify_flags_, std::move(server_info_), address_list_,
       dns_resolution_end_time_, net_log_, &session_);
   if (rv != OK) {
     DCHECK(rv != ERR_IO_PENDING);
@@ -449,13 +441,17 @@
   if (!session_->connection()->connected()) {
     return ERR_QUIC_PROTOCOL_ERROR;
   }
-  bool require_confirmation = factory_->require_confirmation() ||
-                              !server_and_origin_have_same_host_ || is_post_ ||
+  bool require_confirmation = factory_->require_confirmation() || is_post_ ||
                               was_alternative_service_recently_broken_;
 
   rv = session_->CryptoConnect(
       require_confirmation,
       base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()));
+
+  if (!session_->connection()->connected() &&
+      session_->error() == QUIC_PROOF_INVALID)
+    return ERR_QUIC_HANDSHAKE_FAILED;
+
   return rv;
 }
 
@@ -483,11 +479,11 @@
   if (rv != OK)
     return rv;
 
-  DCHECK(!factory_->HasActiveSession(server_id_));
+  DCHECK(!factory_->HasActiveSession(key_.server_id()));
   // There may well now be an active session for this IP.  If so, use the
   // existing session instead.
   AddressList address(session_->connection()->peer_address());
-  if (factory_->OnResolution(server_id_, address)) {
+  if (factory_->OnResolution(key_, address)) {
     session_->connection()->CloseConnection(
         QUIC_CONNECTION_IP_POOLED, "An active session exists for the given IP.",
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
@@ -495,7 +491,7 @@
     return OK;
   }
 
-  factory_->ActivateSession(server_id_, session_);
+  factory_->ActivateSession(key_, session_);
 
   return OK;
 }
@@ -508,7 +504,7 @@
     factory_->CancelRequest(this);
 }
 
-int QuicStreamRequest::Request(const HostPortPair& host_port_pair,
+int QuicStreamRequest::Request(const HostPortPair& destination,
                                PrivacyMode privacy_mode,
                                int cert_verify_flags,
                                const GURL& url,
@@ -517,13 +513,11 @@
                                const CompletionCallback& callback) {
   DCHECK(callback_.is_null());
   DCHECK(factory_);
-  origin_host_ = url.host();
-  privacy_mode_ = privacy_mode;
+  server_id_ = QuicServerId(HostPortPair::FromURL(url), privacy_mode);
 
-  int rv = factory_->Create(host_port_pair, privacy_mode, cert_verify_flags,
-                            url, method, net_log, this);
+  int rv = factory_->Create(server_id_, destination, cert_verify_flags, url,
+                            method, net_log, this);
   if (rv == ERR_IO_PENDING) {
-    host_port_pair_ = host_port_pair;
     net_log_ = net_log;
     callback_ = callback;
   } else {
@@ -547,8 +541,7 @@
 base::TimeDelta QuicStreamRequest::GetTimeDelayForWaitingJob() const {
   if (!factory_)
     return base::TimeDelta();
-  return factory_->GetTimeDelayForWaitingJob(
-      QuicServerId(host_port_pair_, privacy_mode_));
+  return factory_->GetTimeDelayForWaitingJob(server_id_);
 }
 
 std::unique_ptr<QuicHttpStream> QuicStreamRequest::CreateStream() {
@@ -756,25 +749,34 @@
 }
 
 bool QuicStreamFactory::CanUseExistingSession(const QuicServerId& server_id,
-                                              base::StringPiece origin_host) {
+                                              const HostPortPair& destination) {
   // TODO(zhongyi): delete active_sessions_.empty() checks once the
   // android crash issue(crbug.com/498823) is resolved.
   if (active_sessions_.empty())
     return false;
-  SessionMap::iterator it = active_sessions_.find(server_id);
-  if (it == active_sessions_.end())
-    return false;
-  QuicChromiumClientSession* session = it->second;
-  return session->CanPool(origin_host.as_string(), server_id.privacy_mode());
+
+  if (ContainsKey(active_sessions_, server_id))
+    return true;
+
+  for (const auto& key_value : active_sessions_) {
+    QuicChromiumClientSession* session = key_value.second;
+    if (destination.Equals(all_sessions_[session].destination()) &&
+        session->CanPool(server_id.host(), server_id.privacy_mode())) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
-int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
-                              PrivacyMode privacy_mode,
+int QuicStreamFactory::Create(const QuicServerId& server_id,
+                              const HostPortPair& destination,
                               int cert_verify_flags,
                               const GURL& url,
                               base::StringPiece method,
                               const BoundNetLog& net_log,
                               QuicStreamRequest* request) {
+  DCHECK(server_id.host_port_pair().Equals(HostPortPair::FromURL(url)));
   // Enforce session affinity for promised streams.
   QuicClientPromisedInfo* promised =
       push_promise_index_.GetPromised(url.spec());
@@ -782,7 +784,7 @@
     QuicChromiumClientSession* session =
         static_cast<QuicChromiumClientSession*>(promised->session());
     DCHECK(session);
-    if (session->server_id().privacy_mode() == privacy_mode) {
+    if (session->server_id().privacy_mode() == server_id.privacy_mode()) {
       request->SetSession(session);
       ++num_push_streams_created_;
       return OK;
@@ -793,25 +795,36 @@
     promised->Cancel();
   }
 
-  QuicServerId server_id(host_port_pair, privacy_mode);
+  // Use active session for |server_id| if such exists.
   // TODO(rtenneti): crbug.com/498823 - delete active_sessions_.empty() checks.
   if (!active_sessions_.empty()) {
     SessionMap::iterator it = active_sessions_.find(server_id);
     if (it != active_sessions_.end()) {
       QuicChromiumClientSession* session = it->second;
-      if (!session->CanPool(url.host(), privacy_mode))
-        return ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN;
       request->SetSession(session);
       return OK;
     }
   }
 
+  // Associate with active job to |server_id| if such exists.
   if (HasActiveJob(server_id)) {
     active_requests_[request] = server_id;
     job_requests_map_[server_id].insert(request);
     return ERR_IO_PENDING;
   }
 
+  // Pool to active session to |destination| if possible.
+  if (!active_sessions_.empty() && !disable_connection_pooling_) {
+    for (const auto& key_value : active_sessions_) {
+      QuicChromiumClientSession* session = key_value.second;
+      if (destination.Equals(all_sessions_[session].destination()) &&
+          session->CanPool(server_id.host(), server_id.privacy_mode())) {
+        request->SetSession(session);
+        return OK;
+      }
+    }
+  }
+
   // TODO(rtenneti): |task_runner_| is used by the Job. Initialize task_runner_
   // in the constructor after WebRequestActionWithThreadsTest.* tests are fixed.
   if (!task_runner_)
@@ -821,7 +834,7 @@
   if (quic_server_info_factory_.get()) {
     bool load_from_disk_cache = !disable_disk_cache_;
     MaybeInitialize();
-    if (!ContainsKey(quic_supported_servers_at_startup_, host_port_pair)) {
+    if (!ContainsKey(quic_supported_servers_at_startup_, destination)) {
       // If there is no entry for QUIC, consider that as a new server and
       // don't wait for Cache thread to load the data for that server.
       load_from_disk_cache = false;
@@ -831,11 +844,11 @@
     }
   }
 
-  bool server_and_origin_have_same_host = host_port_pair.host() == url.host();
+  QuicSessionKey key(destination, server_id);
   std::unique_ptr<Job> job(
-      new Job(this, host_resolver_, server_id, server_and_origin_have_same_host,
-              WasQuicRecentlyBroken(server_id), cert_verify_flags,
-              method == "POST" /* is_post */, quic_server_info, net_log));
+      new Job(this, host_resolver_, key, WasQuicRecentlyBroken(server_id),
+              cert_verify_flags, method == "POST" /* is_post */,
+              quic_server_info, net_log));
   int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
                                base::Unretained(this), job.get()));
   if (rv == ERR_IO_PENDING) {
@@ -854,30 +867,44 @@
     if (it == active_sessions_.end())
       return ERR_QUIC_PROTOCOL_ERROR;
     QuicChromiumClientSession* session = it->second;
-    if (!session->CanPool(url.host(), privacy_mode))
-      return ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN;
     request->SetSession(session);
   }
   return rv;
 }
 
-void QuicStreamFactory::CreateAuxilaryJob(const QuicServerId server_id,
+QuicStreamFactory::QuicSessionKey::QuicSessionKey(
+    const HostPortPair& destination,
+    const QuicServerId& server_id)
+    : destination_(destination), server_id_(server_id) {}
+
+bool QuicStreamFactory::QuicSessionKey::operator<(
+    const QuicSessionKey& other) const {
+  return std::tie(destination_, server_id_) <
+         std::tie(other.destination_, other.server_id_);
+}
+
+bool QuicStreamFactory::QuicSessionKey::operator==(
+    const QuicSessionKey& other) const {
+  return destination_.Equals(other.destination_) &&
+         server_id_ == other.server_id_;
+}
+
+void QuicStreamFactory::CreateAuxilaryJob(const QuicSessionKey& key,
                                           int cert_verify_flags,
-                                          bool server_and_origin_have_same_host,
                                           bool is_post,
                                           const BoundNetLog& net_log) {
   Job* aux_job =
-      new Job(this, host_resolver_, server_id, server_and_origin_have_same_host,
-              WasQuicRecentlyBroken(server_id), cert_verify_flags, is_post,
-              nullptr, net_log);
-  active_jobs_[server_id].insert(aux_job);
+      new Job(this, host_resolver_, key, WasQuicRecentlyBroken(key.server_id()),
+              cert_verify_flags, is_post, nullptr, net_log);
+  active_jobs_[key.server_id()].insert(aux_job);
   task_runner_->PostTask(FROM_HERE,
                          base::Bind(&QuicStreamFactory::Job::RunAuxilaryJob,
                                     aux_job->GetWeakPtr()));
 }
 
-bool QuicStreamFactory::OnResolution(const QuicServerId& server_id,
+bool QuicStreamFactory::OnResolution(const QuicSessionKey& key,
                                      const AddressList& address_list) {
+  const QuicServerId& server_id(key.server_id());
   DCHECK(!HasActiveSession(server_id));
   if (disable_connection_pooling_) {
     return false;
@@ -891,7 +918,7 @@
       if (!session->CanPool(server_id.host(), server_id.privacy_mode()))
         continue;
       active_sessions_[server_id] = session;
-      session_aliases_[session].insert(server_id);
+      session_aliases_[session].insert(key);
       return true;
     }
   }
@@ -899,7 +926,9 @@
 }
 
 void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
-  QuicServerId server_id = job->server_id();
+  // Copy |server_id|, because |job| might be destroyed before this method
+  // returns.
+  const QuicServerId server_id(job->key().server_id());
   if (rv != OK) {
     JobSet* jobs = &(active_jobs_[server_id]);
     if (jobs->size() > 1) {
@@ -916,26 +945,15 @@
     if (!always_require_handshake_confirmation_)
       set_require_confirmation(false);
 
-    // Create all the streams, but do not notify them yet.
-    SessionMap::iterator session_it = active_sessions_.find(server_id);
-    for (RequestSet::iterator request_it = job_requests_map_[server_id].begin();
-         request_it != job_requests_map_[server_id].end();) {
+    if (!job_requests_map_[server_id].empty()) {
+      SessionMap::iterator session_it = active_sessions_.find(server_id);
       DCHECK(session_it != active_sessions_.end());
       QuicChromiumClientSession* session = session_it->second;
-      QuicStreamRequest* request = *request_it;
-      if (!session->CanPool(request->origin_host(), request->privacy_mode())) {
-        RequestSet::iterator old_request_it = request_it;
-        ++request_it;
-        // Remove request from containers so that OnRequestComplete() is not
-        // called later again on the same request.
-        job_requests_map_[server_id].erase(old_request_it);
-        active_requests_.erase(request);
-        // Notify request of certificate error.
-        request->OnRequestComplete(ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN);
-        continue;
+      for (QuicStreamRequest* request : job_requests_map_[server_id]) {
+        DCHECK(request->server_id() == server_id);
+        // Do not notify |request| yet.
+        request->SetSession(session);
       }
-      request->SetSession(session);
-      ++request_it;
     }
   }
 
@@ -1070,18 +1088,19 @@
   const AliasSet& aliases = session_aliases_[session];
   for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
        ++it) {
-    DCHECK(active_sessions_.count(*it));
-    DCHECK_EQ(session, active_sessions_[*it]);
+    const QuicServerId& server_id = it->server_id();
+    DCHECK(active_sessions_.count(server_id));
+    DCHECK_EQ(session, active_sessions_[server_id]);
     // Track sessions which have recently gone away so that we can disable
     // port suggestions.
     if (session->goaway_received()) {
       gone_away_aliases_.insert(*it);
     }
 
-    active_sessions_.erase(*it);
-    ProcessGoingAwaySession(session, *it, true);
+    active_sessions_.erase(server_id);
+    ProcessGoingAwaySession(session, server_id, true);
   }
-  ProcessGoingAwaySession(session, all_sessions_[session], false);
+  ProcessGoingAwaySession(session, all_sessions_[session].server_id(), false);
   if (!aliases.empty()) {
     const IPEndPoint peer_address = session->connection()->peer_address();
     ip_aliases_[peer_address].erase(session);
@@ -1191,36 +1210,39 @@
 void QuicStreamFactory::OnSessionConnectTimeout(
     QuicChromiumClientSession* session) {
   const AliasSet& aliases = session_aliases_[session];
-  for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
-       ++it) {
-    DCHECK(active_sessions_.count(*it));
-    DCHECK_EQ(session, active_sessions_[*it]);
-    active_sessions_.erase(*it);
-  }
 
   if (aliases.empty()) {
     return;
   }
 
+  for (const QuicSessionKey& key : aliases) {
+    const QuicServerId& server_id = key.server_id();
+    SessionMap::iterator session_it = active_sessions_.find(server_id);
+    DCHECK(session_it != active_sessions_.end());
+    DCHECK_EQ(session, session_it->second);
+    active_sessions_.erase(session_it);
+  }
+
   const IPEndPoint peer_address = session->connection()->peer_address();
   ip_aliases_[peer_address].erase(session);
   if (ip_aliases_[peer_address].empty()) {
     ip_aliases_.erase(peer_address);
   }
-  QuicServerId server_id = *aliases.begin();
+  QuicSessionKey key = *aliases.begin();
   session_aliases_.erase(session);
-  Job* job = new Job(this, host_resolver_, session, server_id);
-  active_jobs_[server_id].insert(job);
+  Job* job = new Job(this, host_resolver_, session, key);
+  active_jobs_[key.server_id()].insert(job);
   int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
                                base::Unretained(this), job));
   DCHECK_EQ(ERR_IO_PENDING, rv);
 }
 
 void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
-  DCHECK(ContainsKey(active_requests_, request));
-  QuicServerId server_id = active_requests_[request];
+  RequestMap::iterator request_it = active_requests_.find(request);
+  DCHECK(request_it != active_requests_.end());
+  const QuicServerId& server_id = request_it->second;
   job_requests_map_[server_id].erase(request);
-  active_requests_.erase(request);
+  active_requests_.erase(request_it);
 }
 
 void QuicStreamFactory::CloseAllSessions(int error, QuicErrorCode quic_error) {
@@ -1248,11 +1270,11 @@
     QuicChromiumClientSession* session = it->second;
     const AliasSet& aliases = session_aliases_.find(session)->second;
     // Only add a session to the list once.
-    if (server_id == *aliases.begin()) {
+    if (server_id == aliases.begin()->server_id()) {
       std::set<HostPortPair> hosts;
       for (AliasSet::const_iterator alias_it = aliases.begin();
            alias_it != aliases.end(); ++alias_it) {
-        hosts.insert(alias_it->host_port_pair());
+        hosts.insert(alias_it->server_id().host_port_pair());
       }
       list->Append(session->GetInfoAsValue(hosts));
     }
@@ -1310,7 +1332,6 @@
   QuicStreamFactory::SessionIdMap::iterator it = all_sessions_.begin();
   while (it != all_sessions_.end()) {
     QuicChromiumClientSession* session = it->first;
-    QuicServerId server_id = it->second;
     ++it;
 
     if (session->GetDefaultSocket()->GetBoundNetwork() != network) {
@@ -1441,8 +1462,8 @@
   return ContainsKey(active_sessions_, server_id);
 }
 
-bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
-  return ContainsKey(active_jobs_, key);
+bool QuicStreamFactory::HasActiveJob(const QuicServerId& server_id) const {
+  return ContainsKey(active_jobs_, server_id);
 }
 
 int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket,
@@ -1500,7 +1521,7 @@
 }
 
 int QuicStreamFactory::CreateSession(
-    const QuicServerId& server_id,
+    const QuicSessionKey& key,
     int cert_verify_flags,
     std::unique_ptr<QuicServerInfo> server_info,
     const AddressList& address_list,
@@ -1510,13 +1531,14 @@
   TRACE_EVENT0("net", "QuicStreamFactory::CreateSession");
   IPEndPoint addr = *address_list.begin();
   bool enable_port_selection = enable_port_selection_;
-  if (enable_port_selection && ContainsKey(gone_away_aliases_, server_id)) {
+  if (enable_port_selection && ContainsKey(gone_away_aliases_, key)) {
     // Disable port selection when the server is going away.
     // There is no point in trying to return to the same server, if
     // that server is no longer handling requests.
     enable_port_selection = false;
-    gone_away_aliases_.erase(server_id);
+    gone_away_aliases_.erase(key);
   }
+  const QuicServerId& server_id = key.server_id();
   scoped_refptr<PortSuggester> port_suggester =
       new PortSuggester(server_id.host_port_pair(), port_seed_);
   DatagramSocket::BindType bind_type =
@@ -1598,7 +1620,7 @@
       base::ThreadTaskRunnerHandle::Get().get(),
       std::move(socket_performance_watcher), net_log.net_log());
 
-  all_sessions_[*session] = server_id;  // owning pointer
+  all_sessions_[*session] = key;  // owning pointer
 
   (*session)->Initialize();
   bool closed_during_initialize = !ContainsKey(all_sessions_, *session) ||
@@ -1613,12 +1635,13 @@
   return OK;
 }
 
-void QuicStreamFactory::ActivateSession(const QuicServerId& server_id,
+void QuicStreamFactory::ActivateSession(const QuicSessionKey& key,
                                         QuicChromiumClientSession* session) {
+  const QuicServerId& server_id(key.server_id());
   DCHECK(!HasActiveSession(server_id));
   UMA_HISTOGRAM_COUNTS("Net.QuicActiveSessions", active_sessions_.size());
   active_sessions_[server_id] = session;
-  session_aliases_[session].insert(server_id);
+  session_aliases_[session].insert(key);
   const IPEndPoint peer_address = session->connection()->peer_address();
   DCHECK(!ContainsKey(ip_aliases_[peer_address], session));
   ip_aliases_[peer_address].insert(session);
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index a56a70e..d1aab01 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -78,7 +78,9 @@
 
   // |cert_verify_flags| is bitwise OR'd of CertVerifier::VerifyFlags and it is
   // passed to CertVerifier::Verify.
-  int Request(const HostPortPair& host_port_pair,
+  // |destination| will be resolved and resulting IPEndPoint used to open a
+  // QuicConnection.  This can be different than HostPortPair::FromURL(url).
+  int Request(const HostPortPair& destination,
               PrivacyMode privacy_mode,
               int cert_verify_flags,
               const GURL& url,
@@ -99,17 +101,13 @@
   // Sets |session_|.
   void SetSession(QuicChromiumClientSession* session);
 
-  const std::string& origin_host() const { return origin_host_; }
-
-  PrivacyMode privacy_mode() const { return privacy_mode_; }
+  const QuicServerId& server_id() const { return server_id_; }
 
   const BoundNetLog& net_log() const { return net_log_; }
 
  private:
   QuicStreamFactory* factory_;
-  HostPortPair host_port_pair_;
-  std::string origin_host_;
-  PrivacyMode privacy_mode_;
+  QuicServerId server_id_;
   BoundNetLog net_log_;
   CompletionCallback callback_;
   base::WeakPtr<QuicChromiumClientSession> session_;
@@ -125,6 +123,31 @@
       public SSLConfigService::Observer,
       public CertDatabase::Observer {
  public:
+  // This class encompasses |destination| and |server_id|.
+  // |destination| is a HostPortPair which is resolved
+  // and a QuicConnection is made to the resulting IP address.
+  // |server_id| identifies the origin of the request,
+  // the crypto handshake advertises |server_id.host()| to the server,
+  // and the certificate is also matched against |server_id.host()|.
+  class NET_EXPORT_PRIVATE QuicSessionKey {
+   public:
+    QuicSessionKey() = default;
+    QuicSessionKey(const HostPortPair& destination,
+                   const QuicServerId& server_id);
+    ~QuicSessionKey() = default;
+
+    // Needed to be an element of std::set.
+    bool operator<(const QuicSessionKey& other) const;
+    bool operator==(const QuicSessionKey& other) const;
+
+    const HostPortPair& destination() const { return destination_; }
+    const QuicServerId& server_id() const { return server_id_; }
+
+   private:
+    HostPortPair destination_;
+    QuicServerId server_id_;
+  };
+
   QuicStreamFactory(
       HostResolver* host_resolver,
       ClientSocketFactory* client_socket_factory,
@@ -165,18 +188,19 @@
       bool enable_token_binding);
   ~QuicStreamFactory() override;
 
-  // Returns true if there is an existing session to |server_id| which can be
-  // used for request to |origin_host|.
+  // Returns true if there is an existing session for |server_id| or if the
+  // request can be pooled to an existing session to the IP address of
+  // |destination|.
   bool CanUseExistingSession(const QuicServerId& server_id,
-                             base::StringPiece origin_host);
+                             const HostPortPair& destination);
 
   // Creates a new QuicHttpStream to |host_port_pair| which will be
   // owned by |request|.
   // If a matching session already exists, this method will return OK.  If no
   // matching session exists, this will return ERR_IO_PENDING and will invoke
   // OnRequestComplete asynchronously.
-  int Create(const HostPortPair& host_port_pair,
-             PrivacyMode privacy_mode,
+  int Create(const QuicServerId& server_id,
+             const HostPortPair& destination,
              int cert_verify_flags,
              const GURL& url,
              base::StringPiece method,
@@ -327,8 +351,8 @@
   FRIEND_TEST_ALL_PREFIXES(HttpStreamFactoryTest, QuicLossyProxyMarkedAsBad);
 
   typedef std::map<QuicServerId, QuicChromiumClientSession*> SessionMap;
-  typedef std::map<QuicChromiumClientSession*, QuicServerId> SessionIdMap;
-  typedef std::set<QuicServerId> AliasSet;
+  typedef std::map<QuicChromiumClientSession*, QuicSessionKey> SessionIdMap;
+  typedef std::set<QuicSessionKey> AliasSet;
   typedef std::map<QuicChromiumClientSession*, AliasSet> SessionAliasMap;
   typedef std::set<QuicChromiumClientSession*> SessionSet;
   typedef std::map<IPEndPoint, SessionSet> IPAliasMap;
@@ -349,9 +373,8 @@
 
   // Creates a job which doesn't wait for server config to be loaded from the
   // disk cache. This job is started via a PostTask.
-  void CreateAuxilaryJob(const QuicServerId server_id,
+  void CreateAuxilaryJob(const QuicSessionKey& key,
                          int cert_verify_flags,
-                         bool server_and_origin_have_same_host,
                          bool is_post,
                          const BoundNetLog& net_log);
 
@@ -359,19 +382,18 @@
   std::unique_ptr<QuicHttpStream> CreateFromSession(
       QuicChromiumClientSession* session);
 
-  bool OnResolution(const QuicServerId& server_id,
-                    const AddressList& address_list);
+  bool OnResolution(const QuicSessionKey& key, const AddressList& address_list);
   void OnJobComplete(Job* job, int rv);
   bool HasActiveSession(const QuicServerId& server_id) const;
   bool HasActiveJob(const QuicServerId& server_id) const;
-  int CreateSession(const QuicServerId& server_id,
+  int CreateSession(const QuicSessionKey& key,
                     int cert_verify_flags,
                     std::unique_ptr<QuicServerInfo> quic_server_info,
                     const AddressList& address_list,
                     base::TimeTicks dns_resolution_end_time,
                     const BoundNetLog& net_log,
                     QuicChromiumClientSession** session);
-  void ActivateSession(const QuicServerId& key,
+  void ActivateSession(const QuicSessionKey& key,
                        QuicChromiumClientSession* session);
 
   // Returns |srtt| in micro seconds from ServerNetworkStats. Returns 0 if there
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 89007ee5..dda32e5 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -4,6 +4,8 @@
 
 #include "net/quic/quic_stream_factory.h"
 
+#include <ostream>
+
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/thread_task_runner_handle.h"
@@ -44,7 +46,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::StringPiece;
-using std::ostream;
 using std::string;
 using std::vector;
 
@@ -53,25 +54,33 @@
 namespace test {
 
 namespace {
+
+enum DestinationType {
+  // In pooling tests with two requests for different origins to the same
+  // destination, the destination should be
+  SAME_AS_FIRST,   // the same as the first origin,
+  SAME_AS_SECOND,  // the same as the second origin, or
+  DIFFERENT,       // different from both.
+};
+
 const char kDefaultServerHostName[] = "www.example.org";
 const char kServer2HostName[] = "mail.example.org";
 const char kServer3HostName[] = "docs.example.org";
 const char kServer4HostName[] = "images.example.org";
+const char kDifferentHostname[] = "different.example.com";
 const int kDefaultServerPort = 443;
 const char kDefaultUrl[] = "https://www.example.org/";
 const char kServer2Url[] = "https://mail.example.org/";
 const char kServer3Url[] = "https://docs.example.org/";
 const char kServer4Url[] = "https://images.example.org/";
 
-// Run all tests with all the combinations of versions and
-// enable_connection_racing.
+// Run QuicStreamFactoryTest instances with all value combinations of version
+// and enable_connection_racting.
 struct TestParams {
-  TestParams(const QuicVersion version, bool enable_connection_racing)
-      : version(version), enable_connection_racing(enable_connection_racing) {}
-
-  friend ostream& operator<<(ostream& os, const TestParams& p) {
-    os << "{ version: " << QuicVersionToString(p.version);
-    os << " enable_connection_racing: " << p.enable_connection_racing << " }";
+  friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+    os << "{ version: " << QuicVersionToString(p.version)
+       << ", enable_connection_racing: "
+       << (p.enable_connection_racing ? "true" : "false") << " }";
     return os;
   }
 
@@ -79,19 +88,68 @@
   bool enable_connection_racing;
 };
 
-// Constructs various test permutations.
-vector<TestParams> GetTestParams() {
-  vector<TestParams> params;
+std::vector<TestParams> GetTestParams() {
+  std::vector<TestParams> params;
   QuicVersionVector all_supported_versions = QuicSupportedVersions();
   for (const QuicVersion version : all_supported_versions) {
-    params.push_back(TestParams(version, false));
-    params.push_back(TestParams(version, true));
+    params.push_back(TestParams{version, false});
+    params.push_back(TestParams{version, true});
+  }
+  return params;
+}
+
+// Run QuicStreamFactoryWithDestinationTest instances with all value
+// combinations of version, enable_connection_racting, and destination_type.
+struct PoolingTestParams {
+  friend std::ostream& operator<<(std::ostream& os,
+                                  const PoolingTestParams& p) {
+    os << "{ version: " << QuicVersionToString(p.version)
+       << ", enable_connection_racing: "
+       << (p.enable_connection_racing ? "true" : "false")
+       << ", destination_type: ";
+    switch (p.destination_type) {
+      case SAME_AS_FIRST:
+        os << "SAME_AS_FIRST";
+        break;
+      case SAME_AS_SECOND:
+        os << "SAME_AS_SECOND";
+        break;
+      case DIFFERENT:
+        os << "DIFFERENT";
+        break;
+    }
+    os << " }";
+    return os;
+  }
+
+  QuicVersion version;
+  bool enable_connection_racing;
+  DestinationType destination_type;
+};
+
+std::vector<PoolingTestParams> GetPoolingTestParams() {
+  std::vector<PoolingTestParams> params;
+  QuicVersionVector all_supported_versions = QuicSupportedVersions();
+  for (const QuicVersion version : all_supported_versions) {
+    params.push_back(PoolingTestParams{version, false, SAME_AS_FIRST});
+    params.push_back(PoolingTestParams{version, false, SAME_AS_SECOND});
+    params.push_back(PoolingTestParams{version, false, DIFFERENT});
+    params.push_back(PoolingTestParams{version, true, SAME_AS_FIRST});
+    params.push_back(PoolingTestParams{version, true, SAME_AS_SECOND});
+    params.push_back(PoolingTestParams{version, true, DIFFERENT});
   }
   return params;
 }
 
 }  // namespace
 
+class QuicHttpStreamPeer {
+ public:
+  static QuicChromiumClientSession* GetSession(QuicHttpStream* stream) {
+    return stream->session_.get();
+  }
+};
+
 class MockQuicServerInfo : public QuicServerInfo {
  public:
   explicit MockQuicServerInfo(const QuicServerId& server_id)
@@ -192,13 +250,14 @@
   std::unique_ptr<MockNetworkChangeNotifier> mock_network_change_notifier_;
 };
 
-class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> {
+class QuicStreamFactoryTestBase {
  protected:
-  QuicStreamFactoryTest()
+  QuicStreamFactoryTestBase(QuicVersion version, bool enable_connection_racing)
       : random_generator_(0),
         clock_(new MockClock()),
         runner_(new TestTaskRunner(clock_)),
-        maker_(GetParam().version, 0, clock_, kDefaultServerHostName),
+        version_(version),
+        maker_(version_, 0, clock_, kDefaultServerHostName),
         cert_verifier_(CertVerifier::CreateDefault()),
         channel_id_service_(
             new ChannelIDService(new DefaultChannelIDStore(nullptr),
@@ -216,7 +275,7 @@
         always_require_handshake_confirmation_(false),
         disable_connection_pooling_(false),
         load_server_info_timeout_srtt_multiplier_(0.0f),
-        enable_connection_racing_(GetParam().enable_connection_racing),
+        enable_connection_racing_(enable_connection_racing),
         enable_non_blocking_io_(true),
         disable_disk_cache_(false),
         prefer_aes_(false),
@@ -234,19 +293,27 @@
     clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
   }
 
+  ~QuicStreamFactoryTestBase() {
+    // If |factory_| was initialized, then it took over ownership of |clock_|.
+    // If |factory_| was not initialized, then |clock_| needs to be destroyed.
+    if (!factory_) {
+      delete clock_;
+    }
+  }
+
   void Initialize() {
+    DCHECK(!factory_);
     factory_.reset(new QuicStreamFactory(
         &host_resolver_, &socket_factory_, http_server_properties_.GetWeakPtr(),
         cert_verifier_.get(), nullptr, channel_id_service_.get(),
         &transport_security_state_, cert_transparency_verifier_.get(),
         /*SocketPerformanceWatcherFactory*/ nullptr,
         &crypto_client_stream_factory_, &random_generator_, clock_,
-        kDefaultMaxPacketSize, std::string(),
-        SupportedVersions(GetParam().version), enable_port_selection_,
-        always_require_handshake_confirmation_, disable_connection_pooling_,
-        load_server_info_timeout_srtt_multiplier_, enable_connection_racing_,
-        enable_non_blocking_io_, disable_disk_cache_, prefer_aes_,
-        max_number_of_lossy_connections_, packet_loss_threshold_,
+        kDefaultMaxPacketSize, std::string(), SupportedVersions(version_),
+        enable_port_selection_, always_require_handshake_confirmation_,
+        disable_connection_pooling_, load_server_info_timeout_srtt_multiplier_,
+        enable_connection_racing_, enable_non_blocking_io_, disable_disk_cache_,
+        prefer_aes_, max_number_of_lossy_connections_, packet_loss_threshold_,
         max_disabled_reasons_, threshold_timeouts_with_open_streams_,
         threshold_public_resets_post_handshake_, receive_buffer_size_,
         /*max_server_configs_stored_in_properties*/ 0,
@@ -349,7 +416,7 @@
     QuicStreamId stream_id = kClientDataStreamId1;
     return maker_.MakeRstPacket(
         1, true, stream_id,
-        AdjustErrorForVersion(QUIC_RST_ACKNOWLEDGEMENT, GetParam().version));
+        AdjustErrorForVersion(QUIC_RST_ACKNOWLEDGEMENT, version_));
   }
 
   static ProofVerifyDetailsChromium DefaultProofVerifyDetails() {
@@ -398,10 +465,10 @@
   MockHostResolver host_resolver_;
   MockClientSocketFactory socket_factory_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
-  ProofVerifyDetailsChromium verify_details_;
   MockRandom random_generator_;
-  MockClock* clock_;  // Owned by factory_.
+  MockClock* clock_;  // Owned by |factory_| once created.
   scoped_refptr<TestTaskRunner> runner_;
+  QuicVersion version_;
   QuicTestPacketMaker maker_;
   HttpServerPropertiesImpl http_server_properties_;
   std::unique_ptr<CertVerifier> cert_verifier_;
@@ -443,6 +510,14 @@
   bool migrate_sessions_early_;
 };
 
+class QuicStreamFactoryTest : public QuicStreamFactoryTestBase,
+                              public ::testing::TestWithParam<TestParams> {
+ protected:
+  QuicStreamFactoryTest()
+      : QuicStreamFactoryTestBase(GetParam().version,
+                                  GetParam().enable_connection_racing) {}
+};
+
 INSTANTIATE_TEST_CASE_P(Version,
                         QuicStreamFactoryTest,
                         ::testing::ValuesIn(GetTestParams()));
@@ -542,39 +617,6 @@
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
 
-TEST_P(QuicStreamFactoryTest, NoZeroRttForDifferentHost) {
-  Initialize();
-  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
-  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-
-  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
-  SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0);
-  socket_factory_.AddSocketDataProvider(&socket_data);
-
-  crypto_client_stream_factory_.set_handshake_mode(
-      MockCryptoClientStream::ZERO_RTT);
-  host_resolver_.set_synchronous_mode(true);
-  host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
-                                           "192.168.0.1", "");
-
-  QuicStreamRequest request(factory_.get());
-  int rv = request.Request(host_port_pair_, privacy_mode_,
-                           /*cert_verify_flags=*/0, url2_, "GET", net_log_,
-                           callback_.callback());
-  // If server and origin have different hostnames, then handshake confirmation
-  // should be required, so Request will return asynchronously.
-  EXPECT_EQ(ERR_IO_PENDING, rv);
-  // Confirm handshake.
-  crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
-      QuicSession::HANDSHAKE_CONFIRMED);
-  EXPECT_EQ(OK, callback_.WaitForResult());
-
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
-  EXPECT_TRUE(stream.get());
-  EXPECT_TRUE(socket_data.AllReadDataConsumed());
-  EXPECT_TRUE(socket_data.AllWriteDataConsumed());
-}
-
 TEST_P(QuicStreamFactoryTest, GoAway) {
   Initialize();
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -855,77 +897,6 @@
   EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
 }
 
-class QuicAlternativeServiceCertificateValidationPooling
-    : public QuicStreamFactoryTest {
- public:
-  void Run(bool valid) {
-    MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
-    SequencedSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
-    socket_factory_.AddSocketDataProvider(&socket_data1);
-
-    HostPortPair server1(kDefaultServerHostName, 443);
-    HostPortPair server2(kServer2HostName, 443);
-
-    GURL url(valid ? url2_ : GURL("http://invalid.example.com/"));
-    HostPortPair alternative(kDefaultServerHostName, 443);
-
-    ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
-    bool common_name_fallback_used;
-    EXPECT_EQ(valid,
-              verify_details.cert_verify_result.verified_cert->VerifyNameMatch(
-                  url.host(), &common_name_fallback_used));
-    EXPECT_TRUE(
-        verify_details.cert_verify_result.verified_cert->VerifyNameMatch(
-            alternative.host(), &common_name_fallback_used));
-    crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-
-    host_resolver_.set_synchronous_mode(true);
-    host_resolver_.rules()->AddIPLiteralRule(alternative.host(), "192.168.0.1",
-                                             "");
-
-    // Open first stream to alternative.
-    QuicStreamRequest request1(factory_.get());
-    EXPECT_EQ(OK, request1.Request(alternative, privacy_mode_,
-                                   /*cert_verify_flags=*/0, url_, "GET",
-                                   net_log_, callback_.callback()));
-    std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
-    EXPECT_TRUE(stream1.get());
-
-    QuicStreamRequest request2(factory_.get());
-    int rv = request2.Request(alternative, privacy_mode_,
-                              /*cert_verify_flags=*/0, url, "GET", net_log_,
-                              callback_.callback());
-    if (valid) {
-      // Alternative service of origin to |alternative| should pool to session
-      // of |stream1| even if origin is different.  Since only one
-      // SocketDataProvider is set up, the second request succeeding means that
-      // it pooled to the session opened by the first one.
-      EXPECT_EQ(OK, rv);
-      std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
-      EXPECT_TRUE(stream2.get());
-    } else {
-      EXPECT_EQ(ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN, rv);
-    }
-
-    EXPECT_TRUE(socket_data1.AllReadDataConsumed());
-    EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
-  }
-};
-
-INSTANTIATE_TEST_CASE_P(Version,
-                        QuicAlternativeServiceCertificateValidationPooling,
-                        ::testing::ValuesIn(GetTestParams()));
-
-TEST_P(QuicAlternativeServiceCertificateValidationPooling, Valid) {
-  Initialize();
-  Run(true);
-}
-
-TEST_P(QuicAlternativeServiceCertificateValidationPooling, Invalid) {
-  Initialize();
-  Run(false);
-}
-
 TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) {
   Initialize();
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
@@ -2638,7 +2609,7 @@
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  if (!GetParam().enable_connection_racing)
+  if (!enable_connection_racing_)
     return;
 
   QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
@@ -4084,5 +4055,270 @@
   EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
 }
 
+// Pool to existing session with matching QuicServerId
+// even if destination is different.
+TEST_P(QuicStreamFactoryTest, PoolByOrigin) {
+  Initialize();
+
+  HostPortPair destination1("first.example.com", 443);
+  HostPortPair destination2("second.example.com", 443);
+
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+  socket_factory_.AddSocketDataProvider(&socket_data);
+
+  QuicStreamRequest request1(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request1.Request(destination1, privacy_mode_,
+                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
+                             callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  EXPECT_TRUE(stream1.get());
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Second request returns synchronously because it pools to existing session.
+  TestCompletionCallback callback2;
+  QuicStreamRequest request2(factory_.get());
+  EXPECT_EQ(OK, request2.Request(destination2, privacy_mode_,
+                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
+                                 callback2.callback()));
+  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  EXPECT_TRUE(stream2.get());
+
+  QuicChromiumClientSession* session1 =
+      QuicHttpStreamPeer::GetSession(stream1.get());
+  QuicChromiumClientSession* session2 =
+      QuicHttpStreamPeer::GetSession(stream2.get());
+  EXPECT_EQ(session1, session2);
+  EXPECT_EQ(QuicServerId(host_port_pair_, privacy_mode_),
+            session1->server_id());
+
+  EXPECT_TRUE(socket_data.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data.AllWriteDataConsumed());
+}
+
+class QuicStreamFactoryWithDestinationTest
+    : public QuicStreamFactoryTestBase,
+      public ::testing::TestWithParam<PoolingTestParams> {
+ protected:
+  QuicStreamFactoryWithDestinationTest()
+      : QuicStreamFactoryTestBase(GetParam().version,
+                                  GetParam().enable_connection_racing),
+        destination_type_(GetParam().destination_type),
+        hanging_read_(SYNCHRONOUS, ERR_IO_PENDING, 0) {}
+
+  HostPortPair GetDestination() {
+    switch (destination_type_) {
+      case SAME_AS_FIRST:
+        return origin1_;
+      case SAME_AS_SECOND:
+        return origin2_;
+      case DIFFERENT:
+        return HostPortPair(kDifferentHostname, 443);
+      default:
+        NOTREACHED();
+        return HostPortPair();
+    }
+  }
+
+  void AddHangingSocketData() {
+    std::unique_ptr<SequencedSocketData> sequenced_socket_data(
+        new SequencedSocketData(&hanging_read_, 1, nullptr, 0));
+    socket_factory_.AddSocketDataProvider(sequenced_socket_data.get());
+    sequenced_socket_data_vector_.push_back(std::move(sequenced_socket_data));
+  }
+
+  bool AllDataConsumed() {
+    for (const auto& socket_data_ptr : sequenced_socket_data_vector_) {
+      if (!socket_data_ptr->AllReadDataConsumed() ||
+          !socket_data_ptr->AllWriteDataConsumed()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  DestinationType destination_type_;
+  HostPortPair origin1_;
+  HostPortPair origin2_;
+  MockRead hanging_read_;
+  std::vector<std::unique_ptr<SequencedSocketData>>
+      sequenced_socket_data_vector_;
+};
+
+INSTANTIATE_TEST_CASE_P(Version,
+                        QuicStreamFactoryWithDestinationTest,
+                        ::testing::ValuesIn(GetPoolingTestParams()));
+
+// A single QUIC request fails because the certificate does not match the origin
+// hostname, regardless of whether it matches the alternative service hostname.
+TEST_P(QuicStreamFactoryWithDestinationTest, InvalidCertificate) {
+  if (destination_type_ == DIFFERENT)
+    return;
+
+  Initialize();
+
+  GURL url("https://mail.example.com/");
+  origin1_ = HostPortPair::FromURL(url);
+
+  // Not used for requests, but this provides a test case where the certificate
+  // is valid for the hostname of the alternative service.
+  origin2_ = HostPortPair("mail.example.org", 433);
+
+  HostPortPair destination = GetDestination();
+
+  scoped_refptr<X509Certificate> cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  bool unused;
+  ASSERT_FALSE(cert->VerifyNameMatch(origin1_.host(), &unused));
+  ASSERT_TRUE(cert->VerifyNameMatch(origin2_.host(), &unused));
+
+  ProofVerifyDetailsChromium verify_details;
+  verify_details.cert_verify_result.verified_cert = cert;
+  verify_details.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  AddHangingSocketData();
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING, request.Request(destination, privacy_mode_,
+                                            /*cert_verify_flags=*/0, url, "GET",
+                                            net_log_, callback_.callback()));
+
+  EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
+
+  EXPECT_TRUE(AllDataConsumed());
+}
+
+// QuicStreamRequest is pooled based on |destination| if certificate matches.
+TEST_P(QuicStreamFactoryWithDestinationTest, SharedCertificate) {
+  Initialize();
+
+  GURL url1("https://www.example.org/");
+  GURL url2("https://mail.example.org/");
+  origin1_ = HostPortPair::FromURL(url1);
+  origin2_ = HostPortPair::FromURL(url2);
+
+  HostPortPair destination = GetDestination();
+
+  scoped_refptr<X509Certificate> cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  bool unused;
+  ASSERT_TRUE(cert->VerifyNameMatch(origin1_.host(), &unused));
+  ASSERT_TRUE(cert->VerifyNameMatch(origin2_.host(), &unused));
+  ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname, &unused));
+
+  ProofVerifyDetailsChromium verify_details;
+  verify_details.cert_verify_result.verified_cert = cert;
+  verify_details.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  AddHangingSocketData();
+
+  QuicStreamRequest request1(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request1.Request(destination, privacy_mode_,
+                             /*cert_verify_flags=*/0, url1, "GET", net_log_,
+                             callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  EXPECT_TRUE(stream1.get());
+  EXPECT_TRUE(HasActiveSession(origin1_));
+
+  // Second request returns synchronously because it pools to existing session.
+  TestCompletionCallback callback2;
+  QuicStreamRequest request2(factory_.get());
+  EXPECT_EQ(OK, request2.Request(destination, privacy_mode_,
+                                 /*cert_verify_flags=*/0, url2, "GET", net_log_,
+                                 callback2.callback()));
+  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  EXPECT_TRUE(stream2.get());
+
+  QuicChromiumClientSession* session1 =
+      QuicHttpStreamPeer::GetSession(stream1.get());
+  QuicChromiumClientSession* session2 =
+      QuicHttpStreamPeer::GetSession(stream2.get());
+  EXPECT_EQ(session1, session2);
+
+  EXPECT_EQ(QuicServerId(origin1_, privacy_mode_), session1->server_id());
+
+  EXPECT_TRUE(AllDataConsumed());
+}
+
+// QuicStreamRequest is not pooled if certificate does not match its origin.
+TEST_P(QuicStreamFactoryWithDestinationTest, DisjointCertificate) {
+  Initialize();
+
+  GURL url1("https://news.example.org/");
+  GURL url2("https://mail.example.com/");
+  origin1_ = HostPortPair::FromURL(url1);
+  origin2_ = HostPortPair::FromURL(url2);
+
+  HostPortPair destination = GetDestination();
+
+  scoped_refptr<X509Certificate> cert1(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  bool unused;
+  ASSERT_TRUE(cert1->VerifyNameMatch(origin1_.host(), &unused));
+  ASSERT_FALSE(cert1->VerifyNameMatch(origin2_.host(), &unused));
+  ASSERT_FALSE(cert1->VerifyNameMatch(kDifferentHostname, &unused));
+
+  ProofVerifyDetailsChromium verify_details1;
+  verify_details1.cert_verify_result.verified_cert = cert1;
+  verify_details1.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details1);
+
+  scoped_refptr<X509Certificate> cert2(
+      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"));
+  ASSERT_TRUE(cert2->VerifyNameMatch(origin2_.host(), &unused));
+  ASSERT_FALSE(cert2->VerifyNameMatch(kDifferentHostname, &unused));
+
+  ProofVerifyDetailsChromium verify_details2;
+  verify_details2.cert_verify_result.verified_cert = cert2;
+  verify_details2.cert_verify_result.is_issued_by_known_root = true;
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details2);
+
+  AddHangingSocketData();
+  AddHangingSocketData();
+
+  QuicStreamRequest request1(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request1.Request(destination, privacy_mode_,
+                             /*cert_verify_flags=*/0, url1, "GET", net_log_,
+                             callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  EXPECT_TRUE(stream1.get());
+  EXPECT_TRUE(HasActiveSession(origin1_));
+
+  TestCompletionCallback callback2;
+  QuicStreamRequest request2(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request2.Request(destination, privacy_mode_,
+                             /*cert_verify_flags=*/0, url2, "GET", net_log_,
+                             callback2.callback()));
+  EXPECT_EQ(OK, callback2.WaitForResult());
+  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  EXPECT_TRUE(stream2.get());
+
+  // |request2| does not pool to the first session, because the certificate does
+  // not match.  Instead, another session is opened to the same destination, but
+  // with a different QuicServerId.
+  QuicChromiumClientSession* session1 =
+      QuicHttpStreamPeer::GetSession(stream1.get());
+  QuicChromiumClientSession* session2 =
+      QuicHttpStreamPeer::GetSession(stream2.get());
+  EXPECT_NE(session1, session2);
+
+  EXPECT_EQ(QuicServerId(origin1_, privacy_mode_), session1->server_id());
+  EXPECT_EQ(QuicServerId(origin2_, privacy_mode_), session2->server_id());
+
+  EXPECT_TRUE(AllDataConsumed());
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/ssl/client_cert_store_win.cc b/net/ssl/client_cert_store_win.cc
index 1ebf02d..499d1c4 100644
--- a/net/ssl/client_cert_store_win.cc
+++ b/net/ssl/client_cert_store_win.cc
@@ -128,6 +128,20 @@
       if (ok)
         intermediates.push_back(copied_intermediate);
     }
+
+    // Drop the self-signed root, if any. Match Internet Explorer in not sending
+    // it. Although the root's signature is irrelevant for authentication, some
+    // servers reject chains if the root is explicitly sent and has a weak
+    // signature algorithm. See https://crbug.com/607264.
+    //
+    // The leaf or a intermediate may also have a weak signature algorithm but,
+    // in that case, assume it is a configuration error.
+    if (!intermediates.empty() &&
+        X509Certificate::IsSelfSigned(intermediates.back())) {
+      CertFreeCertificateContext(intermediates.back());
+      intermediates.pop_back();
+    }
+
     // TODO(svaldez): cert currently wraps cert_context2 which may be backed
     // by a smartcard with threading difficulties. Instead, create a fresh
     // X509Certificate with CreateFromBytes and route cert_context2 into the
diff --git a/net/url_request/url_request_data_job_fuzzer.cc b/net/url_request/url_request_data_job_fuzzer.cc
new file mode 100644
index 0000000..c99a5a0c
--- /dev/null
+++ b/net/url_request/url_request_data_job_fuzzer.cc
@@ -0,0 +1,171 @@
+// Copyright 2016 The Chromium 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 <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/run_loop.h"
+#include "net/base/fuzzed_data_provider.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/data_protocol_handler.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "net/url_request/url_request_test_util.h"
+
+namespace {
+
+const size_t kMaxLengthForFuzzedRange = 32;
+
+}  // namespace
+
+// This class tests creating and reading to completion a URLRequest with fuzzed
+// input. The fuzzer provides a data: URL and optionally generates custom Range
+// headers. The amount of data read in each Read call is also fuzzed, as is
+// the size of the IOBuffer to read data into.
+class URLRequestDataJobFuzzerHarness : public net::URLRequest::Delegate {
+ public:
+  URLRequestDataJobFuzzerHarness()
+      : context_(true), task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+    job_factory_.SetProtocolHandler(
+        "data", base::WrapUnique(new net::DataProtocolHandler()));
+    context_.set_job_factory(&job_factory_);
+    context_.Init();
+  }
+
+  static URLRequestDataJobFuzzerHarness* GetInstance() {
+    return base::Singleton<URLRequestDataJobFuzzerHarness>::get();
+  }
+
+  int CreateAndReadFromDataURLRequest(const uint8_t* data, size_t size) {
+    net::FuzzedDataProvider provider(data, size);
+    read_lengths_.clear();
+
+    // Allocate an IOBuffer with fuzzed size.
+    uint32_t buf_size = provider.ConsumeValueInRange(1, 127);  // 7 bits.
+    scoped_refptr<net::IOBuffer> buf(
+        new net::IOBuffer(static_cast<size_t>(buf_size)));
+    buf_.swap(buf);
+
+    // Generate a range header, and a bool determining whether to use it.
+    // Generate the header regardless of the bool value to keep the data URL and
+    // header in consistent byte addresses so the fuzzer doesn't have to work as
+    // hard.
+    bool use_range = provider.ConsumeBool();
+    base::StringPiece range(provider.ConsumeBytes(kMaxLengthForFuzzedRange));
+
+    // Generate a sequence of reads sufficient to read the entire data URL.
+    size_t simulated_bytes_read = 0;
+    while (simulated_bytes_read < provider.remaining_bytes()) {
+      size_t read_length = provider.ConsumeValueInRange(1, buf_size);
+      read_lengths_.push_back(read_length);
+      simulated_bytes_read += read_length;
+    }
+
+    // The data URL is the rest of the fuzzed data. If the URL is invalid just
+    // use a test variant, so the fuzzer has a chance to execute something.
+    base::StringPiece data_bytes(provider.ConsumeRemainingBytes());
+    GURL data_url(data_bytes);
+    if (!data_url.is_valid())
+      data_url = GURL("data:text/html;charset=utf-8,<p>test</p>");
+
+    // Create a URLRequest with the given data URL and start reading
+    // from it.
+    std::unique_ptr<net::URLRequest> request =
+        context_.CreateRequest(data_url, net::DEFAULT_PRIORITY, this);
+    if (use_range) {
+      std::string range_str = range.as_string();
+      if (!net::HttpUtil::IsValidHeaderValue(range_str))
+        range_str = "bytes=3-";
+      request->SetExtraRequestHeaderByName("Range", range_str, true);
+    }
+
+    // Block the thread while the request is read.
+    base::RunLoop read_loop;
+    read_loop_ = &read_loop;
+    request->Start();
+    read_loop.Run();
+    read_loop_ = nullptr;
+    return 0;
+  }
+
+  void QuitLoop() {
+    DCHECK(read_loop_);
+    task_runner_->PostTask(FROM_HERE, read_loop_->QuitClosure());
+  }
+
+  void ReadFromRequest(net::URLRequest* request) {
+    bool sync = false;
+    do {
+      // If possible, pop the next read size. If none exists, then this should
+      // be the last call to Read.
+      bool using_populated_read = read_lengths_.size() > 0;
+      size_t read_size = 1;
+      if (using_populated_read) {
+        read_size = read_lengths_.back();
+        read_lengths_.pop_back();
+      }
+
+      int bytes_read = 0;
+      sync = request->Read(buf_.get(), read_size, &bytes_read);
+      // No more populated reads implies !bytes_read.
+      DCHECK(using_populated_read || !bytes_read);
+    } while (sync);
+
+    if (!request->status().is_io_pending())
+      QuitLoop();
+  }
+
+  // net::URLRequest::Delegate:
+  void OnReceivedRedirect(net::URLRequest* request,
+                          const net::RedirectInfo& redirect_info,
+                          bool* defer_redirect) override {}
+  void OnAuthRequired(net::URLRequest* request,
+                      net::AuthChallengeInfo* auth_info) override {}
+  void OnCertificateRequested(
+      net::URLRequest* request,
+      net::SSLCertRequestInfo* cert_request_info) override {}
+  void OnSSLCertificateError(net::URLRequest* request,
+                             const net::SSLInfo& ssl_info,
+                             bool fatal) override {}
+  void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override {}
+  void OnResponseStarted(net::URLRequest* request) override {
+    DCHECK(!request->status().is_io_pending());
+    DCHECK(buf_.get());
+    DCHECK(read_loop_);
+    if (request->status().is_success()) {
+      ReadFromRequest(request);
+    } else {
+      QuitLoop();
+    }
+  }
+  void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
+    DCHECK(!request->status().is_io_pending());
+    DCHECK(buf_.get());
+    DCHECK(read_loop_);
+    if (request->status().is_success() && bytes_read > 0) {
+      ReadFromRequest(request);
+    } else {
+      QuitLoop();
+    }
+  }
+
+ private:
+  friend struct base::DefaultSingletonTraits<URLRequestDataJobFuzzerHarness>;
+
+  net::TestURLRequestContext context_;
+  net::URLRequestJobFactoryImpl job_factory_;
+  std::vector<size_t> read_lengths_;
+  scoped_refptr<net::IOBuffer> buf_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::RunLoop* read_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestDataJobFuzzerHarness);
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Using a static singleton test harness lets the test run ~3-4x faster.
+  return URLRequestDataJobFuzzerHarness::GetInstance()
+      ->CreateAndReadFromDataURLRequest(data, size);
+}
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index c38baaf..974b795c 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -141,8 +141,7 @@
       ephemerality = CID_EPHEMERAL_COOKIE_PERSISTENT;
     }
   } else if (cookie_store->IsEphemeral()) {
-    // TODO(crbug.com/599049): Add NOTREACHED once this case doesn't happen on
-    // iOS anymore.
+    NOTREACHED();
     ephemerality = CID_PERSISTENT_COOKIE_EPHEMERAL;
   } else if (cookie_store->GetChannelIDServiceID() == -1) {
     ephemerality = PERSISTENT_UNKNOWN;
diff --git a/ppapi/BUILD.gn b/ppapi/BUILD.gn
index 839bbcab..364e11a 100644
--- a/ppapi/BUILD.gn
+++ b/ppapi/BUILD.gn
@@ -247,6 +247,7 @@
     # link time as the test executable requires symbols defined in the
     # shared library.
     configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+    configs += [ "//build/config/gcc:symbol_visibility_default" ]
 
     sources = ppapi_sources.cpp_source_files
     sources += [
diff --git a/ppapi/native_client/src/untrusted/irt_stub/BUILD.gn b/ppapi/native_client/src/untrusted/irt_stub/BUILD.gn
index 0ce018b..a492353d 100644
--- a/ppapi/native_client/src/untrusted/irt_stub/BUILD.gn
+++ b/ppapi/native_client/src/untrusted/irt_stub/BUILD.gn
@@ -17,6 +17,7 @@
     "thread_creator.c",
   ]
   configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+  configs += [ "//build/config/gcc:symbol_visibility_default" ]
   cflags = [ "-fPIC" ]
   deps = [
     "//build/config/nacl:nacl_base",
diff --git a/remoting/remoting_srcs.gypi b/remoting/remoting_srcs.gypi
index 838c5d5..035404db8 100644
--- a/remoting/remoting_srcs.gypi
+++ b/remoting/remoting_srcs.gypi
@@ -251,6 +251,8 @@
     ],
 
     'remoting_signaling_sources': [
+      'signaling/chromoting_event.cc',
+      'signaling/chromoting_event.h',
       'signaling/iq_sender.cc',
       'signaling/iq_sender.h',
       'signaling/jid_util.cc',
@@ -262,6 +264,8 @@
       'signaling/server_log_entry.cc',
       'signaling/server_log_entry.h',
       'signaling/signal_strategy.h',
+      'signaling/telemetry_log_writer.cc',
+      'signaling/telemetry_log_writer.h',
       'signaling/xmpp_signal_strategy.cc',
       'signaling/xmpp_signal_strategy.h',
       'signaling/xmpp_stream_parser.cc',
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index 9be0e35..9184abc 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -365,6 +365,7 @@
         'signaling/push_notification_subscriber_unittest.cc',
         'signaling/server_log_entry_unittest.cc',
         'signaling/server_log_entry_unittest.h',
+        'signaling/telemetry_log_writer_unittest.cc',
         'signaling/xmpp_login_handler_unittest.cc',
         'signaling/xmpp_signal_strategy_unittest.cc',
         'signaling/xmpp_stream_parser_unittest.cc',
diff --git a/remoting/signaling/BUILD.gn b/remoting/signaling/BUILD.gn
index db2cf98..7afaad37 100644
--- a/remoting/signaling/BUILD.gn
+++ b/remoting/signaling/BUILD.gn
@@ -29,8 +29,10 @@
 
   if (is_nacl) {
     sources -= [
+      "chromoting_event.cc",
       "log_to_server.cc",
       "server_log_entry.cc",
+      "telemetry_log_writer.cc",
       "xmpp_signal_strategy.cc",
     ]
   }
@@ -62,6 +64,7 @@
     "push_notification_subscriber_unittest.cc",
     "server_log_entry_unittest.cc",
     "server_log_entry_unittest.h",
+    "telemetry_log_writer_unittest.cc",
     "xmpp_login_handler_unittest.cc",
     "xmpp_signal_strategy_unittest.cc",
     "xmpp_stream_parser_unittest.cc",
diff --git a/remoting/signaling/chromoting_event.cc b/remoting/signaling/chromoting_event.cc
new file mode 100644
index 0000000..7b52c9be
--- /dev/null
+++ b/remoting/signaling/chromoting_event.cc
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium 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 "remoting/signaling/chromoting_event.h"
+
+#include "base/sys_info.h"
+
+namespace remoting {
+
+namespace {
+
+const char kKeyType[] = "type";
+const char kKeyCpu[] = "cpu";
+const char kKeyOs[] = "os";
+const char kKeyOsVersion[] = "os_version";
+
+}  // namespace
+
+ChromotingEvent::ChromotingEvent() : values_map_(new base::DictionaryValue()) {}
+
+ChromotingEvent::ChromotingEvent(Type type) : ChromotingEvent() {
+  SetEnum(kKeyType, type);
+}
+
+ChromotingEvent::ChromotingEvent(const ChromotingEvent& other) {
+  try_count_ = other.try_count_;
+  values_map_.reset(other.values_map_->DeepCopy());
+}
+
+ChromotingEvent::ChromotingEvent(ChromotingEvent&& other) {
+  try_count_ = other.try_count_;
+  values_map_ = std::move(other.values_map_);
+}
+
+ChromotingEvent::~ChromotingEvent() {}
+
+ChromotingEvent& ChromotingEvent::operator=(const ChromotingEvent& other) {
+  if (this != &other) {
+    try_count_ = other.try_count_;
+    values_map_.reset(other.values_map_->DeepCopy());
+  }
+  return *this;
+}
+
+ChromotingEvent& ChromotingEvent::operator=(ChromotingEvent&& other) {
+  try_count_ = other.try_count_;
+  values_map_ = std::move(other.values_map_);
+  return *this;
+}
+
+void ChromotingEvent::SetString(const std::string& key,
+                                const std::string& value) {
+  values_map_->SetString(key, value);
+}
+
+void ChromotingEvent::SetInteger(const std::string& key, int value) {
+  values_map_->SetInteger(key, value);
+}
+
+void ChromotingEvent::SetBoolean(const std::string& key, bool value) {
+  values_map_->SetBoolean(key, value);
+}
+
+void ChromotingEvent::SetDouble(const std::string& key, double value) {
+  values_map_->SetDouble(key, value);
+}
+
+void ChromotingEvent::AddSystemInfo() {
+  SetString(kKeyCpu, base::SysInfo::OperatingSystemArchitecture());
+  SetString(kKeyOsVersion, base::SysInfo::OperatingSystemVersion());
+  std::string osName = base::SysInfo::OperatingSystemName();
+#if defined(OS_LINUX)
+  Os os = Os::CHROMOTING_LINUX;
+#elif defined(OS_CHROMEOS)
+  Os os = Os::CHROMOTING_CHROMEOS;
+#elif defined(OS_MACOSX)
+  Os os = Os::CHROMOTING_MAC;
+#elif defined(OS_WIN)
+  Os os = Os::CHROMOTING_WINDOWS;
+#elif defined(OS_ANDROID)
+  Os os = Os::CHROMOTING_ANDROID;
+#elif defined(OS_IOS)
+  Os os = Os::CHROMOTING_IOS;
+#else
+  Os os = Os::OTHER;
+#endif
+  SetEnum(kKeyOs, os);
+}
+
+void ChromotingEvent::IncrementTryCount() {
+  try_count_++;
+}
+
+std::unique_ptr<base::DictionaryValue> ChromotingEvent::CopyDictionaryValue()
+    const {
+  return std::unique_ptr<base::DictionaryValue>(values_map_->DeepCopy());
+}
+
+// static
+bool ChromotingEvent::IsEndOfSession(SessionState state) {
+  return state == SessionState::CLOSED ||
+         state == SessionState::CONNECTION_DROPPED ||
+         state == SessionState::CONNECTION_FAILED ||
+         state == SessionState::CONNECTION_CANCELED;
+}
+
+}  // namespace remoting
diff --git a/remoting/signaling/chromoting_event.h b/remoting/signaling/chromoting_event.h
new file mode 100644
index 0000000..90cef03
--- /dev/null
+++ b/remoting/signaling/chromoting_event.h
@@ -0,0 +1,128 @@
+// Copyright 2016 The Chromium 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 REMOTING_SIGNALING_CHROMOTING_EVENT_H_
+#define REMOTING_SIGNALING_CHROMOTING_EVENT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/values.h"
+
+namespace remoting {
+
+// This is the representation of the log entry being sent to the telemetry
+// server. The content should be synced with chromoting_event.js and
+// chromoting_extensions.proto.
+class ChromotingEvent {
+ public:
+  enum class ConnectionError {
+    NONE = 1,
+    HOST_OFFLINE = 2,
+    SESSION_REJECTED = 3,
+    INCOMPATIBLE_PROTOCOL = 4,
+    NETWORK_FAILURE = 5,
+    UNKNOWN_ERROR = 6,
+    INVALID_ACCESS_CODE = 7,
+    MISSING_PLUGIN = 8,
+    AUTHENTICATION_FAILED = 9,
+    BAD_VERSION = 10,
+    HOST_OVERLOAD = 11,
+    P2P_FAILURE = 12,
+    UNEXPECTED = 13,
+    CLIENT_SUSPENDED = 14,
+    NACL_DISABLED = 15,
+    MAX_SESSION_LENGTH = 16,
+    HOST_CONFIGURATION_ERROR = 17,
+    NACL_PLUGIN_CRASHED = 18
+  };
+
+  enum class Mode { IT2ME = 1, ME2ME = 2, LGAPP = 3 };
+
+  // macro defines from command line have polluted OS names like
+  // "LINUX", "ANDROID", etc.
+  enum class Os {
+    CHROMOTING_LINUX = 1,
+    CHROMOTING_CHROMEOS = 2,
+    CHROMOTING_MAC = 3,
+    CHROMOTING_WINDOWS = 4,
+    OTHER = 5,
+    CHROMOTING_ANDROID = 6,
+    CHROMOTING_IOS = 7
+  };
+
+  enum class Role { CLIENT = 0, HOST = 1 };
+
+  enum class SessionState {
+    UNKNOWN = 1,               // deprecated.
+    CREATED = 2,               // deprecated.
+    BAD_PLUGIN_VERSION = 3,    // deprecated.
+    UNKNOWN_PLUGIN_ERROR = 4,  // deprecated.
+    CONNECTING = 5,
+    INITIALIZING = 6,  // deprecated.
+    CONNECTED = 7,
+    CLOSED = 8,
+    CONNECTION_FAILED = 9,
+    UNDEFINED = 10,
+    PLUGIN_DISABLED = 11,  // deprecated.
+    CONNECTION_DROPPED = 12,
+    CONNECTION_CANCELED = 13,
+    AUTHENTICATED = 14,
+    STARTED = 15,
+    SIGNALING = 16,
+    CREATING_PLUGIN = 17,
+  };
+
+  enum class Type {
+    SESSION_STATE = 1,
+    CONNECTION_STATISTICS = 2,
+    SESSION_ID_OLD = 3,
+    SESSION_ID_NEW = 4,
+    HEARTBEAT = 5,
+    HEARTBEAT_REJECTED = 6,
+    RESTART = 7,
+    HOST_STATUS = 8,
+    SIGNAL_STRATEGY_PROGRESS = 9
+  };
+
+  ChromotingEvent();
+  explicit ChromotingEvent(Type type);
+  ChromotingEvent(const ChromotingEvent& other);
+  ChromotingEvent(ChromotingEvent&& other);
+
+  ~ChromotingEvent();
+
+  ChromotingEvent& operator=(const ChromotingEvent& other);
+  ChromotingEvent& operator=(ChromotingEvent&& other);
+
+  // Sets an arbitrary key/value entry.
+  void SetString(const std::string& key, const std::string& value);
+  void SetInteger(const std::string& key, int value);
+  void SetBoolean(const std::string& key, bool value);
+  void SetDouble(const std::string& key, double value);
+  template <typename E>
+  void SetEnum(const std::string& key, E e) {
+    SetInteger(key, static_cast<int>(e));
+  }
+
+  // Adds fields of CPU type, OS type and OS version.
+  void AddSystemInfo();
+
+  void IncrementTryCount();
+  int try_count() const { return try_count_; }
+
+  // Returns a copy of the internal dictionary value.
+  std::unique_ptr<base::DictionaryValue> CopyDictionaryValue() const;
+
+  // Returns true if the SessionState concludes the end of session.
+  static bool IsEndOfSession(SessionState state);
+
+ private:
+  std::unique_ptr<base::DictionaryValue> values_map_;
+
+  int try_count_ = 0;
+};
+
+}  // namespace remoting
+#endif  // REMOTING_SIGNALING_CHROMOTING_EVENT_H_
diff --git a/remoting/signaling/telemetry_log_writer.cc b/remoting/signaling/telemetry_log_writer.cc
new file mode 100644
index 0000000..5e5b77c
--- /dev/null
+++ b/remoting/signaling/telemetry_log_writer.cc
@@ -0,0 +1,115 @@
+// Copyright 2016 The Chromium 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 "remoting/signaling/telemetry_log_writer.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "net/http/http_status_code.h"
+
+namespace remoting {
+
+const int kMaxTries = 5;
+
+TelemetryLogWriter::TelemetryLogWriter(
+    const std::string& telemetry_base_url,
+    std::unique_ptr<UrlRequestFactory> request_factory)
+    : telemetry_base_url_(telemetry_base_url),
+      request_factory_(std::move(request_factory)) {}
+TelemetryLogWriter::~TelemetryLogWriter() {}
+
+void TelemetryLogWriter::SetAuthToken(const std::string& auth_token) {
+  DCHECK(CalledOnValidThread());
+  auth_token_ = auth_token;
+  SendPendingEntries();
+}
+
+void TelemetryLogWriter::SetAuthClosure(base::Closure closure) {
+  DCHECK(CalledOnValidThread());
+  auth_closure_ = closure;
+}
+
+void TelemetryLogWriter::Log(const ChromotingEvent& entry) {
+  DCHECK(CalledOnValidThread());
+  pending_entries_.push_back(entry);
+  SendPendingEntries();
+}
+
+void TelemetryLogWriter::SendPendingEntries() {
+  DCHECK(CalledOnValidThread());
+  if (request_ || pending_entries_.empty()) {
+    return;
+  }
+
+  base::ListValue* events = new base::ListValue();
+  while (!pending_entries_.empty()) {
+    ChromotingEvent& entry = pending_entries_.front();
+    events->Append(entry.CopyDictionaryValue());
+    entry.IncrementTryCount();
+    sending_entries_.push_back(std::move(entry));
+    pending_entries_.pop_front();
+  }
+  base::DictionaryValue log_dictionary;
+  log_dictionary.Set("event", std::unique_ptr<base::Value>(events));
+
+  std::string json;
+  JSONStringValueSerializer serializer(&json);
+  if (!serializer.Serialize(log_dictionary)) {
+    LOG(ERROR) << "Failed to serialize log to JSON.";
+    return;
+  }
+  PostJsonToServer(json);
+}
+
+void TelemetryLogWriter::PostJsonToServer(const std::string& json) {
+  DCHECK(CalledOnValidThread());
+  DCHECK(!request_);
+  request_ = request_factory_->CreateUrlRequest(UrlRequest::Type::POST,
+                                                telemetry_base_url_);
+  if (!auth_token_.empty()) {
+    request_->AddHeader("Authorization:Bearer " + auth_token_);
+  }
+
+  VLOG(1) << "Posting log to telemetry server: " << json;
+
+  request_->SetPostData("application/json", json);
+  request_->Start(
+      base::Bind(&TelemetryLogWriter::OnSendLogResult, base::Unretained(this)));
+}
+
+void TelemetryLogWriter::OnSendLogResult(
+    const remoting::UrlRequest::Result& result) {
+  DCHECK(CalledOnValidThread());
+  DCHECK(request_);
+  if (!result.success || result.status != net::HTTP_OK) {
+    LOG(WARNING) << "Error occur when sending logs to the telemetry server, "
+                 << "status: " << result.status;
+    VLOG(1) << "Response body: \n"
+            << "body: " << result.response_body;
+  } else {
+    VLOG(1) << "Successfully sent " << sending_entries_.size()
+            << " log(s) to telemetry server.";
+    sending_entries_.clear();
+  }
+  while (!sending_entries_.empty()) {
+    ChromotingEvent& entry = sending_entries_.front();
+    if (entry.try_count() < kMaxTries) {
+      pending_entries_.push_back(std::move(entry));
+    }
+    sending_entries_.pop_front();
+  }
+  bool should_call_auth_closure =
+      result.status == net::HTTP_UNAUTHORIZED && !auth_closure_.is_null();
+  request_.reset();  // This may also destroy the result.
+  if (should_call_auth_closure) {
+    VLOG(1) << "Request is unauthorized. Trying to call the auth closure...";
+    auth_closure_.Run();
+  } else {
+    SendPendingEntries();
+  }
+}
+
+}  // namespace remoting
diff --git a/remoting/signaling/telemetry_log_writer.h b/remoting/signaling/telemetry_log_writer.h
new file mode 100644
index 0000000..26c2e9f
--- /dev/null
+++ b/remoting/signaling/telemetry_log_writer.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium 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 REMOTING_SIGNALING_TELEMETRY_LOG_WRITER_H_
+#define REMOTING_SIGNALING_TELEMETRY_LOG_WRITER_H_
+
+#include <deque>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/values.h"
+#include "remoting/base/url_request.h"
+#include "remoting/signaling/chromoting_event.h"
+
+namespace remoting {
+
+// TelemetryLogWriter sends log entries (ChromotingEvent) to the telemetry
+// server.
+// Logs to be sent will be queued and sent when it is available. Logs failed
+// to send will be retried for a few times and dropped if they still can't be
+// sent.
+class TelemetryLogWriter : public base::NonThreadSafe {
+ public:
+  TelemetryLogWriter(const std::string& telemetry_base_url,
+                     std::unique_ptr<UrlRequestFactory> request_factory);
+
+  // "Authorization:Bearer {TOKEN}" will be added if auth_token is not empty.
+  // After this function is called, the log writer will try to send out pending
+  // logs if the list is not empty.
+  void SetAuthToken(const std::string& auth_token);
+
+  // The closure will be called when the request fails with unauthorized error
+  // code. The closure should call SetAuthToken to set the token.
+  // If the closure is not set, the log writer will try to resend the logs
+  // immediately.
+  void SetAuthClosure(base::Closure closure);
+
+  // Push the log entry to the pending list and send out all the pending logs.
+  void Log(const ChromotingEvent& entry);
+
+  virtual ~TelemetryLogWriter();
+
+ private:
+  void SendPendingEntries();
+  void PostJsonToServer(const std::string& json);
+  void OnSendLogResult(const remoting::UrlRequest::Result& result);
+
+  std::string telemetry_base_url_;
+  std::unique_ptr<UrlRequestFactory> request_factory_;
+  std::string auth_token_;
+  base::Closure auth_closure_;
+  std::unique_ptr<UrlRequest> request_;
+
+  // Entries to be sent.
+  std::deque<ChromotingEvent> pending_entries_;
+
+  // Entries being sent.
+  // These will be pushed back to pending_entries if error occurs.
+  std::deque<ChromotingEvent> sending_entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(TelemetryLogWriter);
+};
+
+}  // namespace remoting
+#endif  // REMOTING_SIGNALING_TELEMETRY_LOG_WRITER_H_
diff --git a/remoting/signaling/telemetry_log_writer_unittest.cc b/remoting/signaling/telemetry_log_writer_unittest.cc
new file mode 100644
index 0000000..d9bcbdd
--- /dev/null
+++ b/remoting/signaling/telemetry_log_writer_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2016 The Chromium 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 "remoting/signaling/telemetry_log_writer.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "net/http/http_status_code.h"
+#include "remoting/base/url_request.h"
+#include "remoting/signaling/chromoting_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+class FakeUrlRequest : public UrlRequest {
+ public:
+  FakeUrlRequest(const std::string& expected_post,
+                 const UrlRequest::Result& returned_result)
+      : expected_post_(expected_post), returned_result_(returned_result) {}
+
+  void Respond() { on_result_callback_.Run(returned_result_); }
+
+  // UrlRequest overrides.
+  void SetPostData(const std::string& content_type,
+                   const std::string& post_data) override {
+    EXPECT_EQ(content_type, "application/json");
+    EXPECT_EQ(post_data, expected_post_);
+  }
+
+  void AddHeader(const std::string& value) override {}
+
+  void Start(const OnResultCallback& on_result_callback) override {
+    on_result_callback_ = on_result_callback;
+  }
+
+ private:
+  std::string expected_post_;
+  UrlRequest::Result returned_result_;
+  OnResultCallback on_result_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeUrlRequest);
+};
+
+class FakeUrlRequestFactory : public UrlRequestFactory {
+ public:
+  ~FakeUrlRequestFactory() override { EXPECT_TRUE(expected_requests_.empty()); }
+
+  // Returns a respond closure. Run this closure to respond to the URL request.
+  base::Closure AddExpectedRequest(const std::string& exp_post,
+                                   const UrlRequest::Result& ret_result) {
+    FakeUrlRequest* fakeRequest = new FakeUrlRequest(exp_post, ret_result);
+    base::Closure closure =
+        base::Bind(&FakeUrlRequest::Respond, base::Unretained(fakeRequest));
+    expected_requests_.push_back(std::unique_ptr<UrlRequest>(fakeRequest));
+    return closure;
+  }
+
+  // request_factory_ override.
+  std::unique_ptr<UrlRequest> CreateUrlRequest(
+      UrlRequest::Type type,
+      const std::string& url) override {
+    EXPECT_FALSE(expected_requests_.empty());
+    if (expected_requests_.empty()) {
+      return std::unique_ptr<UrlRequest>(nullptr);
+    }
+    EXPECT_EQ(type, UrlRequest::Type::POST);
+    std::unique_ptr<UrlRequest> request(std::move(expected_requests_.front()));
+    expected_requests_.pop_front();
+    return request;
+  }
+
+ private:
+  std::deque<std::unique_ptr<UrlRequest>> expected_requests_;
+};
+
+class TelemetryLogWriterTest : public testing::Test {
+ public:
+  TelemetryLogWriterTest()
+      : request_factory_(new FakeUrlRequestFactory()),
+        log_writer_("", base::WrapUnique(request_factory_)) {
+    success_result_.success = true;
+    success_result_.status = 200;
+    success_result_.response_body = "{}";
+
+    unauth_result_.success = false;
+    unauth_result_.status = net::HTTP_UNAUTHORIZED;
+    unauth_result_.response_body = "{}";
+  }
+
+ protected:
+  void LogFakeEvent() {
+    ChromotingEvent entry;
+    entry.SetInteger("id", id_);
+    id_++;
+    log_writer_.Log(entry);
+  }
+
+  void SetAuthClosure() {
+    log_writer_.SetAuthClosure(
+        base::Bind(&TelemetryLogWriterTest::SetAuth, base::Unretained(this)));
+  }
+
+  UrlRequest::Result success_result_;
+  UrlRequest::Result unauth_result_;
+
+  FakeUrlRequestFactory* request_factory_;  // For peeking. No ownership.
+  TelemetryLogWriter log_writer_;
+
+  int set_auth_count_ = 0;
+
+ private:
+  void SetAuth() {
+    set_auth_count_++;
+    log_writer_.SetAuthToken("some token");
+  }
+
+  int id_ = 0;
+};
+
+// Test workflow: add request -> log event -> respond request.
+// Test fails if req is incorrect or creates more/less reqs than expected
+TEST_F(TelemetryLogWriterTest, PostOneLogImmediately) {
+  auto respond = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", success_result_);
+  LogFakeEvent();
+  respond.Run();
+}
+
+TEST_F(TelemetryLogWriterTest, PostOneLogAndHaveTwoPendingLogs) {
+  auto respond1 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", success_result_);
+  LogFakeEvent();
+
+  auto respond2 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":1},{\"id\":2}]}", success_result_);
+  LogFakeEvent();
+  LogFakeEvent();
+  respond1.Run();
+  respond2.Run();
+}
+
+TEST_F(TelemetryLogWriterTest, PostLogFailedAndRetry) {
+  // kMaxTries = 5
+  auto respond1 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", UrlRequest::Result::Failed());
+  auto respond2 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", UrlRequest::Result::Failed());
+  auto respond3 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", UrlRequest::Result::Failed());
+  auto respond4 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", UrlRequest::Result::Failed());
+  auto respond5 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", UrlRequest::Result::Failed());
+
+  LogFakeEvent();
+
+  respond1.Run();
+  respond2.Run();
+  respond3.Run();
+  respond4.Run();
+  respond5.Run();
+}
+
+TEST_F(TelemetryLogWriterTest, PostOneLogFailedResendWithTwoPendingLogs) {
+  auto respond1 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", UrlRequest::Result::Failed());
+  LogFakeEvent();
+
+  auto respond2 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":1},{\"id\":2},{\"id\":0}]}", success_result_);
+  LogFakeEvent();
+  LogFakeEvent();
+
+  respond1.Run();
+  respond2.Run();
+}
+
+TEST_F(TelemetryLogWriterTest, PostOneUnauthorizedCallClosureAndRetry) {
+  SetAuthClosure();
+
+  auto respond1 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", unauth_result_);
+  LogFakeEvent();
+
+  auto respond2 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", success_result_);
+  respond1.Run();
+  respond2.Run();
+
+  EXPECT_EQ(1, set_auth_count_);
+}
+
+TEST_F(TelemetryLogWriterTest, PostOneUnauthorizedAndJustRetry) {
+  auto respond1 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", unauth_result_);
+  LogFakeEvent();
+
+  auto respond2 = request_factory_->AddExpectedRequest(
+      "{\"event\":[{\"id\":0}]}", success_result_);
+  respond1.Run();
+  respond2.Run();
+
+  EXPECT_EQ(0, set_auth_count_);
+}
+
+}  // namespace remoting
diff --git a/services/shell/public/cpp/BUILD.gn b/services/shell/public/cpp/BUILD.gn
index 5bb2bc19..2b51994 100644
--- a/services/shell/public/cpp/BUILD.gn
+++ b/services/shell/public/cpp/BUILD.gn
@@ -69,31 +69,6 @@
   ]
 }
 
-source_set("test_support") {
-  testonly = true
-  sources = [
-    "application_test_base.h",
-    "lib/application_test_base.cc",
-    "lib/application_test_main.cc",
-  ]
-
-  public_deps = [
-    ":cpp",
-    "//testing/gtest",
-  ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/logging",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//services/shell/public/interfaces:interfaces_cpp_sources",
-  ]
-
-  data_deps = []
-}
-
 source_set("shell_test_support") {
   testonly = true
   sources = [
diff --git a/services/shell/public/cpp/application_test_base.h b/services/shell/public/cpp/application_test_base.h
deleted file mode 100644
index dc25e9f..0000000
--- a/services/shell/public/cpp/application_test_base.h
+++ /dev/null
@@ -1,101 +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 SERVICES_SHELL_PUBLIC_CPP_APPLICATION_TEST_BASE_H_
-#define SERVICES_SHELL_PUBLIC_CPP_APPLICATION_TEST_BASE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "services/shell/public/cpp/connector.h"
-#include "services/shell/public/cpp/shell_client.h"
-#include "services/shell/public/cpp/shell_connection.h"
-#include "services/shell/public/interfaces/shell_client.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace shell {
-
-class ShellConnection;
-
-namespace test {
-
-// Run all application tests. This must be called after the environment is
-// initialized, to support construction of a default run loop.
-MojoResult RunAllTests(MojoHandle shell_client_request_handle);
-
-// Used to configure the ShellConnection. This is used internally by
-// ApplicationTestBase, but useful if you do not want to subclass
-// ApplicationTestBase.
-class TestHelper {
- public:
-  explicit TestHelper(ShellClient* client);
-  ~TestHelper();
-
-  Connector* connector() { return shell_connection_->connector(); }
-  const std::string& test_name() { return name_; }
-  const std::string& test_userid() { return userid_; }
-  uint32_t test_instance_id() { return instance_id_; }
-
- private:
-  // The application delegate used if GetShellClient is not overridden.
-  ShellClient default_shell_client_;
-
-  // The application implementation instance, reconstructed for each test.
-  std::unique_ptr<ShellConnection> shell_connection_;
-
-  std::string name_;
-  std::string userid_;
-  uint32_t instance_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestHelper);
-};
-
-// A GTEST base class for application testing executed in mojo_shell.
-class ApplicationTestBase : public testing::Test {
- public:
-  ApplicationTestBase();
-  ~ApplicationTestBase() override;
-
- protected:
-  Connector* connector() {
-    return test_helper_ ? test_helper_->connector() : nullptr;
-  }
-  const std::string& test_name() const {
-    return test_helper_ ? test_helper_->test_name() : empty_;
-  }
-  const std::string& test_userid() const {
-    return test_helper_ ? test_helper_->test_userid() : inherit_user_id_;
-  }
-  uint32_t test_instance_id() const {
-    return test_helper_ ? test_helper_->test_instance_id()
-                        : mojom::kInvalidInstanceID;
-  }
-
-  // Get the ShellClient for the application to be tested.
-  virtual ShellClient* GetShellClient();
-
-  // testing::Test:
-  void SetUp() override;
-  void TearDown() override;
-
-  // True by default, which indicates a MessageLoop will automatically be
-  // created for the application.  Tests may override this function to prevent
-  // a default loop from being created.
-  virtual bool ShouldCreateDefaultRunLoop();
-
- private:
-  std::unique_ptr<TestHelper> test_helper_;
-  std::string empty_;
-  std::string inherit_user_id_ = mojom::kInheritUserID;
-
-  DISALLOW_COPY_AND_ASSIGN(ApplicationTestBase);
-};
-
-}  // namespace test
-
-}  // namespace shell
-
-#endif  // SERVICES_SHELL_PUBLIC_CPP_APPLICATION_TEST_BASE_H_
diff --git a/services/shell/public/cpp/lib/application_test_base.cc b/services/shell/public/cpp/lib/application_test_base.cc
deleted file mode 100644
index 79bc74e..0000000
--- a/services/shell/public/cpp/lib/application_test_base.cc
+++ /dev/null
@@ -1,182 +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 <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "services/shell/public/cpp/application_test_base.h"
-#include "services/shell/public/cpp/shell_connection.h"
-#include "services/shell/public/interfaces/shell_client.mojom.h"
-
-namespace shell {
-namespace test {
-
-namespace {
-
-// Share the application name with multiple application tests.
-mojom::IdentityPtr g_identity;
-uint32_t g_id = mojom::kInvalidInstanceID;
-
-// ShellClient request handle passed from the shell in MojoMain, stored in
-// between SetUp()/TearDown() so we can (re-)intialize new ShellConnections.
-mojom::ShellClientRequest g_shell_client_request;
-
-// Connector pointer passed in the initial mojo.ShellClient.Initialize() call,
-// stored in between initial setup and the first test and between SetUp/TearDown
-// calls so we can (re-)initialize new ShellConnections.
-mojom::ConnectorPtr g_connector;
-
-class ShellGrabber : public mojom::ShellClient {
- public:
-  explicit ShellGrabber(mojom::ShellClientRequest request)
-      : binding_(this, std::move(request)) {
-    binding_.set_connection_error_handler([] { _exit(1); });
-  }
-
-  void WaitForInitialize() {
-    // Initialize is always the first call made on ShellClient.
-    CHECK(binding_.WaitForIncomingMethodCall());
-  }
-
- private:
-  // mojom::ShellClient implementation.
-  void Initialize(mojom::IdentityPtr identity,
-                  uint32_t id,
-                  const InitializeCallback& callback) override {
-    callback.Run(GetProxy(&g_connector));
-
-    g_identity = std::move(identity);
-    g_id = id;
-    g_shell_client_request = binding_.Unbind();
-  }
-
-  void AcceptConnection(mojom::IdentityPtr source,
-                        uint32_t source_id,
-                        mojom::InterfaceProviderRequest local_interfaces,
-                        mojom::InterfaceProviderPtr remote_interfaces,
-                        mojom::CapabilityRequestPtr capability_spec,
-                        const mojo::String& name) override {
-    CHECK(false);
-  }
-
-  mojo::Binding<ShellClient> binding_;
-};
-
-void IgnoreConnectorRequest(mojom::ConnectorRequest) {}
-
-}  // namespace
-
-MojoResult RunAllTests(MojoHandle shell_client_request_handle) {
-  {
-    // This loop is used for init, and then destroyed before running tests.
-    base::MessageLoop message_loop;
-
-    // Grab the shell handle.
-    ShellGrabber grabber(
-        mojo::MakeRequest<mojom::ShellClient>(mojo::MakeScopedHandle(
-            mojo::MessagePipeHandle(shell_client_request_handle))));
-    grabber.WaitForInitialize();
-    CHECK(g_connector);
-    CHECK(g_shell_client_request.is_pending());
-
-    int argc = 0;
-    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-    const char** argv = new const char* [cmd_line->argv().size() + 1];
-#if defined(OS_WIN)
-    std::vector<std::string> local_strings;
-#endif
-    for (auto& arg : cmd_line->argv()) {
-#if defined(OS_WIN)
-      local_strings.push_back(base::WideToUTF8(arg));
-      argv[argc++] = local_strings.back().c_str();
-#else
-      argv[argc++] = arg.c_str();
-#endif
-    }
-    argv[argc] = nullptr;
-
-    testing::InitGoogleTest(&argc, const_cast<char**>(&(argv[0])));
-  }
-
-  int result = RUN_ALL_TESTS();
-
-  // Shut down our message pipes before exiting.
-  (void)g_shell_client_request.PassMessagePipe();
-  g_connector.reset();
-
-  return (result == 0) ? MOJO_RESULT_OK : MOJO_RESULT_UNKNOWN;
-}
-
-TestHelper::TestHelper(ShellClient* client)
-    : shell_connection_(new ShellConnection(
-          client == nullptr ? &default_shell_client_ : client,
-          std::move(g_shell_client_request))),
-      name_(g_identity->name),
-      userid_(g_identity->user_id),
-      instance_id_(g_id) {
-  shell_connection_->SetAppTestConnectorForTesting(std::move(g_connector));
-
-  // Fake ShellClient initialization.
-  mojom::ShellClient* shell_client = shell_connection_.get();
-  shell_client->Initialize(std::move(g_identity), g_id,
-                           base::Bind(&IgnoreConnectorRequest));
-}
-
-TestHelper::~TestHelper() {
-  // We may have supplied a member as the client. Delete |shell_connection_|
-  // while still valid.
-  shell_connection_.reset();
-}
-
-ApplicationTestBase::ApplicationTestBase() : test_helper_(nullptr) {}
-
-ApplicationTestBase::~ApplicationTestBase() {
-}
-
-ShellClient* ApplicationTestBase::GetShellClient() {
-  return nullptr;
-}
-
-void ApplicationTestBase::SetUp() {
-  // A run loop is recommended for ShellConnection initialization and
-  // communication.
-  if (ShouldCreateDefaultRunLoop()) {
-    CHECK(!base::MessageLoop::current());
-    // Not leaked: accessible from |base::MessageLoop::current()|.
-    base::MessageLoop* message_loop = new base::MessageLoop();
-    CHECK_EQ(message_loop, base::MessageLoop::current());
-  }
-
-  CHECK(g_shell_client_request.is_pending());
-  CHECK(g_connector);
-
-  // New applications are constructed for each test to avoid persisting state.
-  test_helper_.reset(new TestHelper(GetShellClient()));
-}
-
-void ApplicationTestBase::TearDown() {
-  CHECK(!g_shell_client_request.is_pending());
-  CHECK(!g_connector);
-
-  test_helper_.reset();
-
-  if (ShouldCreateDefaultRunLoop()) {
-    CHECK(base::MessageLoop::current());
-    delete base::MessageLoop::current();
-    CHECK(!base::MessageLoop::current());
-  }
-}
-
-bool ApplicationTestBase::ShouldCreateDefaultRunLoop() {
-  return true;
-}
-
-}  // namespace test
-}  // namespace shell
diff --git a/services/shell/public/cpp/lib/application_test_main.cc b/services/shell/public/cpp/lib/application_test_main.cc
deleted file mode 100644
index 0e0d44d..0000000
--- a/services/shell/public/cpp/lib/application_test_main.cc
+++ /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.
-
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/test/test_timeouts.h"
-#include "mojo/logging/init_logging.h"
-#include "mojo/public/c/system/main.h"
-#include "services/shell/public/cpp/application_runner.h"
-#include "services/shell/public/cpp/application_test_base.h"
-
-MojoResult MojoMain(MojoHandle handle) {
-  // An AtExitManager instance is needed to construct message loops.
-  base::AtExitManager at_exit;
-
-  // Initialize the current process Commandline and test timeouts.
-  shell::ApplicationRunner::InitBaseCommandLine();
-  mojo::InitLogging();
-  TestTimeouts::Initialize();
-
-  return shell::test::RunAllTests(handle);
-}
diff --git a/services/shell/runner/child/BUILD.gn b/services/shell/runner/child/BUILD.gn
index bb4c7d19..7f8b2bd5 100644
--- a/services/shell/runner/child/BUILD.gn
+++ b/services/shell/runner/child/BUILD.gn
@@ -9,7 +9,7 @@
 group("child") {
   testonly = true
   deps = [
-    ":apptests",
+    ":test_native_main",
   ]
 }
 
@@ -31,61 +31,3 @@
     "//services/shell/runner/common",
   ]
 }
-
-mojom("apptest_interfaces") {
-  sources = [
-    "test_native_service.mojom",
-  ]
-
-  deps = [
-    "//services/shell/public/interfaces",
-  ]
-
-  import_dirs = [ "//mojo/services" ]
-}
-
-mojo_native_application("apptests") {
-  output_name = "mojo_runner_child_apptest"
-  testonly = true
-  avoid_runner_cycle = true
-
-  sources = [
-    "native_apptest.cc",
-  ]
-
-  deps = [
-    ":apptest_interfaces",
-    "//base",
-    "//base/test:test_config",
-    "//services/shell/public/cpp:sources",
-    "//services/shell/public/cpp:test_support",
-  ]
-
-  data_deps = [
-    ":manifest",
-    ":native_target",
-  ]
-}
-
-mojo_application_manifest("manifest") {
-  application_name = "mojo_runner_child_apptest"
-  source = "manifest.json"
-}
-
-executable("native_target") {
-  output_name = "mojo_runner_child_apptest_native_target"
-  testonly = true
-
-  sources = [
-    "native_apptest_target.cc",
-  ]
-
-  deps = [
-    ":apptest_interfaces",
-    ":test_native_main",
-    "//base",
-    "//build/config/sanitizers:deps",
-    "//build/win:default_exe_manifest",
-    "//services/shell/public/cpp",
-  ]
-}
diff --git a/services/shell/runner/child/native_apptest.cc b/services/shell/runner/child/native_apptest.cc
deleted file mode 100644
index 06bddc8c..0000000
--- a/services/shell/runner/child/native_apptest.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "services/shell/public/cpp/application_test_base.h"
-#include "services/shell/runner/child/test_native_service.mojom.h"
-
-namespace shell {
-
-namespace {
-
-void InvertCallback(bool* result, bool from_native) {
-  *result = from_native;
-}
-
-}  // namespace
-
-using NativeAppTest = test::ApplicationTestBase;
-
-TEST_F(NativeAppTest, Connect) {
-  test::TestNativeServicePtr native_service;
-  connector()->ConnectToInterface("exe:mojo_runner_child_apptest_native_target",
-                                  &native_service);
-
-  bool result = false;
-  native_service->Invert(
-      true, base::Bind(&InvertCallback, base::Unretained(&result)));
-  native_service.WaitForIncomingResponse();
-  EXPECT_FALSE(result);
-
-  native_service->Invert(
-      false, base::Bind(&InvertCallback, base::Unretained(&result)));
-  native_service.WaitForIncomingResponse();
-  EXPECT_TRUE(result);
-}
-
-}  // namespace shell
diff --git a/services/shell/runner/child/native_apptest_target.cc b/services/shell/runner/child/native_apptest_target.cc
deleted file mode 100644
index ab1ec4c..0000000
--- a/services/shell/runner/child/native_apptest_target.cc
+++ /dev/null
@@ -1,62 +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 <utility>
-
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/shell/public/cpp/connection.h"
-#include "services/shell/public/cpp/interface_factory.h"
-#include "services/shell/public/cpp/shell_client.h"
-#include "services/shell/runner/child/test_native_main.h"
-#include "services/shell/runner/child/test_native_service.mojom.h"
-#include "services/shell/runner/init.h"
-
-namespace {
-
-class TargetApplicationDelegate
-    : public shell::ShellClient,
-      public shell::test::TestNativeService,
-      public shell::InterfaceFactory<shell::test::TestNativeService> {
- public:
-  TargetApplicationDelegate() {}
-  ~TargetApplicationDelegate() override {}
-
- private:
-  // shell::ShellClient:
-  bool AcceptConnection(shell::Connection* connection) override {
-    connection->AddInterface<shell::test::TestNativeService>(this);
-    return true;
-  }
-
-  // shell::test::TestNativeService:
-  void Invert(bool from_driver, const InvertCallback& callback) override {
-    callback.Run(!from_driver);
-  }
-
-  // shell::InterfaceFactory<shell::test::TestNativeService>:
-  void Create(
-      shell::Connection* connection,
-      mojo::InterfaceRequest<shell::test::TestNativeService> request) override {
-    bindings_.AddBinding(this, std::move(request));
-  }
-
-  mojo::BindingSet<shell::test::TestNativeService> bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate);
-};
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::AtExitManager at_exit;
-  base::CommandLine::Init(argc, argv);
-
-  shell::InitializeLogging();
-
-  TargetApplicationDelegate delegate;
-  return shell::TestNativeMain(&delegate);
-}
diff --git a/services/shell/runner/child/test_native_service.mojom b/services/shell/runner/child/test_native_service.mojom
deleted file mode 100644
index 7bd50c1..0000000
--- a/services/shell/runner/child/test_native_service.mojom
+++ /dev/null
@@ -1,9 +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.
-
-module shell.test;
-
-interface TestNativeService {
-  Invert(bool from_driver) => (bool from_target);
-};
diff --git a/services/shell/runner/host/child_process_host.cc b/services/shell/runner/host/child_process_host.cc
index e14d793..f17ab88 100644
--- a/services/shell/runner/host/child_process_host.cc
+++ b/services/shell/runner/host/child_process_host.cc
@@ -154,9 +154,9 @@
   // that case, they're automatically inherited by child processes. See
   // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx
   // Trying to add them to the list of handles to inherit causes CreateProcess
-  // to fail. When this process is launched from Python
-  // (i.e. by apptest_runner.py) then a real handle is used. In that case, we do
-  // want to add it to the list of handles that is inherited.
+  // to fail. When this process is launched from Python then a real handle is
+  // used. In that case, we do want to add it to the list of handles that is
+  // inherited.
   if (options.stdout_handle &&
       GetFileType(options.stdout_handle) != FILE_TYPE_CHAR) {
     handle_passing_info_.push_back(options.stdout_handle);
diff --git a/storage/browser/blob/OWNERS b/storage/browser/blob/OWNERS
index c7e8dcb..04a70ee3 100644
--- a/storage/browser/blob/OWNERS
+++ b/storage/browser/blob/OWNERS
@@ -1 +1,2 @@
+dmurph@chromium.org
 jianli@chromium.org
diff --git a/storage/common/blob_storage/OWNERS b/storage/common/blob_storage/OWNERS
new file mode 100644
index 0000000..471c08e
--- /dev/null
+++ b/storage/common/blob_storage/OWNERS
@@ -0,0 +1 @@
+dmurph@chromium.org
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 25c73992..8b20266 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -8431,16 +8431,6 @@
         "test": "blink_platform_unittests"
       },
       {
-        "args": [
-          "--gtest_filter=-SaveType/SavePageMultiFrameBrowserTest.ObjectElements/0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -8474,12 +8464,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "components_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "compositor_unittests"
       },
       {
@@ -8495,12 +8479,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "crypto_unittests"
       },
       {
@@ -8591,12 +8569,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "ipc_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "jingle_unittests"
       },
       {
@@ -8651,12 +8623,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "net_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "ppapi_unittests"
       },
       {
@@ -8729,12 +8695,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "webkit_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "wm_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index b7d65bd6..e83c84c 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1,4 +1,25 @@
 {
+  "Android Arm64 Builder (dbg)": {
+    "additional_compile_targets": [
+      "android_builder_tests"
+    ]
+  },
+  "Android Builder": {
+    "additional_compile_targets": [
+      "cronet_test_instrumentation_apk",
+      "system_webview_apk"
+    ]
+  },
+  "Android Builder (dbg)": {
+    "additional_compile_targets": [
+      "system_webview_apk"
+    ]
+  },
+  "Android Clang Builder (dbg)": {
+    "additional_compile_targets": [
+      "android_builder_tests"
+    ]
+  },
   "Android GN": {
     "gtest_tests": [
       {
@@ -823,6 +844,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "media_mojo_shell_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "media_unittests"
       },
       {
@@ -835,12 +862,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_apptests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "mojo_common_unittests"
       },
       {
@@ -1305,12 +1326,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_apptests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "mojo_common_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.mojo.json b/testing/buildbot/chromium.mojo.json
index b3a54d1e..907e9cb 100644
--- a/testing/buildbot/chromium.mojo.json
+++ b/testing/buildbot/chromium.mojo.json
@@ -2,12 +2,6 @@
   "Chromium Mojo Android": {
     "additional_compile_targets": [
       "mash:all"
-    ],
-    "scripts": [
-      {
-        "name": "mojo_apptests",
-        "script": "mojo_apptests.py"
-      }
     ]
   },
   "Chromium Mojo Linux": {
@@ -25,17 +19,14 @@
         "test": "browser_tests"
       },
       {
+        "test": "media_mojo_shell_unittests"
+      },
+      {
         "test": "mus_ws_unittests"
       },
       {
         "test": "views_mus_unittests"
       }
-    ],
-    "scripts": [
-      {
-        "name": "mojo_apptests",
-        "script": "mojo_apptests.py"
-      }
     ]
   },
   "Chromium Mojo Windows": {
@@ -52,17 +43,14 @@
         "test": "browser_tests"
       },
       {
+        "test": "media_mojo_shell_unittests"
+      },
+      {
         "test": "mus_ws_unittests"
       },
       {
         "test": "views_mus_unittests"
       }
-    ],
-    "scripts": [
-      {
-        "name": "mojo_apptests",
-        "script": "mojo_apptests.py"
-      }
     ]
   }
 }
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index c6d0c9e..77ef6f88 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -364,6 +364,10 @@
     "label": "//media:media_unittests",
     "type": "windowed_test_launcher",
   },
+  "media_mojo_shell_unittests": {
+    "label": "//media/mojo/services:media_mojo_shell_unittests",
+    "type": "console_test_launcher",
+  },
   "media_blink_unittests": {
     "label": "//media/blink:media_blink_unittests",
     "type": "windowed_test_launcher",
@@ -372,14 +376,6 @@
     "label": "//media/midi:midi_unittests",
     "type": "windowed_test_launcher",
   },
-  "mojo_apptests": {
-    "label": ":mojo_apptests",
-    "type": "script",
-    "script": "//mojo/tools/apptest_runner.py",
-    "args": [
-      '.',
-    ],
-  },
   "mojo_common_unittests": {
     "label": "//mojo/common:mojo_common_unittests",
     "type": "console_test_launcher",
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index a302af47..4da0901 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -275,3 +275,13 @@
   libfuzzer_options = [ "max_len=32" ]
   additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ]
 }
+
+fuzzer_test("convert_woff2ttf_fuzzer") {
+  sources = [
+    "convert_woff2ttf_fuzzer.cc",
+  ]
+  deps = [
+    "//third_party/woff2:woff2_dec",
+  ]
+  seed_corpus = "//testing/libfuzzer/fuzzers/woff2_corpus"
+}
diff --git a/testing/libfuzzer/fuzzers/convert_woff2ttf_fuzzer.cc b/testing/libfuzzer/fuzzers/convert_woff2ttf_fuzzer.cc
new file mode 100644
index 0000000..5dd3a67b
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/convert_woff2ttf_fuzzer.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 <stddef.h>
+#include <stdint.h>
+
+#include "third_party/woff2/src/woff2_dec.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::string buf;
+  woff2::WOFF2StringOut out(&buf);
+  out.SetMaxSize(30 * 1024 * 1024);
+  woff2::ConvertWOFF2ToTTF(data, size, &out);
+  return 0;
+}
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/Ahem.woff b/testing/libfuzzer/fuzzers/woff2_corpus/Ahem.woff
new file mode 100644
index 0000000..0bc8093
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/Ahem.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/AhemSpaceLigature.woff b/testing/libfuzzer/fuzzers/woff2_corpus/AhemSpaceLigature.woff
new file mode 100644
index 0000000..2f576b9a
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/AhemSpaceLigature.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/DejaVuSerif-webfont.woff b/testing/libfuzzer/fuzzers/woff2_corpus/DejaVuSerif-webfont.woff
new file mode 100644
index 0000000..4583cc6
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/DejaVuSerif-webfont.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/LinLibertine_R.woff b/testing/libfuzzer/fuzzers/woff2_corpus/LinLibertine_R.woff
new file mode 100644
index 0000000..0357ad4
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/LinLibertine_R.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/MEgalopolisExtra.woff b/testing/libfuzzer/fuzzers/woff2_corpus/MEgalopolisExtra.woff
new file mode 100644
index 0000000..ee37efff
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/MEgalopolisExtra.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/OpenSans-Regular.woff b/testing/libfuzzer/fuzzers/woff2_corpus/OpenSans-Regular.woff
new file mode 100644
index 0000000..55b25f8
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/OpenSans-Regular.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/mplus-1p-regular.woff b/testing/libfuzzer/fuzzers/woff2_corpus/mplus-1p-regular.woff
new file mode 100644
index 0000000..42cfff6
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/mplus-1p-regular.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/open_sans.woff b/testing/libfuzzer/fuzzers/woff2_corpus/open_sans.woff
new file mode 100644
index 0000000..55b25f8
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/open_sans.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/sileot-webfont.woff b/testing/libfuzzer/fuzzers/woff2_corpus/sileot-webfont.woff
new file mode 100644
index 0000000..81547578
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/sileot-webfont.woff
Binary files differ
diff --git a/testing/libfuzzer/fuzzers/woff2_corpus/tcu-font.woff b/testing/libfuzzer/fuzzers/woff2_corpus/tcu-font.woff
new file mode 100644
index 0000000..c880aaab
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/woff2_corpus/tcu-font.woff
Binary files differ
diff --git a/testing/scripts/mojo_apptests.py b/testing/scripts/mojo_apptests.py
deleted file mode 100755
index 8718ddd6..0000000
--- a/testing/scripts/mojo_apptests.py
+++ /dev/null
@@ -1,45 +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.
-
-import json
-import os
-import sys
-
-
-import common
-
-
-def main_run(args):
-  runner = os.path.join(common.SRC_DIR, 'mojo', 'tools', 'apptest_runner.py')
-  build_dir = os.path.join(common.SRC_DIR, 'out', args.build_config_fs)
-
-  with common.temporary_file() as tempfile_path:
-    rc = common.run_command([sys.executable, runner, build_dir, '--verbose',
-                             '--write-full-results-to', tempfile_path])
-    with open(tempfile_path) as f:
-      results = json.load(f)
-
-  parsed_results = common.parse_common_test_results(results, test_separator='.')
-  failures = parsed_results['unexpected_failures']
-
-  json.dump({
-      'valid': bool(rc <= common.MAX_FAILURES_EXIT_STATUS and
-                   ((rc == 0) or failures)),
-      'failures': failures.keys(),
-  }, args.output)
-
-  return rc
-
-
-def main_compile_targets(args):
-  json.dump(['mojo_apptests'], args.output)
-
-
-if __name__ == '__main__':
-  funcs = {
-    'run': main_run,
-    'compile_targets': main_compile_targets,
-  }
-  sys.exit(common.run_script(sys.argv[1:], funcs))
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
index 2f30dd6..7f3c78f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -4,15 +4,11 @@
 # https://crbug.com/582494 - [sigsegv / av] blink::Document::styleEngine.
 http/tests/security/mixedContent/insecure-plugin-in-iframe.html [ Crash ]
 
-# https://crbug.com/602818 - Incorrect process transfer for blob URL
-http/tests/fileapi/blob-url-in-subframe.html [ Timeout ]
-
 # https://crbug.com/584984 - Check failed: bindings_ == kInvalidBindings || bindings_ == bindings
 http/tests/inspector-enabled/console-log-before-frame-navigation.html [ Crash ]
 http/tests/inspector/change-iframe-src.html [ Crash ]
 http/tests/navigation/cross-origin-fragment-navigation-is-async.html [ Crash Failure ]
 http/tests/security/frameNavigation/xss-ALLOWED-targeted-subframe-navigation-change.html [ Crash Timeout ]
-http/tests/security/opened-document-security-origin-resets-on-navigation.html [ Crash ]
 http/tests/security/xssAuditor/anchor-url-dom-write-location-inline-event-null-char.html [ Crash Timeout ]
 http/tests/security/xssAuditor/anchor-url-dom-write-location-inline-event.html [ Crash ]
 http/tests/security/xssAuditor/dom-write-location-inline-event.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 96113c4..bd8816bd 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -531,6 +531,7 @@
 crbug.com/412381 imported/web-platform-tests/mediacapture-streams/GUM-empty-option-param.html [ Failure ]
 crbug.com/412381 imported/web-platform-tests/mediacapture-streams/GUM-trivial-constraint.html [ Failure ]
 crbug.com/412381 imported/web-platform-tests/mediacapture-streams/GUM-unknownkey-option-param.html [ Failure ]
+crbug.com/412381 imported/web-platform-tests/mediacapture-streams/MediaStream-MediaElement-preload-none.html [ Pass Timeout Failure ]
 
 crbug.com/325673 imported/web-platform-tests/custom-elements/concepts/custom-elements-type-allowed-chars-first-char.html [ Failure Timeout ]
 crbug.com/325673 imported/web-platform-tests/custom-elements/concepts/custom-elements-type-allowed-chars.html [ Failure Timeout ]
@@ -1236,17 +1237,6 @@
 
 crbug.com/579493 http/tests/security/xss-DENIED-xsl-document-securityOrigin.xml [ Timeout ]
 
-crbug.com/414264 fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar.html [ NeedsManualRebaseline ]
-crbug.com/414264 fast/sub-pixel/inline-block-with-padding.html [ NeedsManualRebaseline ]
-crbug.com/414264 fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl.html [ NeedsManualRebaseline ]
-crbug.com/414264 fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl.html [ NeedsManualRebaseline ]
-crbug.com/414264 fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar.html [ NeedsManualRebaseline ]
-crbug.com/414264 fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew.html [ NeedsManualRebaseline ]
-crbug.com/414264 editing/selection/5057506.html [ NeedsManualRebaseline ]
-crbug.com/414264 [ Win ] fast/text/emphasis.html [ NeedsManualRebaseline ]
-crbug.com/414264 [ Win ] fast/text/international/vertical-text-metrics-test.html [ NeedsManualRebaseline ]
-crbug.com/414264 [ Win ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar.html [ NeedsManualRebaseline ]
-
 crbug.com/572723 [ Linux Mac10.9 ] inspector/sources/debugger/debugger-disable-enable.html [ Pass Failure Timeout ]
 crbug.com/572723 inspector/sources/debugger/debugger-uncaught-promise-on-pause.html [ Timeout Pass ]
 
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index 0cc0dcde..1b9a0f2 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -181,7 +181,6 @@
 imported/csswg-test/.htaccess [ Skip ]
 
 imported/web-platform-tests/2dcontext [ Skip ]
-imported/web-platform-tests/DOMEvents [ Skip ]
 ## Owners: jsbell@chromium.org
 # imported/web-platform-tests/FileAPI [ Pass ]
 ## Owners: jsbell@chromium.org
@@ -288,7 +287,8 @@
 ## Owners: chongz@chromium.org
 # imported/web-platform-tests/touch-events [ Pass ]
 imported/web-platform-tests/typedarrays [ Skip ]
-imported/web-platform-tests/uievents [ Skip ]
+## Owners: dtapuska@chromium.org
+# imported/web-platform-tests/uievents [ Pass ]
 imported/web-platform-tests/url [ Skip ]
 ## Owners: jsbell@chromium.org
 # imported/web-platform-tests/user-timing [ Pass ]
@@ -324,6 +324,7 @@
 imported/web-platform-tests/webstorage/OWNERS [ Skip ]
 imported/web-platform-tests/mediacapture-streams/OWNERS [ Skip ]
 imported/web-platform-tests/touch-events/OWNERS [ Skip ]
+imported/web-platform-tests/uievents/OWNERS [ Skip ]
 imported/web-platform-tests/web-animations/OWNERS [ Skip ]
 
 # Exceptions for individual files that fail due to bugs in the
diff --git a/third_party/WebKit/LayoutTests/accessibility/inline-text-box-next-on-line.html b/third_party/WebKit/LayoutTests/accessibility/inline-text-box-next-on-line.html
index 307d6cc..b16feff 100644
--- a/third_party/WebKit/LayoutTests/accessibility/inline-text-box-next-on-line.html
+++ b/third_party/WebKit/LayoutTests/accessibility/inline-text-box-next-on-line.html
@@ -17,6 +17,10 @@
     </div>
 </div>
 
+<p id="paragraphWithLink">
+    Paragraph with <a href="#">link</a>
+</p>
+
 <script>
 test(function(t)
 {
@@ -89,8 +93,28 @@
     }
 }, 'Test |nextOnLine| and |previousOnLine| for |AXLayoutObject|.');
 
+test(function(t)
+{
+    var axObj = accessibilityController.accessibleElementById('paragraphWithLink');
+    // There should be one static text child and a link in this paragraph.
+    assert_equals(axObj.childrenCount, 2);
+    axObj = axObj.childAtIndex(0);
+    assert_equals(axObj.role, 'AXRole: AXStaticText');
+
+    var lineText = [];
+    lineText.push(axObj.name);
+    axObj = axObj.nextOnLine();
+    assert_equals(axObj.role, 'AXRole: AXInlineTextBox');
+    lineText.push(axObj.name);
+    axObj = axObj.previousOnLine();
+    assert_equals(axObj.role, 'AXRole: AXInlineTextBox');
+    lineText.push(axObj.name);
+    assert_array_equals(lineText, ['Paragraph with ', 'link', 'Paragraph with ']);
+}, 'Test |nextOnLine| and |previousOnLine| for paragraphs with links.');
+
 if (window.testRunner) {
     document.getElementById('paragraph').style.display = 'none';
     document.getElementById('contentEditable').style.display = 'none';
+    document.getElementById('paragraphWithLink').style.display = 'none';
 }
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/window-lookup-precedence-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/window-lookup-precedence-expected.txt
index e984f8c..189e89c 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/window-lookup-precedence-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/window-lookup-precedence-expected.txt
@@ -304,7 +304,7 @@
 PASS win['statusbar'] == '[object BarProp]' is true
 PASS win['toolbar'] == '[object BarProp]' is true
 PASS win['toolbar'] == '[object BarProp]' is true
-PASS win['console'] == '[object Console]' is true
+PASS win['console'] == '[object Object]' is true
 PASS win['history'] == '[object History]' is true
 PASS win['navigator'] == '[object Navigator]' is true
 PASS win['navigator'] == '[object Navigator]' is true
diff --git a/third_party/WebKit/LayoutTests/fast/imagecapture/ImageCapture-creation.html b/third_party/WebKit/LayoutTests/fast/imagecapture/ImageCapture-creation.html
index 8cfadc4e..ab0ae95 100644
--- a/third_party/WebKit/LayoutTests/fast/imagecapture/ImageCapture-creation.html
+++ b/third_party/WebKit/LayoutTests/fast/imagecapture/ImageCapture-creation.html
@@ -7,46 +7,52 @@
 // This test verifies that ImageCapture can be created (or not) with different
 // Media Stream Track types (audio, video).
 
-var testVideo = promise_test(function() {
-  const gotStream = this.step_func(function(stream) {
-    assert_equals(stream.getAudioTracks().length, 0);
-    assert_equals(stream.getVideoTracks().length, 1);
-    assert_equals(stream.getVideoTracks()[0].readyState, 'live');
-    assert_true(stream.getVideoTracks()[0].enabled);
-    assert_false(stream.getVideoTracks()[0].muted);
+function makeAsyncTest(modifyTrack, message) {
+  async_test(function(test) {
 
-    var capturer = new ImageCapture(stream.getVideoTracks()[0]);
-    assert_equals(capturer.videoStreamTrack, stream.getVideoTracks()[0]);
+    const gotStream = test.step_func(function(stream) {
+      assert_equals(stream.getAudioTracks().length, 0);
+      assert_equals(stream.getVideoTracks().length, 1);
 
-    // Assert that grabFrame() is rejected if the associated video track is
-    // disabled, or ended. grabFrame() would also reject if the video Track is
-    // muted but that's a read-only property.
-    stream.getVideoTracks()[0].enabled = false;
-    assert_promise_rejects(capturer.grabFrame(),
-                           'InvalidStateError',
-                           'ImageCapturer cannot grabFrame() of a disabled Track');
+      var videoTrack = stream.getVideoTracks()[0];
+      assert_equals(videoTrack.readyState, 'live');
+      assert_true(videoTrack.enabled);
+      assert_false(videoTrack.muted);
 
-    stream.getVideoTracks()[0].stop();
-    assert_equals(stream.getVideoTracks()[0].readyState, 'ended');
-    assert_promise_rejects(capturer.grabFrame(),
-                           'InvalidStateError',
-                           'ImageCapturer cannot grabFrame() of a non-live Track');
+      var capturer = new ImageCapture(videoTrack);
+      assert_equals(capturer.videoStreamTrack, videoTrack);
 
-    this.done();
-  });
+      modifyTrack(videoTrack);
 
-  const onError = this.step_func(function() {
-    assert_unreached('Error creating MediaStream');
-  });
+      assert_promise_rejects(capturer.grabFrame(),
+                             'InvalidStateError',
+                             'Should throw InvalidStateError.')
+      .then(() => test.done());
+    });
 
-  navigator.webkitGetUserMedia({video:true}, gotStream, onError);
-}, 'verifies that ImageCapture API rejects grabFrame() in certain Track states.');
+    const onError = test.unreached_func('Error creating MediaStream.');
+    navigator.webkitGetUserMedia({ video : true }, gotStream, onError);
 
-var testAudio = promise_test(function() {
-  const onError = this.step_func(function() {
-     assert_unreached('Error creating MediaStream');
-  });
+  }, message);
+}
 
+var disableTrack = function(videoTrack) {
+  // grabFrame() is rejected if the associated video track is disabled.
+  videoTrack.enabled = false;
+};
+
+var stopTrack = function(videoTrack) {
+  // grabFrame() is rejected if the associated video track is ended.
+  videoTrack.stop();
+  assert_equals(videoTrack.readyState, 'ended');
+};
+
+// Create the rejection tests. Note that grabFrame() would also be rejected if
+// the video Track was muted but that's a read-only property of the Track.
+makeAsyncTest(disableTrack, 'grabFrame() of a disabled Track');
+makeAsyncTest(stopTrack, 'grabFrame() of an ended Track');
+
+var testAudio = async_test(function() {
   navigator.webkitGetUserMedia({audio:true},
     this.step_func(function(stream) {
       assert_equals(stream.getAudioTracks().length, 1);
@@ -58,7 +64,8 @@
                     'an ImageCapturer can only be created from a video track');
 
       this.done();
-    }), onError);
+    }),
+    this.unreached_func('Error creating MediaStream.'));
 }, 'verifies that an ImageCapture cannot be created out of an Audio Track');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-isTypeSupported.html b/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-isTypeSupported.html
index 796935d5..7b0c985 100644
--- a/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-isTypeSupported.html
+++ b/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-isTypeSupported.html
@@ -18,12 +18,16 @@
   assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=vp9"));
   assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=vp8.0"));
   assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=vp9.0"));
+  assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=h264"));
+  assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=H264"));
   // "video/webm" supports audio codec specification, see
   // http://www.webmproject.org/docs/container/
   assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=vp8,opus"));
   assert_true(MediaRecorder.isTypeSupported("video/WEBM;codecs=VP8,OPUS"));
   assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=vp9,opus"));
   assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=vp8,vp9,opus"));
+  assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=h264,opus"));
+  assert_true(MediaRecorder.isTypeSupported("video/webm;codecs=h264,vp9,opus"));
 }, 'check MediaRecorder.isTypeSupported() with valid video MIME types');
 
 test(function() {
diff --git a/third_party/WebKit/LayoutTests/fast/sub-pixel/inline-block-with-padding-expected.txt b/third_party/WebKit/LayoutTests/fast/sub-pixel/inline-block-with-padding-expected.txt
index d762f9f..24d0fc6 100644
--- a/third_party/WebKit/LayoutTests/fast/sub-pixel/inline-block-with-padding-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/sub-pixel/inline-block-with-padding-expected.txt
@@ -1,4 +1,4 @@
-PASS links[0].offsetWidth is within 5 of 177
+PASS links[0].offsetWidth is within 5 of 176
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/http/tests/permissionclient/image-permissions-expected.txt b/third_party/WebKit/LayoutTests/http/tests/permissionclient/image-permissions-expected.txt
index 8a10b80c..1278a8c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/permissionclient/image-permissions-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/permissionclient/image-permissions-expected.txt
@@ -1,5 +1,5 @@
-PERMISSION CLIENT: allowImage(http://127.0.0.1:8000/permissionclient/resources/redir.php?url=http://127.0.0.1:8000/permissionclient/resources/boston.gif): true
-PERMISSION CLIENT: allowImage(http://127.0.0.1:8000/permissionclient/resources/boston.gif): true
+MockContentSettingsClient: allowImage(http://127.0.0.1:8000/permissionclient/resources/redir.php?url=http://127.0.0.1:8000/permissionclient/resources/boston.gif): true
+MockContentSettingsClient: allowImage(http://127.0.0.1:8000/permissionclient/resources/boston.gif): true
 This test checks that all URLs in a redirect chain are passed to the PermissionClient.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/http/tests/permissionclient/video-permissions-expected.txt b/third_party/WebKit/LayoutTests/http/tests/permissionclient/video-permissions-expected.txt
index 627e2ee..e0070e1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/permissionclient/video-permissions-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/permissionclient/video-permissions-expected.txt
@@ -1,5 +1,5 @@
-PERMISSION CLIENT: allowMedia(http://127.0.0.1:8000/permissionclient/resources/redir.php?url=http://127.0.0.1:8000/media/resources/test.ogv): true
-PERMISSION CLIENT: allowMedia(http://127.0.0.1:8000/media/resources/test.ogv): true
+MockContentSettingsClient: allowMedia(http://127.0.0.1:8000/permissionclient/resources/redir.php?url=http://127.0.0.1:8000/media/resources/test.ogv): true
+MockContentSettingsClient: allowMedia(http://127.0.0.1:8000/media/resources/test.ogv): true
 This test checks that all URLs in a redirect chain are passed to the PermissionClient.
 
 PASS: video loaded
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/cross-frame-access-put-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/cross-frame-access-put-expected.txt
index 85665b0..25ccdc3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/cross-frame-access-put-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/cross-frame-access-put-expected.txt
@@ -85,7 +85,7 @@
 ALERT: PASS: window.XPathResult should be 'function XPathResult() { [native code] }' and is.
 ALERT: PASS: window.clientInformation should be '[object Navigator]' and is.
 ALERT: PASS: window.closed should be 'false' and is.
-ALERT: PASS: window.console should be '[object Console]' and is.
+ALERT: PASS: window.console should be '[object Object]' and is.
 ALERT: PASS: window.crypto should be '[object Crypto]' and is.
 ALERT: PASS: window.defaultStatus should be '' and is.
 ALERT: PASS: window.defaultstatus should be '' and is.
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/html/syntax/serializing-html-fragments/initial-linefeed-pre-expected.txt b/third_party/WebKit/LayoutTests/imported/web-platform-tests/html/syntax/serializing-html-fragments/initial-linefeed-pre-expected.txt
new file mode 100644
index 0000000..d4431dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/html/syntax/serializing-html-fragments/initial-linefeed-pre-expected.txt
@@ -0,0 +1,18 @@
+x
+
+x
+ 
+x
+
+x
+This is a testharness.js-based test.
+FAIL outer div assert_equals: expected "\n<div id=\"inner\">\n<pre id=\"pre1\">x</pre>\n<pre id=\"pre2\">\n\nx</pre>\n<textarea id=\"textarea1\">x</textarea>\n<textarea id=\"textarea2\">\n\nx</textarea>\n<listing id=\"listing1\">x</listing>\n<listing id=\"listing2\">\n\nx</listing>\n</div>\n" but got "\n<div id=\"inner\">\n<pre id=\"pre1\">x</pre>\n<pre id=\"pre2\">\nx</pre>\n<textarea id=\"textarea1\">x</textarea>\n<textarea id=\"textarea2\">\nx</textarea>\n<listing id=\"listing1\">x</listing>\n<listing id=\"listing2\">\nx</listing>\n</div>\n"
+FAIL inner div assert_equals: expected "\n<pre id=\"pre1\">x</pre>\n<pre id=\"pre2\">\n\nx</pre>\n<textarea id=\"textarea1\">x</textarea>\n<textarea id=\"textarea2\">\n\nx</textarea>\n<listing id=\"listing1\">x</listing>\n<listing id=\"listing2\">\n\nx</listing>\n" but got "\n<pre id=\"pre1\">x</pre>\n<pre id=\"pre2\">\nx</pre>\n<textarea id=\"textarea1\">x</textarea>\n<textarea id=\"textarea2\">\nx</textarea>\n<listing id=\"listing1\">x</listing>\n<listing id=\"listing2\">\nx</listing>\n"
+PASS pre1 
+PASS pre2 
+PASS textarea1 
+PASS textarea2 
+PASS listing1 
+PASS listing2 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html
new file mode 100644
index 0000000..4a7faf9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>innerHTML getter for pre/textarea/listing with initial LF</title>
+<script src=../../../../../resources/testharness.js></script>
+<script src=../../../../../resources/testharnessreport.js></script>
+<div id="outer">
+<div id="inner">
+<pre id="pre1">
+x</pre>
+<pre id="pre2">
+
+x</pre>
+<textarea id="textarea1">
+x</textarea>
+<textarea id="textarea2">
+
+x</textarea>
+<listing id="listing1">
+x</listing>
+<listing id="listing2">
+
+x</listing>
+</div>
+</div>
+
+<script>
+var expected_outer = '\n<div id="inner">\n<pre id="pre1">x</pre>\n<pre id="pre2">\n\nx</pre>\n<textarea id="textarea1">x</textarea>\n<textarea id="textarea2">\n\nx</textarea>\n<listing id="listing1">x</listing>\n<listing id="listing2">\n\nx</listing>\n</div>\n';
+var expected_inner = expected_outer.replace('\n<div id="inner">', '').replace('</div>\n', '');
+var expected_1 = 'x';
+var expected_2 = '\nx';
+
+test(function() {
+  assert_equals(outer.innerHTML, expected_outer);
+}, 'outer div');
+
+test(function() {
+  assert_equals(inner.innerHTML, expected_inner);
+}, 'inner div');
+
+['pre', 'textarea', 'listing'].forEach(function(tag) {
+  test(function() {
+    assert_equals(document.getElementById(tag + '1').innerHTML, expected_1);
+  }, tag + '1');
+
+  test(function() {
+    assert_equals(document.getElementById(tag + '2').innerHTML, expected_2);
+  }, tag + '2');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/mediacapture-streams/MediaStream-MediaElement-preload-none.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/mediacapture-streams/MediaStream-MediaElement-preload-none.html
new file mode 100644
index 0000000..1ce3c39
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/mediacapture-streams/MediaStream-MediaElement-preload-none.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!--  Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang).  -->
+<html>
+    <head>
+        <title>Test that the HTMLMediaElement preload 'none' attribute value is ignored for MediaStream used as srcObject and MediaStream object URLs used as src.</title>>
+        <link rel="author" title="Matthew Wolenetz" href="mailto:wolenetz@chromium.org"/>
+        <script src="../../../resources/testharness.js"></script>
+        <script src="../../../resources/testharnessreport.js"></script>
+        <script src="../../../resources/vendor-prefix.js" data-prefixed-objects='[{"ancestors":["navigator"], "name":"getUserMedia"}, {"ancestors":["window"], "name":"MediaStream"}]'></script>
+    </head>
+    <body>
+        <p class="instructions" style="display:none">When prompted, accept to share your audio and video streams.</p>
+        <h1 class="instructions" style="display:none">Description</h1>
+        <p class="instructions" style="display:none">This test checks that the HTMLMediaElement preload 'none' attribute value is ignored for MediaStream used as srcObject and MediaStream object URLs used as src.</p>
+
+        <audio preload="none"></audio>
+        <video preload="none"></video>
+
+        <script>
+            function testPreloadNone(t, mediaElement, setSourceStreamFunc)
+            {
+                // The optional deferred load steps (for preload none) for MediaStream resources should be skipped.
+                mediaElement.addEventListener("suspend", t.unreached_func("'suspend' should not be fired."));
+
+                mediaElement.addEventListener("loadeddata", t.step_func(function()
+                {
+                    assert_equals(mediaElement.networkState, mediaElement.NETWORK_LOADING);
+                    t.done();
+                }));
+
+                setSourceStreamFunc();
+                assert_equals(mediaElement.networkState, mediaElement.NETWORK_NO_SOURCE); // Resource selection is active.
+            }
+
+            async_test(function(t)
+            {
+                var aud = document.querySelector("audio");
+                navigator.getUserMedia({audio:true}, t.step_func(function(stream)
+                {
+                    testPreloadNone(t, aud, t.step_func(function()
+                    {
+                        aud.src = URL.createObjectURL(stream);
+                        t.add_cleanup(function() { URL.revokeObjectURL(aud.src); });
+                    }));
+                }), t.unreached_func("getUserMedia error callback was invoked."));
+            }, "Test that preload 'none' is ignored for MediaStream object URL used as src");
+
+            async_test(function(t)
+            {
+                var vid = document.querySelector("video");
+                navigator.getUserMedia({video:true}, t.step_func(function(stream)
+                {
+                    testPreloadNone(t, vid, t.step_func(function() { vid.srcObject = stream; }));
+                }), t.unreached_func("getUserMedia error callback was invoked."));
+            }, "Test that preload 'none' is ignored for MediaStream used as srcObject");
+        </script>
+    </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/touch-events/historical.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/touch-events/historical.html
new file mode 100644
index 0000000..5bf79d5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/touch-events/historical.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Historical touch events features</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="touch-support.js"></script>
+<body>
+<script>
+test(function() {
+  assert_false("identifiedTouch" in TouchList.prototype,
+               "Should not be supported on the prototype");
+
+  var touchList = document.createTouchList();
+  assert_false("identifiedTouch" in touchList,
+               "Should not be supported on the instance");
+}, "TouchList::identifiedTouch");
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/ClickFakeEvent.nondocument.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/ClickFakeEvent.nondocument.html
new file mode 100644
index 0000000..042f08d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/ClickFakeEvent.nondocument.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Click event on an element not in the document</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+  var EVENT = "click";
+  var TARGET = document.createElement("somerandomelement");
+  var t = async_test("Click event can be dispatched to an element that is not in the document.")
+  TARGET.addEventListener(EVENT, t.step_func(function(evt) {
+    assert_equals(evt.target, TARGET);
+    t.done();
+  }), true);
+  var e = document.createEvent("Event");
+  e.initEvent(EVENT, true, true);
+  TARGET.dispatchEvent(e);
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/README.md b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/README.md
new file mode 100644
index 0000000..065c0c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/README.md
@@ -0,0 +1,3 @@
+To run the UIEvents tests, go to:
+
+http://w3c-test.org/uievents/order-of-events/mouse-events/
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/README.md b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/README.md
new file mode 100644
index 0000000..097a510
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/README.md
@@ -0,0 +1,4 @@
+Constructors and States tests
+==============================
+
+These test that each of the defined interfaces can be constructed synthetically, and that all attributes can be set to the appropriate state via the constructors.
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/constructors-expected.txt b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/constructors-expected.txt
new file mode 100644
index 0000000..52c6ee0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/constructors-expected.txt
@@ -0,0 +1,46 @@
+This is a testharness.js-based test.
+PASS Event constructor (no argument) 
+PASS Event constructor (undefined argument) 
+PASS Event constructor (null argument) 
+PASS Event constructor (empty argument) 
+PASS Event constructor (argument with default values) 
+PASS Event constructor (argument with non-default values) 
+PASS UIEvent constructor (no argument) 
+PASS UIEvent constructor (undefined argument) 
+PASS UIEvent constructor (null argument) 
+PASS UIEvent constructor (empty argument) 
+PASS UIEvent constructor (argument with default values) 
+PASS UIEvent constructor (argument with non-default values) 
+PASS FocusEvent constructor (no argument) 
+PASS FocusEvent constructor (undefined argument) 
+PASS FocusEvent constructor (null argument) 
+PASS FocusEvent constructor (empty argument) 
+PASS FocusEvent constructor (argument with default values) 
+PASS FocusEvent constructor (argument with non-default values) 
+PASS MouseEvent constructor (no argument) 
+PASS MouseEvent constructor (undefined argument) 
+PASS MouseEvent constructor (null argument) 
+PASS MouseEvent constructor (empty argument) 
+PASS MouseEvent constructor (argument with default values) 
+PASS MouseEvent constructor (argument with non-default values) 
+PASS WheelEvent constructor (no argument) 
+PASS WheelEvent constructor (undefined argument) 
+PASS WheelEvent constructor (null argument) 
+PASS WheelEvent constructor (empty argument) 
+PASS WheelEvent constructor (argument with default values) 
+PASS WheelEvent constructor (argument with non-default values) 
+FAIL KeyboardEvent constructor (no argument) assert_true: Event object "[object KeyboardEvent]" should have a isComposing property expected true got false
+FAIL KeyboardEvent constructor (undefined argument) assert_true: Event object "[object KeyboardEvent]" should have a isComposing property expected true got false
+FAIL KeyboardEvent constructor (null argument) assert_true: Event object "[object KeyboardEvent]" should have a isComposing property expected true got false
+FAIL KeyboardEvent constructor (empty argument) assert_true: Event object "[object KeyboardEvent]" should have a isComposing property expected true got false
+FAIL KeyboardEvent constructor (argument with default values) assert_true: Event object "[object KeyboardEvent]" should have a isComposing property expected true got false
+FAIL KeyboardEvent constructor (argument with non-default values) assert_true: Event object "[object KeyboardEvent]" should have a isComposing property expected true got false
+PASS CompositionEvent constructor (no argument) 
+PASS CompositionEvent constructor (undefined argument) 
+PASS CompositionEvent constructor (null argument) 
+PASS CompositionEvent constructor (empty argument) 
+PASS CompositionEvent constructor (argument with default values) 
+PASS CompositionEvent constructor (argument with non-default values) 
+PASS UIEvent constructor (view argument with wrong type) 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/constructors.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/constructors.html
new file mode 100644
index 0000000..07c46ec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/constructors/constructors.html
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event constructors</title>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function assert_props(iface, event, defaults) {
+  assert_true(event instanceof self[iface]);
+  expected[iface].properties.forEach(function(p) {
+    var property = p[0], value = p[defaults ? 1 : 2];
+    assert_true(property in event,
+                "Event " + format_value(event) + " should have a " +
+                property + " property");
+    assert_equals(event[property], value,
+                  "The value of the " + property + " property should be " +
+                  format_value(value));
+  });
+  if ("parent" in expected[iface]) {
+    assert_props(expected[iface].parent, event, defaults);
+  }
+}
+
+var EventModifierInit = [
+  ["ctrlKey", false, true],
+  ["shiftKey", false, true],
+  ["altKey", false, true],
+  ["metaKey", false, true],
+];
+var expected = {
+  "Event": {
+    "properties": [
+      ["bubbles", false, true],
+      ["cancelable", false, true],
+    ],
+  },
+
+  "UIEvent": {
+    "parent": "Event",
+    "properties": [
+      ["view", null, window],
+      ["detail", 0, 7],
+    ],
+  },
+
+  "FocusEvent": {
+    "parent": "UIEvent",
+    "properties": [
+      ["relatedTarget", null, document],
+    ],
+  },
+
+  "MouseEvent": {
+    "parent": "UIEvent",
+    "properties": EventModifierInit.concat([
+      ["screenX", 0, 40],
+      ["screenY", 0, 40],
+      ["clientX", 0, 40],
+      ["clientY", 0, 40],
+      ["button", 0, 40],
+      ["buttons", 0, 40],
+      ["relatedTarget", null, document],
+    ]),
+  },
+
+  "WheelEvent": {
+    "parent": "MouseEvent",
+    "properties": [
+      ["deltaX", 0.0, 3.1],
+      ["deltaY", 0.0, 3.1],
+      ["deltaZ", 0.0, 3.1],
+      ["deltaMode", 0, 40],
+    ],
+  },
+
+  "KeyboardEvent": {
+    "parent": "UIEvent",
+    "properties": EventModifierInit.concat([
+      ["key", "", "string"],
+      ["code", "", "string"],
+      ["location", 0, 7],
+      ["repeat", false, true],
+      ["isComposing", false, true],
+      ["charCode", 0, 7],
+      ["keyCode", 0, 7],
+      ["which", 0, 7],
+    ]),
+  },
+
+  "CompositionEvent": {
+    "parent": "UIEvent",
+    "properties": [
+      ["data", "", "string"],
+    ],
+  },
+};
+
+Object.keys(expected).forEach(function(iface) {
+  test(function() {
+    var event = new self[iface]("type");
+    assert_props(iface, event, true);
+  }, iface + " constructor (no argument)");
+
+  test(function() {
+    var event = new self[iface]("type", undefined);
+    assert_props(iface, event, true);
+  }, iface + " constructor (undefined argument)");
+
+  test(function() {
+    var event = new self[iface]("type", null);
+    assert_props(iface, event, true);
+  }, iface + " constructor (null argument)");
+
+  test(function() {
+    var event = new self[iface]("type", {});
+    assert_props(iface, event, true);
+  }, iface + " constructor (empty argument)");
+
+  test(function() {
+    var dictionary = {};
+    expected[iface].properties.forEach(function(p) {
+      var property = p[0], value = p[1];
+      dictionary[property] = value;
+    });
+    var event = new self[iface]("type", dictionary);
+    assert_props(iface, event, true);
+  }, iface + " constructor (argument with default values)");
+
+  test(function() {
+    function fill_in(iface, dictionary) {
+      if ("parent" in expected[iface]) {
+        fill_in(expected[iface].parent, dictionary)
+      }
+      expected[iface].properties.forEach(function(p) {
+        var property = p[0], value = p[2];
+        dictionary[property] = value;
+      });
+    }
+
+    var dictionary = {};
+    fill_in(iface, dictionary);
+
+    var event = new self[iface]("type", dictionary);
+    assert_props(iface, event, false);
+  }, iface + " constructor (argument with non-default values)");
+});
+
+test(function () {
+  assert_throws(new TypeError(), function() {
+    new UIEvent("x", { view: 7 })
+  });
+}, "UIEvent constructor (view argument with wrong type)")
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/hierarchy/README.md b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/hierarchy/README.md
new file mode 100644
index 0000000..3bf23b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/hierarchy/README.md
@@ -0,0 +1,34 @@
+Verify the Class Hierarchy
+==========================
+
+Make sure the events inherit from the correct interfaces:
+    e.g., UIEvent > MouseEvent
+
+Requires manual and automated tests
+* manually create event and verify hierarchy
+* WebDriver create the event and verify hierarchy
+
+UIEvent
+ * load, unload, abort, error, select, resize, scroll
+ * Note: some event types may be dropped given that they don't appear to be UIEvents by other specs that define them.
+
+FocusEvent
+ * blur, focus, focusin, focusout
+ * blur and focus are handled in HTML5
+ * but they aren't sure if focusin/out are needed: see bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=25877
+
+MouseEvent
+ * click, dblclick, mousedown, mouseenter, mouseleave, mousemove, mouseout, mouseover, mouseup
+
+WheelEvent
+ * wheel
+
+KeyboardEvent
+ * keydown, keyup
+ * need to show interaction with beforeinput and input, which are in the Editing spec
+
+CompositionEvent
+ * compositionstart
+ * compositionupdate
+ * compositionend
+ * need to show interaction with the keyboard events: keydown, keyup
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/interface/README.md b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/interface/README.md
new file mode 100644
index 0000000..970fdd4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/interface/README.md
@@ -0,0 +1,13 @@
+Interface tests
+==============================
+
+These test that the basic UI Events interfaces exist, specifically:
+
+1. Does the interface exist
+2. Are all the members defined on the interface accounted for:
+ * UIEvent
+ * MouseEvent
+ * FocusEvent
+ * KeyboardEvent
+ * WheelEvent
+ * CompositionEvent
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/README.md b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/README.md
new file mode 100644
index 0000000..38c454dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/README.md
@@ -0,0 +1,3 @@
+# Keyboard event tests
+
+These tests focus on testing the |key|, |code| and other attributes of KeyboardEvents.
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/key-manual.css b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/key-manual.css
new file mode 100644
index 0000000..b2add2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/key-manual.css
@@ -0,0 +1,118 @@
+.keyboard {
+    display: table;
+    border-collapse: separate;
+    border-spacing: 2px;
+    width: 800px;
+    border: 2px solid black;
+    border-radius: 10px;
+    padding: 5px;
+}
+
+.key-row {
+    display: table;
+    margin: 0;
+    padding: 0;
+}
+
+.key {
+    display: table-cell;
+    border: 2px solid black;
+    border-radius: 8px;
+    width: 50px;
+    height: 40px;
+    vertical-align: middle;
+    text-align: center;
+    margin: 0;
+    padding: 0;
+}
+
+.wide1 {
+    width: 70px;
+}
+
+.wide2 {
+    width: 90px;
+}
+
+.wide3 {
+    width: 110px;
+}
+
+.wide4 {
+    width: 130px;
+}
+
+.wide5 {
+    width: 300px;
+}
+
+.nextKey {
+    background-color: yellow;
+}
+
+.goodKey {
+    background-color: #80ff08;
+}
+
+.badKey {
+    background-color: #ff8080;
+}
+
+.activeModifierKey {
+    background-color: #a0a0ff;
+}
+
+.skippedKey {
+    background-color: #e0e0e0;
+}
+
+#options {
+    display: none;
+    margin: 20px;
+}
+
+#optionstoggle, #helptoggle {
+    font-size: 10pt;
+}
+
+.opttable {
+    border: 1px solid black;
+}
+
+.optcell {
+    vertical-align: top;
+    padding: 0 10px;
+}
+
+.opttitle {
+    font-weight: bold;
+}
+
+.error {
+    border: 1px solid red;
+    margin: 5px;
+    padding: 5px;
+}
+
+.error1 {
+    font-size: 12pt;
+    margin: 0 0 0 10px;
+    padding: 0;
+}
+
+.error2 {
+    font-size: 10pt;
+    margin: 0 0 0 20px;
+    padding: 0;
+}
+
+.help {
+    font-size: 11pt;
+    margin: 0 0 5px 20px;
+    padding: 0;
+}
+
+body {
+    margin: 10px;
+    padding: 0 20px;
+}
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/key-manual.js b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/key-manual.js
new file mode 100644
index 0000000..175258b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/keyboard/key-manual.js
@@ -0,0 +1,671 @@
+var _testing = false;
+
+// The index into _keyTable of the key currently being tested.
+var _currKey = 0;
+
+var _keysTotal = 0;
+var _keysGood = 0;
+var _keysBad = 0;
+var _keysSkipped = 0;
+
+var _modifierMode = "None";
+
+var _keydownCapture = [];
+var _keyupCapture = [];
+
+var CAPTURE_KEYCODE = 0;
+var CAPTURE_CODE = 1;
+var CAPTURE_KEY = 2;
+var CAPTURE_SHIFTKEY = 3;
+var CAPTURE_CONTROLKEY = 4;
+var CAPTURE_ALTKEY = 5;
+var CAPTURE_METAKEY = 6;
+
+// An array of KeyInfo for each key to be tested.
+var _keyTable = [];
+
+// KeyInfo fields.
+var KEYINFO_CODE = 0;       // |code| for this key
+var KEYINFO_ROW = 1;        // Keyboard row
+var KEYINFO_TYPE = 2;       // Key type (see below)
+var KEYINFO_WIDTH = 3;      // Width of key: 0=normal
+var KEYINFO_KEYCAP = 4;     // Keycap string to display
+var KEYINFO_KEY = 5;        // Unmodified key value
+var KEYINFO_KEY_SHIFT = 6;  // Shifted key value
+
+var KEYTYPE_NORMAL = 0;
+var KEYTYPE_DISABLED = 1;   // Key cannot be tested: e.g., CapsLock
+var KEYTYPE_END = 2;        // Used to mark end of KeyTable
+var KEYTYPE_MODIFIER = 3;   // Modifer key
+
+function clearChildren(e) {
+    while (e.firstChild !== null) {
+        e.removeChild(e.firstChild);
+    }
+}
+
+function setText(e, text) {
+    clearChildren(e);
+    e.appendChild(document.createTextNode(text));
+}
+
+function setUserAgent() {
+    var userAgent = navigator.userAgent;
+    uaDiv = document.getElementById("useragent");
+    setText(uaDiv, userAgent);
+}
+
+function addEventListener(obj, etype, handler) {
+    if (obj.addEventListener) {
+        obj.addEventListener(etype, handler, false);
+    } else if (obj.attachEvent) {
+        obj.attachEvent("on"+etype, handler);
+    } else {
+        obj["on"+etype] = handler;
+    }
+}
+
+function addClass(obj, className) {
+    obj.classList.add(className);
+}
+
+function removeClass(obj, className) {
+    obj.classList.remove(className);
+}
+
+function addInnerText(obj, text) {
+    obj.appendChild(document.createTextNode(text));
+}
+
+function calcLocation(loc) {
+    if (loc == 1) return "LEFT";
+    if (loc == 2) return "RIGHT";
+    if (loc == 3) return "NUMPAD";
+    return loc;
+}
+
+function isModifierKey(e) {
+    // Shift, Control, Alt
+    if (e.keyCode >= 16 && e.keyCode <= 18) {
+        return true;
+    }
+    // Windows, Command or Meta key.
+    if (e.keyCode == 224 // Right/Left: Gecko
+        || e.keyCode == 91    // Left: WebKit/Blink
+        || e.keyCode == 93    // Right: WebKit/Blink
+        ) {
+        return true;
+    }
+    return false;
+}
+
+function init(title, keytable) {
+    _keyTable = keytable;
+
+    createBody(title, keytable);
+
+    setUserAgent();
+
+    var input = document.getElementById("input");
+    input.disabled = true;
+    addEventListener(input, "keydown", onKeyDown);
+    addEventListener(input, "keyup", onKeyUp);
+    //addEventListener(input, "beforeInput", onBeforeInput);
+    //addEventListener(input, "input", onInput);
+}
+
+function onKeyDown(e) {
+    // Ignore modifier keys when checking modifier combinations.
+    if (_modifierMode != "None" && isModifierKey(e)) {
+        return;
+    }
+
+    _keydownInfo = [e.keyCode, e.code, e.key, e.shiftKey, e.ctrlKey, e.altKey, e.metaKey];
+    if (e.keyCode == 9 || e.code == "Tab") {
+        e.preventDefault();
+    }
+}
+
+function onKeyUp(e) {
+    // Ignore modifier keys when checking modifier combinations.
+    if (_modifierMode != "None" && isModifierKey(e)) {
+        return;
+    }
+
+    _keyupInfo = [e.keyCode, e.code, e.key, e.shiftKey, e.ctrlKey, e.altKey, e.metaKey];
+
+    if (_testing) {
+        verifyKey();
+        nextKey();
+    }
+}
+
+function onBeforeInput(e) {
+}
+
+function onInput(e) {
+}
+
+function addError(elem, str) {
+    var p = document.createElement('p');
+    p.classList.add("error2");
+    p.textContent = str;
+    elem.appendChild(p);
+}
+
+function addErrorIncorrect(elem, eventName, attrName, keyEventInfo, attr, expected) {
+    addError(elem, "Incorrect " + eventName
+        + " |" + attrName + "| = " + keyEventInfo[attr]
+        + " - Expected " + expected);
+}
+
+function verifyKeyEventFields(eventName, keyEventInfo, code, key, error) {
+    var verifyCode = document.getElementById("opt_attr_code").checked;
+    var verifyKey = document.getElementById("opt_attr_key").checked;
+    var verifyModifiers = document.getElementById("opt_attr_modifiers").checked;
+    var good = true;
+
+    if (!verifyCode && !verifyKey && !verifyModifiers) {
+        good = false;
+        addError(error, "Invalid test: At least one attribute must be selected for testing.");
+    }
+    if (verifyCode && keyEventInfo[CAPTURE_CODE] != code) {
+        good = false;
+        addErrorIncorrect(error, eventName, "code", keyEventInfo, CAPTURE_CODE, code);
+    }
+    if (verifyKey && keyEventInfo[CAPTURE_KEY] != key) {
+        good = false;
+        addErrorIncorrect(error, eventName, "key", keyEventInfo, CAPTURE_KEY, key);
+    }
+    if (verifyModifiers) {
+        if (keyEventInfo[CAPTURE_SHIFTKEY] != (_modifierMode == "Shift")) {
+            good = false;
+            addErrorIncorrect(error, eventName, "shiftKey", keyEventInfo, CAPTURE_SHIFTKEY, false);
+        }
+        if (keyEventInfo[CAPTURE_CONTROLKEY]) {
+            good = false;
+            addErrorIncorrect(error, eventName, "controlKey", keyEventInfo, CAPTURE_CONTROLKEY, false);
+        }
+        if (keyEventInfo[CAPTURE_ALTKEY]) {
+            good = false;
+            addErrorIncorrect(error, eventName, "altKey", keyEventInfo, CAPTURE_ALTKEY, false);
+        }
+        if (keyEventInfo[CAPTURE_METAKEY]) {
+            good = false;
+            addErrorIncorrect(error, eventName, "metaKey", keyEventInfo, CAPTURE_METAKEY, false);
+        }
+    }
+
+    return good;
+}
+
+function verifyKey() {
+    _keysTotal++;
+
+    var keyInfo = _keyTable[_currKey];
+    var code = keyInfo[KEYINFO_CODE];
+    var key = keyInfo[KEYINFO_KEY];
+    var keyShift = keyInfo[KEYINFO_KEY_SHIFT];
+
+    var keyCheck = key;
+    if (_modifierMode == "Shift") {
+        keyCheck = keyShift;
+    }
+
+    var verifyKeydown = document.getElementById("opt_event_keydown").checked;
+    var verifyKeyup = document.getElementById("opt_event_keyup").checked;
+
+    var error = document.createElement('div');
+    error.classList.add("error");
+    var good = true;
+
+    if (verifyKeydown) {
+        good = verifyKeyEventFields("keydown", _keydownInfo, code, keyCheck, error);
+    }
+    if (verifyKeyup) {
+        good = verifyKeyEventFields("keyup", _keyupInfo, code, keyCheck, error);
+    }
+
+    if (!verifyKeydown && !verifyKeyup) {
+        good = false;
+        addError(error, "Invalid test: At least one event must be selected for testing.");
+    }
+
+    // Allow Escape key to skip the current key.
+    var skipped = false;
+    if (_keydownInfo[CAPTURE_KEYCODE] == 27 || _keydownInfo[CAPTURE_CODE] == "Escape") {
+        good = true;
+        skipped = true;
+    }
+
+    if (!good) {
+        var p = document.createElement('p');
+        p.classList.add("error1");
+        p.textContent = "Error : " + code;
+        error.insertBefore(p, error.firstChild);
+    }
+
+    removeNextKeyHilight();
+    if (skipped) {
+        _keysSkipped++;
+        document.getElementById(code).classList.add("skippedKey")
+    } else if (good) {
+        _keysGood++;
+        document.getElementById(code).classList.add("goodKey")
+    } else {
+        _keysBad++;
+        document.getElementById(code).classList.add("badKey")
+    }
+    updateTestSummary(good ? null : error);
+}
+
+function updateTestSummary(error) {
+    document.getElementById("keys-total").textContent = _keysTotal;
+    document.getElementById("keys-good").textContent = _keysGood;
+    document.getElementById("keys-bad").textContent = _keysBad;
+    document.getElementById("keys-skipped").textContent = _keysSkipped;
+
+    if (error) {
+        var errors = document.getElementById("errors");
+        errors.insertBefore(error, errors.firstChild);
+    }
+}
+
+function resetTest() {
+    _keysTotal = 0;
+    _keysGood = 0;
+    _keysBad = 0;
+
+    _currKey = -1;
+    nextKey();
+
+    updateTestSummary();
+
+    // Remove previous test results.
+    clearChildren(document.getElementById("errors"));
+
+    // Remove highlighting from keys.
+    for (var i = 0; i < _keyTable.length; i++) {
+        var code = _keyTable[i][KEYINFO_CODE];
+        var type = _keyTable[i][KEYINFO_TYPE];
+        if (type != KEYTYPE_END) {
+            var key = document.getElementById(code);
+            key.classList.remove("goodKey");
+            key.classList.remove("badKey");
+            key.classList.remove("skippedKey");
+        }
+    }
+}
+
+function startTest() {
+    if (_testing) {
+        // Cancel the currently running test.
+        endTest();
+        return;
+    }
+
+    resetTest();
+    _testing = true;
+    document.getElementById("start").value = "Stop Test"
+
+    var input = document.getElementById("input");
+    input.value = "";
+    input.disabled = false;
+    input.focus();
+
+    // Show test instructions and info.
+    document.getElementById("test-info").style.display = 'block';
+    document.getElementById("instructions").style.display = 'block';
+    document.getElementById("test-done").style.display = 'none';
+}
+
+function endTest() {
+    _testing = false;
+    removeNextKeyHilight();
+    document.getElementById("start").value = "Restart Test"
+    document.getElementById("input").disabled = true;
+    document.getElementById("instructions").style.display = 'none';
+    document.getElementById("test-done").style.display = 'block';
+}
+
+function removeNextKeyHilight() {
+    var curr = document.getElementById(_keyTable[_currKey][KEYINFO_CODE]);
+    if (curr) {
+        removeClass(curr, "nextKey")
+    }
+}
+
+function addNextKeyHilight() {
+    var curr = document.getElementById(_keyTable[_currKey][KEYINFO_CODE]);
+    if (curr) {
+        addClass(curr, "nextKey")
+    }
+}
+
+function nextKey() {
+    var keyInfo;
+    var keepLooking = true;
+    do {
+        _currKey++;
+        keyInfo = _keyTable[_currKey];
+        var type = keyInfo[KEYINFO_TYPE];
+
+        // Skip over disabled keys.
+        keepLooking = (type == KEYTYPE_DISABLED);
+
+        // Skip over modifier keys if we're testing modifier combinations.
+        if (_modifierMode != "None" && type == KEYTYPE_MODIFIER) {
+            keepLooking = true;
+        }
+
+        // Skip over keys in disabled rows.
+        if (type != KEYTYPE_END) {
+            var row = keyInfo[KEYINFO_ROW];
+            var rowEnabled = document.getElementById("opt_row_" + row).checked;
+            keepLooking = keepLooking || !rowEnabled;
+        }
+    } while (keepLooking);
+
+    if (keyInfo[KEYINFO_TYPE] == KEYTYPE_END) {
+        endTest();
+    } else {
+        addNextKeyHilight();
+    }
+}
+
+function toggleOptions() {
+    var link = document.getElementById("optionstoggle");
+    var options = document.getElementById("options");
+    clearChildren(link);
+    if (options.style.display == "block") {
+        options.style.display = "none";
+        addInnerText(link, "Show Options");
+    }
+    else {
+        options.style.display = "block";
+        addInnerText(link, "Hide Options");
+    }
+}
+
+function toggleHelp() {
+    var link = document.getElementById("helptoggle");
+    var help = document.getElementById("help");
+    clearChildren(link);
+    if (help.style.display == "block") {
+        help.style.display = "none";
+        addInnerText(link, "Show Help");
+    }
+    else {
+        help.style.display = "block";
+        addInnerText(link, "Hide Help");
+    }
+}
+
+function createBody(title, keytable) {
+    var body = document.getElementsByTagName("body")[0];
+    var p;
+    var span;
+
+    var h1 = document.createElement('h1');
+    h1.textContent = "Keyboard Event Manual Test - " + title;
+    body.appendChild(h1);
+
+    // Display useragent.
+    p = document.createElement('p');
+    p.textContent = "UserAgent: ";
+    var useragent = document.createElement('span');
+    useragent.id = "useragent";
+    p.appendChild(useragent);
+    body.appendChild(p);
+
+    // Display input textedit.
+    p = document.createElement('p');
+    p.textContent = "Test Input: ";
+    var input1 = document.createElement('input');
+    input1.id = "input";
+    input1.type = "text";
+    input1.size = 80;
+    p.appendChild(input1);
+    p.appendChild(document.createTextNode(" "));
+    var input2 = document.createElement('input');
+    input2.id = "start";
+    input2.type = "button";
+    input2.onclick = function() { startTest(); return false; }
+    input2.value = "Start Test";
+    p.appendChild(input2);
+    p.appendChild(document.createTextNode(" "));
+    var optionsToggle = document.createElement('a');
+    optionsToggle.id = "optionstoggle";
+    optionsToggle.href = "javascript:toggleOptions()";
+    optionsToggle.textContent = "Show Options";
+    p.appendChild(optionsToggle);
+    p.appendChild(document.createTextNode(" "));
+    var helpToggle = document.createElement('a');
+    helpToggle.id = "helptoggle";
+    helpToggle.href = "javascript:toggleHelp()";
+    helpToggle.textContent = "Show Help";
+    p.appendChild(helpToggle);
+    body.appendChild(p);
+
+    createOptions(body);
+
+    createHelp(body);
+
+    createKeyboard(body, keytable);
+
+    // Test info and summary.
+    var test_info = document.createElement('div');
+    test_info.id = "test-info";
+    test_info.style.display = "none";
+
+    var instructions = document.createElement('div');
+    instructions.id = "instructions";
+    p = document.createElement('p');
+    p.textContent = "Press the highlighted key.";
+    instructions.appendChild(p);
+    test_info.appendChild(instructions);
+
+    var test_done = document.createElement('div');
+    test_done.id = "test-done";
+    p = document.createElement('p');
+    p.textContent = "Test complete!";
+    test_done.appendChild(p);
+    test_info.appendChild(test_done);
+
+    var summary = document.createElement('div');
+    summary.id = "summary";
+    p = document.createElement('p');
+    summary.appendChild(document.createTextNode("Keys Tested: "));
+    span = document.createElement('span');
+    span.id = "keys-total";
+    span.textContent = 0;
+    summary.appendChild(span);
+    summary.appendChild(document.createTextNode("; Passed "));
+    span = document.createElement('span');
+    span.id = "keys-good";
+    span.textContent = 0;
+    summary.appendChild(span);
+    summary.appendChild(document.createTextNode("; Failed "));
+    span = document.createElement('span');
+    span.id = "keys-bad";
+    span.textContent = 0;
+    summary.appendChild(span);
+    summary.appendChild(document.createTextNode("; Skipped "));
+    span = document.createElement('span');
+    span.id = "keys-skipped";
+    span.textContent = 0;
+    summary.appendChild(span);
+    test_info.appendChild(summary);
+
+    var errors = document.createElement('div');
+    errors.id = "errors";
+    test_info.appendChild(errors);
+
+    body.appendChild(test_info);
+}
+
+function addOptionTitle(cell, title) {
+    var span = document.createElement('span');
+    span.classList.add("opttitle");
+    span.textContent = title;
+    cell.appendChild(span);
+    cell.appendChild(document.createElement("br"));
+}
+
+function addOptionCheckbox(cell, id, text) {
+    var label = document.createElement("label");
+
+    var input = document.createElement("input");
+    input.type = "checkbox";
+    input.id = id;
+    input.checked = true;
+    label.appendChild(input);
+
+    label.appendChild(document.createTextNode(" " + text));
+    cell.appendChild(label);
+
+    cell.appendChild(document.createElement("br"));
+}
+
+function addOptionRadio(cell, group, text, handler, checked) {
+    var label = document.createElement("label");
+
+    var input = document.createElement("input");
+    input.type = "radio";
+    input.name = group;
+    input.value = text;
+    input.onclick = handler;
+    input.checked = checked;
+    label.appendChild(input);
+
+    label.appendChild(document.createTextNode(" " + text));
+    cell.appendChild(label);
+
+    cell.appendChild(document.createElement("br"));
+}
+
+function handleModifierGroup() {
+    var radio = document.querySelector("input[name=opt_modifier]:checked");
+    var oldMode = _modifierMode;
+    _modifierMode = radio.value;
+
+    if (oldMode == "Shift") {
+        document.getElementById("ShiftLeft").classList.remove("activeModifierKey");
+        document.getElementById("ShiftRight").classList.remove("activeModifierKey");
+    }
+
+    if (_modifierMode == "Shift") {
+        document.getElementById("ShiftLeft").classList.add("activeModifierKey");
+        document.getElementById("ShiftRight").classList.add("activeModifierKey");
+    }
+}
+
+function createOptions(body) {
+    var options = document.createElement('div');
+    options.id = "options";
+    options.style.display = "none";
+
+    var table = document.createElement('table');
+    table.classList.add("opttable");
+    var row = document.createElement('tr');
+    var cell;
+
+    cell = document.createElement('td');
+    cell.classList.add("optcell");
+    addOptionTitle(cell, "Keyboard Rows");
+    addOptionCheckbox(cell, "opt_row_0", "Row E (top)");
+    addOptionCheckbox(cell, "opt_row_1", "Row D");
+    addOptionCheckbox(cell, "opt_row_2", "Row C");
+    addOptionCheckbox(cell, "opt_row_3", "Row B");
+    addOptionCheckbox(cell, "opt_row_4", "Row A (bottom)");
+    row.appendChild(cell);
+
+    cell = document.createElement('td');
+    cell.classList.add("optcell");
+    addOptionTitle(cell, "Events");
+    addOptionCheckbox(cell, "opt_event_keydown", "keydown");
+    addOptionCheckbox(cell, "opt_event_keyup", "keyup");
+    row.appendChild(cell);
+
+    cell = document.createElement('td');
+    cell.classList.add("optcell");
+    addOptionTitle(cell, "Attributes");
+    addOptionCheckbox(cell, "opt_attr_code", "code");
+    addOptionCheckbox(cell, "opt_attr_key", "key");
+    addOptionCheckbox(cell, "opt_attr_modifiers", "modifiers");
+    row.appendChild(cell);
+
+    cell = document.createElement('td');
+    cell.classList.add("optcell");
+    addOptionTitle(cell, "Modifiers");
+    addOptionRadio(cell, "opt_modifier", "None", handleModifierGroup, true);
+    addOptionRadio(cell, "opt_modifier", "Shift", handleModifierGroup, false);
+    row.appendChild(cell);
+
+    table.appendChild(row);
+    options.appendChild(table);
+
+    body.appendChild(options);
+}
+
+function addHelpText(div, text) {
+    var p = document.createElement('p');
+    p.classList.add("help");
+    p.textContent = text;
+    div.appendChild(p);
+}
+
+function createHelp(body) {
+    var help = document.createElement('div');
+    help.id = "help";
+    help.style.display = "none";
+
+    addHelpText(help, "Click on the \"Start Test\" button to begin testing.");
+    addHelpText(help, "Press the hilighted key to test it.");
+    addHelpText(help, "Clicking anywhere outside the \"Test Input\" editbox will pause testing. To resume, click back inside the editbox.");
+    addHelpText(help, "To skip a key while testing, press Escape.");
+    addHelpText(help, "When testing with modifier keys, the modifier must be pressed before the keydown and released after the keyup of the key being tested.");
+
+    body.appendChild(help);
+}
+
+function createKeyboard(body, keytable) {
+    var keyboard = document.createElement('div');
+    keyboard.classList.add("keyboard");
+
+    var currRow = 0;
+    var row = document.createElement('div');
+    row.classList.add("key-row");
+
+    for (var i = 0; i < keytable.length; i++) {
+        var code = keytable[i][KEYINFO_CODE];
+        var rowId = keytable[i][KEYINFO_ROW];
+        var type = keytable[i][KEYINFO_TYPE];
+        var width = keytable[i][KEYINFO_WIDTH];
+        var keyCap = keytable[i][KEYINFO_KEYCAP];
+
+        if (type == KEYTYPE_END) {
+            continue;
+        }
+
+        if (rowId != currRow) {
+            keyboard.appendChild(row);
+            row = document.createElement('div');
+            row.classList.add("key-row");
+            currRow = rowId;
+        }
+
+        var key = document.createElement('div');
+        key.id = code;
+        key.classList.add("key");
+        if (width != 0) {
+            key.classList.add("wide" + width);
+        }
+        key.textContent = keyCap;
+
+        row.appendChild(key);
+    }
+
+    keyboard.appendChild(row);
+    body.appendChild(keyboard);
+}
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/DOM.event.flow.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/DOM.event.flow.html
new file mode 100644
index 0000000..513ab79
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/DOM.event.flow.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> Event dispatch and DOM event flow </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<table id="table" border="1" style="display: none">
+    <tbody id="table-body">
+    <tr id="table-row">
+        <td id="table-cell">Shady Grove</td>
+        <td>Aeolian</td>
+    </tr>
+    <tr id="parent">
+        <td id="target">Over the river, Charlie</td>
+        <td>Dorian</td>
+    </tr>
+    </tbody>
+</table>
+
+<script>
+    var EVENT = "foo";
+    var TARGET = document.getElementById("target");
+    var PARENT = document.getElementById("parent");
+    var TBODY = document.getElementById("table-body");
+    var TABLE = document.getElementById("table");
+    var BODY = document.body;
+    var HTML = document.documentElement;
+    var CurrentTargets = [window, document, HTML, BODY, TABLE, TBODY, PARENT, TARGET];
+    var ExpectResult = CurrentTargets.concat([TARGET, PARENT, TBODY, TABLE, BODY, HTML, document, window]);
+    var ActualResult = [];
+    var ExpectPhases = [1,1,1,1,1,1,1,2,2,3,3,3,3,3,3,3,];
+    var ActualPhases = [];
+
+    var description = "Test Description: Dispatch an event in a DOM tree using the DOM event flow.";
+
+    test(function()
+    {
+        for (var i=0; i < CurrentTargets.length; i++)
+        {
+            CurrentTargets[i].addEventListener(EVENT, TestEvent, true);
+            CurrentTargets[i].addEventListener(EVENT, TestEvent, false);
+        }
+
+        var evt = document.createEvent("Event");
+        evt.initEvent(EVENT, true, true);
+        TARGET.dispatchEvent(evt);
+
+        assert_array_equals(ActualResult, ExpectResult, "ActualResult");
+        assert_array_equals(ActualPhases, ExpectPhases, "ActualPhases");
+
+    }, description);
+
+    function TestEvent(evt)
+    {
+        ActualResult.push(evt.currentTarget);
+        ActualPhases.push(evt.eventPhase);
+    }
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventListener.eventHandler.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventListener.eventHandler.html
new file mode 100644
index 0000000..5df58b95
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventListener.eventHandler.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> EventLister member: handleEvent() </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<table id="table" border="1" style="display: none">
+    <tbody id="table-body">
+    <tr id="table-row">
+        <td id="table-cell">Shady Grove</td>
+        <td>Aeolian</td>
+    </tr>
+    <tr id="parent">
+        <td id="target">Over the river, Charlie</td>
+        <td>Dorian</td>
+    </tr>
+    </tbody>
+</table>
+
+<script>
+    var EVENT = "foo";
+    var TARGET = document.getElementById("target");
+    var TestResult = false;
+
+    var description = "Test Description: " +
+                      "handleEvent - This method shall be called whenever an event occurs of the event type for " +
+                      "which the EventListener interface was registered.";
+
+    var EventListener = {};
+    EventListener.handleEvent = function(evt)
+    {
+        if ((EVENT == evt.type) && (TARGET == evt.target) && (this === EventListener))
+        {
+            TestResult = true;
+        }
+        else
+        {
+            TestResult = false;
+        }
+    }
+
+    test(function()
+    {
+        var evt = document.createEvent("Event");
+        evt.initEvent(EVENT, true, true);
+
+        TARGET.addEventListener(EVENT, EventListener, true);
+        TARGET.dispatchEvent(evt);
+        TARGET.removeEventListener(EVENT, EventListener, true);
+
+        assert_true(TestResult);
+
+    }, description);
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent-expected.txt b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent-expected.txt
new file mode 100644
index 0000000..2ee74a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Test Description: An event object may be properly dispatched multiple times while also allowing to prevent the event objects propagation prior to the event dispatch. assert_array_equals: lengths differ, expected 5 got 2
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent.html
new file mode 100644
index 0000000..2ddaf70
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> Multiple dispatchEvent() and stopPropagation() </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<div id="parent" style="display: none">
+    <input id="target" type="hidden" value=""/>
+</div>
+
+<script>
+    var EVENT = "foo";
+    var TARGET = document.getElementById("target");
+    var PARENT = document.getElementById("parent");
+    var ExpectResult = [TARGET, PARENT, PARENT, document, window];
+    var ActualResult = [];
+
+    var description = "Test Description: " +
+                      "An event object may be properly dispatched multiple times while also allowing to prevent the event objects " +
+                      "propagation prior to the event dispatch.";
+
+    test(function()
+    {
+        var evt = document.createEvent("Event");
+        evt.initEvent(EVENT, true, true);
+
+        TARGET.addEventListener(EVENT, TestEvent, false);
+        PARENT.addEventListener(EVENT, TestEvent, false);
+        document.addEventListener(EVENT, TestEvent, false);
+        window.addEventListener(EVENT, TestEvent, false);
+
+        TARGET.dispatchEvent(evt);
+        PARENT.dispatchEvent(evt);
+        document.dispatchEvent(evt);
+
+        assert_array_equals(ActualResult, ExpectResult);
+
+    }, description);
+
+    function TestEvent(evt)
+    {
+        ActualResult.push(evt.currentTarget);
+
+        if (PARENT == evt.currentTarget)
+        {
+            evt.stopPropagation();
+        }
+    }
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/ProcessingInstruction.DOMCharacterDataModified.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/ProcessingInstruction.DOMCharacterDataModified.html
new file mode 100644
index 0000000..30366b374
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/ProcessingInstruction.DOMCharacterDataModified.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> ProcessingInstruction.data and DOMCharacterDataModified event </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<iframe id="helper" style="display: none"></iframe>
+
+<script>
+    var description = "Test Description: " +
+                      "DOMCharacterDataModified event fires after ProcessingInstruction.data have been modified, " +
+                      "but the node itself has not been inserted or deleted. The proximal event target of this " +
+                      "event shall be the ProcessingInstruction node.";
+
+    var t = async_test(description);
+
+    var HELPER = document.getElementById("helper");
+
+    HELPER.onload = t.step_func(function()
+    {
+        assert_true(HELPER.contentWindow.TestResult);
+        t.done();
+    });
+
+    HELPER.src = "./support/ProcessingInstruction.DOMCharacterDataModified.xml";
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/dispatchEvent.click.checkbox.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/dispatchEvent.click.checkbox.html
new file mode 100644
index 0000000..fb2c6a34
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/dispatchEvent.click.checkbox.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> MouseEvent: Default action and synthetic click event </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<div style="display: none">
+    <input type="checkbox" id="target">
+    <button id="button"> Click Here </button>
+</div>
+
+<script>
+    setup({explicit_done:true});
+    var EVENT = "click";
+    var TARGET = document.getElementById("target");
+    var BUTTON = document.getElementById("button");
+    var state;
+
+    var description = "Test Description: " +
+                      "MouseEvent: Default action is performed when a synthetic click event is dispatched on a checkbox element";
+
+    BUTTON.addEventListener(EVENT, TestEvent, true);
+    TARGET.addEventListener(EVENT, TestEvent, true);
+
+    window.onload = function()
+    {
+        state = TARGET.checked;
+        BUTTON.click();
+    }
+
+    function TestEvent(evt)
+    {
+        if (BUTTON == evt.target)
+        {
+            var e;
+            test(function()
+            {
+                BUTTON.removeEventListener(EVENT, TestEvent, true);
+
+                e = document.createEvent("MouseEvent");
+                e.initMouseEvent(EVENT,  /* type */
+                                 false,  /* bubbles */
+                                 true,   /* cancelable */
+                                 window, /* view */
+                                 1,      /* detail */
+                                 0,      /* screenX */
+                                 0,      /* screenY */
+                                 0,      /* clientX */
+                                 0,      /* clientY */
+                                 false,  /* ctrlKey */
+                                 false,  /* altKey */
+                                 false,  /* shiftKey */
+                                 false,  /* metaKey */
+                                 0,      /* button */
+                                 null    /* relatedTarget */);
+
+                assert_array_equals([TARGET.checked, e.type, e.bubbles], [state, EVENT, false]);
+
+            }, "Checkbox state is unchanged before the synthetic click event is dispatched");
+
+            TARGET.dispatchEvent(e);
+        }
+        else if (TARGET == evt.target)
+        {
+            test(function()
+            {
+                assert_array_equals([TARGET.checked, evt.type, evt.bubbles], [!state, EVENT, false]);
+
+            }, description);
+
+            done();
+        }
+    }
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/domnodeinserted.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/domnodeinserted.html
new file mode 100644
index 0000000..94804ce
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/domnodeinserted.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>MutationEvent: DOMNodeInserted Event Type</title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+<div id=log></div>
+
+<div id="target" style="display: none"></div>
+
+<script>
+var EVENT = "DOMNodeInserted";
+var TARGET;
+var NODE;
+
+var t = async_test("Test Description: DOMNodeInserted event fires when a node has been added as a child of another node.");
+
+TARGET = document.getElementById("target");
+
+TARGET.addEventListener(EVENT, t.step_func(function(evt)
+{
+    assert_equals(evt.type, EVENT);
+    t.done();
+}), true);
+
+NODE = document.createElement("INPUT");
+TARGET.appendChild(NODE);
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/stopImmediatePropagation.effect.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/stopImmediatePropagation.effect.html
new file mode 100644
index 0000000..2462e5b2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/stopImmediatePropagation.effect.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> Event.stopImmediatePropagation() immediate effect </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<table id="table" border="1" style="display: none">
+    <tbody id="table-body">
+    <tr id="table-row">
+        <td id="table-cell">Shady Grove</td>
+        <td>Aeolian</td>
+    </tr>
+    <tr id="parent">
+        <td id="target">Over the river, Charlie</td>
+        <td>Dorian</td>
+    </tr>
+    </tbody>
+</table>
+
+<script>
+    var EVENT = "foo";
+    var TARGET = document.getElementById("target");
+    var PARENT = document.getElementById("parent");
+    var TBODY = document.getElementById("table-body");
+    var TABLE = document.getElementById("table");
+    var BODY = document.body;
+    var HTML = document.documentElement;
+    var CurrentTargets = [window, document, HTML, BODY, TABLE, TBODY, PARENT, TARGET];
+    var ExpectResult = [window, window, document, document, HTML, HTML, BODY, BODY, TABLE];
+    var ActualResult = [];
+    var ExpectListeners = [0,1,0,1,0,1,0,1,0];
+    var ActualListeners = [];
+
+    var description = "Test Description: " +
+                      "stopImmediatePropagation() prevents other event listeners from being triggered and, unlike " +
+                      "Event.stopPropagation(), its effect must be immediate. Once it has been called, further calls " +
+                      "to this method have no additional effect.";
+
+    test(function()
+    {
+        for (var i=0; i < CurrentTargets.length; i++)
+        {
+            CurrentTargets[i].addEventListener(EVENT, function(e){TestEvent(e, 0)}, true);
+            CurrentTargets[i].addEventListener(EVENT, function(e){TestEvent(e, 1)}, true);
+        }
+
+        var evt = document.createEvent("Event");
+        evt.initEvent(EVENT, true, true);
+        TARGET.dispatchEvent(evt);
+
+        assert_array_equals(ActualResult, ExpectResult, "ActualResult");
+        assert_array_equals(ActualListeners, ExpectListeners, "ActualListeners");
+
+    }, description);
+
+    function TestEvent(evt, i)
+    {
+        ActualResult.push(evt.currentTarget);
+        ActualListeners.push(i);
+        if ((1 == evt.eventPhase) && (TABLE == evt.currentTarget) && (0 == i))
+        {
+            evt.stopImmediatePropagation();
+        }
+    }
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/stopPropagation.deferred.effect.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/stopPropagation.deferred.effect.html
new file mode 100644
index 0000000..4a5e03e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/stopPropagation.deferred.effect.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> Event.stopPropagation() deferred effect </title>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<table id="table" border="1" style="display: none">
+    <tbody id="table-body">
+    <tr id="table-row">
+        <td id="table-cell">Shady Grove</td>
+        <td>Aeolian</td>
+    </tr>
+    <tr id="parent">
+        <td id="target">Over the river, Charlie</td>
+        <td>Dorian</td>
+    </tr>
+    </tbody>
+</table>
+
+<script>
+    var EVENT = "foo";
+    var TARGET = document.getElementById("target");
+    var PARENT = document.getElementById("parent");
+    var TBODY = document.getElementById("table-body");
+    var TABLE = document.getElementById("table");
+    var BODY = document.body;
+    var HTML = document.documentElement;
+    var CurrentTargets = [window, document, HTML, BODY, TABLE, TBODY, PARENT, TARGET];
+    var ExpectResult = [window, window, document, document, HTML, HTML, BODY, BODY, TABLE, TABLE];
+    var ActualResult = [];
+    var ExpectListeners = [0,1,0,1,0,1,0,1,0,1];
+    var ActualListeners = [];
+
+    var description = "Test Description: " +
+                      "stopPropagation() prevents other event listeners from being triggered but its effect must be " +
+                      "deferred until all event listeners attached on the Event.currentTarget have been triggered.";
+
+    test(function()
+    {
+        for (var i=0; i < CurrentTargets.length; i++)
+        {
+            CurrentTargets[i].addEventListener(EVENT, function(e){TestEvent(e, 0)}, true);
+            CurrentTargets[i].addEventListener(EVENT, function(e){TestEvent(e, 1)}, true);
+        }
+
+        var evt = document.createEvent("Event");
+        evt.initEvent(EVENT, true, true);
+        TARGET.dispatchEvent(evt);
+
+        assert_array_equals(ActualResult, ExpectResult, "ActualResult");
+        assert_array_equals(ActualListeners, ExpectListeners, "ActualListeners");
+
+    }, description);
+
+    function TestEvent(evt, i)
+    {
+        ActualResult.push(evt.currentTarget);
+        ActualListeners.push(i);
+        if ((1 == evt.eventPhase) && (TABLE == evt.currentTarget) && (0 == i))
+        {
+            evt.stopPropagation();
+        }
+    }
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/support/ProcessingInstruction.DOMCharacterDataModified.xml b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/support/ProcessingInstruction.DOMCharacterDataModified.xml
new file mode 100644
index 0000000..4c95ae2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/approved/support/ProcessingInstruction.DOMCharacterDataModified.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<root>
+    <head xmlns="http://www.w3.org/1999/xhtml">
+        <title> ProcessingInstruction.data and DOMCharacterDataModified event </title>
+    </head>
+    <div id="log" xmlns="http://www.w3.org/1999/xhtml"></div>
+
+    <pi><?foo bar?></pi>
+
+    <script type="text/javascript" xmlns="http://www.w3.org/1999/xhtml">
+        <![CDATA[
+            var EVENT = "DOMCharacterDataModified";
+            var TARGET = document.getElementsByTagName('pi')[0].firstChild;
+            var TestResult = false;
+
+            TARGET.addEventListener(EVENT, TestEvent, false);
+            TARGET.data = "new" + TARGET.data;
+
+            function TestEvent(evt)
+            {
+                if ((EVENT == evt.type) && (TARGET == evt.target) && ("newbar" == evt.newValue))
+                {
+                    TestResult = true;
+                }
+                else
+                {
+                    TestResult = false;
+                }
+            }
+        ]]>
+    </script>
+</root>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/converted/EventListener.dispatch.new.event.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/converted/EventListener.dispatch.new.event.html
new file mode 100644
index 0000000..ee3825f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/converted/EventListener.dispatch.new.event.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> Dispatch additional events inside an event listener </title>
+<script src="../../../../../../../resources/testharness.js"></script>
+<script src="../../../../../../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id=log></div>
+
+<table id="table" border="1" style="display: none">
+    <tbody id="table-body">
+    <tr id="table-row">
+        <td id="table-cell">Shady Grove</td>
+        <td>Aeolian</td>
+    </tr>
+    <tr id="parent">
+        <td id="target">Over the river, Charlie</td>
+        <td>Dorian</td>
+    </tr>
+    </tbody>
+</table>
+
+<script>
+
+    var EVENT = "foo";
+    var TARGET = document.getElementById("target");
+    var PARENT = document.getElementById("parent");
+    var TBODY = document.getElementById("table-body");
+    var TABLE = document.getElementById("table");
+    var BODY = document.body;
+    var HTML = document.documentElement;
+    var CurrentTargets = [window, document, HTML, BODY, TABLE, TBODY, PARENT, TARGET];
+    var ExpectResult = [window, document, HTML, BODY, TABLE, TARGET, PARENT, TBODY,
+                        TABLE, BODY, HTML, document, window, TBODY, PARENT, TARGET];
+    var ActualResult = [];
+    var ExpectTypes = "foo,foo,foo,foo,foo,bar,bar,bar,bar,bar,bar,bar,bar,foo,foo,foo,";
+    var ActualTypes = "";
+
+    var description = "Test Description: " +
+                      "Implementations of the DOM event model must be reentrant. Event listeners may perform actions that " +
+                      "cause additional events to be dispatched. Such events are handled in a synchronous manner, the event " +
+                      "propagation that causes the event listener to be triggered must resume only after the event dispatch " +
+                      "of the new event is completed.";
+
+    test(function()
+    {
+        for (var i=0; i < CurrentTargets.length; i++)
+        {
+            CurrentTargets[i].addEventListener(EVENT, TestEvent, true);
+            CurrentTargets[i].addEventListener("bar", TestEvent, false);
+        }
+
+        var evt = document.createEvent("Event");
+        evt.initEvent(EVENT, false, true);
+        TARGET.dispatchEvent(evt);
+
+        assert_array_equals(ActualResult, ExpectResult, "ActualResult");
+        assert_equals(ActualTypes, ExpectTypes, "ActualTypes");
+
+    }, description);
+
+    function TestEvent(evt)
+    {
+        ActualResult.push(evt.currentTarget);
+        ActualTypes += evt.type + ",";
+
+        if (TABLE == evt.currentTarget && EVENT == evt.type)
+        {
+            var e = document.createEvent("Event");
+            e.initEvent("bar", true, true);
+            TARGET.dispatchEvent(e);
+        }
+    }
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/converted/support/ProcessingInstruction.DOMCharacterDataModified.xml b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/converted/support/ProcessingInstruction.DOMCharacterDataModified.xml
new file mode 100644
index 0000000..4c95ae2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/converted/support/ProcessingInstruction.DOMCharacterDataModified.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<root>
+    <head xmlns="http://www.w3.org/1999/xhtml">
+        <title> ProcessingInstruction.data and DOMCharacterDataModified event </title>
+    </head>
+    <div id="log" xmlns="http://www.w3.org/1999/xhtml"></div>
+
+    <pi><?foo bar?></pi>
+
+    <script type="text/javascript" xmlns="http://www.w3.org/1999/xhtml">
+        <![CDATA[
+            var EVENT = "DOMCharacterDataModified";
+            var TARGET = document.getElementsByTagName('pi')[0].firstChild;
+            var TestResult = false;
+
+            TARGET.addEventListener(EVENT, TestEvent, false);
+            TARGET.data = "new" + TARGET.data;
+
+            function TestEvent(evt)
+            {
+                if ((EVENT == evt.type) && (TARGET == evt.target) && ("newbar" == evt.newValue))
+                {
+                    TestResult = true;
+                }
+                else
+                {
+                    TestResult = false;
+                }
+            }
+        ]]>
+    </script>
+</root>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/16kb.js b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/16kb.js
new file mode 100644
index 0000000..fb3c0d6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/16kb.js
@@ -0,0 +1,140 @@
+var text =
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " +
+"This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! This is a test! " ;
+ActualResult.push("SCRIPT:loaded");
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/InvalidBitMap.png b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/InvalidBitMap.png
new file mode 100644
index 0000000..0a15a8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/InvalidBitMap.png
@@ -0,0 +1 @@
+Invalid BitMap
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/iepreview.png b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/iepreview.png
new file mode 100644
index 0000000..63959a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/iepreview.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/style01.css b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/style01.css
new file mode 100644
index 0000000..961757b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/legacy-domevents-tests/submissions/Microsoft/support/style01.css
@@ -0,0 +1,3 @@
+BODY {
+    PADDING-BOTTOM: 0px; BACKGROUND-COLOR: #eef0eb; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px
+}
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/README.md b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/README.md
new file mode 100644
index 0000000..a6ef35eb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/README.md
@@ -0,0 +1,15 @@
+Order of events
+============================
+
+Testing of how events fire in relation to one another
+
+(this is the big work item)
+
+Expecting to need:
+ * braindead test (does it work at all?)
+ * specific functional tests
+ * specific edge cases
+
+Notes:
+ * small tests so that they can be given specific names for what's broken if it fails
+ * include a link to the corresponding test in the spec.
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/event-phases-order.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/event-phases-order.html
new file mode 100644
index 0000000..46ae16d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/event-phases-order.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!--  Submitted from TestTWF Paris  -->
+<title>Event phases order</title>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function() {
+    document.addEventListener('DOMContentLoaded', this.step_func_done(function() {
+        var parent = document.getElementById('parent');
+        var child = document.getElementById('child');
+
+        var order = [];
+
+        parent.addEventListener('click', this.step_func(function(){ order.push(1) }), true);
+        child.addEventListener('click', this.step_func(function(){ order.push(2) }), false);
+        parent.addEventListener('click', this.step_func(function(){ order.push(3) }), false);
+
+        child.click();
+
+        assert_array_equals(order, [1, 2, 3]);
+    }));
+}, "Event phases order");
+</script>
+
+    <div id="parent">
+        <div id="child"></div>
+    </div>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/init-event-while-dispatching-expected.txt b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/init-event-while-dispatching-expected.txt
new file mode 100644
index 0000000..53f0a8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/init-event-while-dispatching-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS Calling initKeyboardEvent while dispatching. 
+PASS Calling initMouseEvent while dispatching. 
+FAIL Calling initCustomEvent while dispatching. assert_equals: initCustomEvent detail setter should short-circuit expected (object) null but got (number) 1
+PASS Calling initUIEvent while dispatching. 
+PASS Calling initEvent while dispatching. 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/init-event-while-dispatching.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/init-event-while-dispatching.html
new file mode 100644
index 0000000..9306e08
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/order-of-events/init-event-while-dispatching.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Re-initializing events while dispatching them</title>
+<link rel="author" title="Josh Matthews" href="mailto:josh@joshmatthews.net">
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var events = {
+  'KeyboardEvent': {
+    'constructor': function() { return new KeyboardEvent("type", {key: "A"}); },
+    'init': function(ev) { ev.initKeyboardEvent("type2", true, true, null, "a", 1, "", true, "") },
+    'check': function(ev) {
+       assert_equals(ev.key, "A", "initKeyboardEvent key setter should short-circuit");
+       assert_false(ev.repeat, "initKeyboardEvent repeat setter should short-circuit");
+       assert_equals(ev.location, 0, "initKeyboardEvent location setter should short-circuit");
+     }
+  },
+  'MouseEvent': {
+    'constructor': function() { return new MouseEvent("type"); },
+    'init': function(ev) { ev.initMouseEvent("type2", true, true, null, 0, 1, 1, 1, 1, true, true, true, true, 1, null) },
+    'check': function(ev) {
+      assert_equals(ev.screenX, 0, "initMouseEvent screenX setter should short-circuit");
+      assert_equals(ev.screenY, 0, "initMouseEvent screenY setter should short-circuit");
+      assert_equals(ev.clientX, 0, "initMouseEvent clientX setter should short-circuit");
+      assert_equals(ev.clientY, 0, "initMouseEvent clientY setter should short-circuit");
+      assert_false(ev.ctrlKey, "initMouseEvent ctrlKey setter should short-circuit");
+      assert_false(ev.altKey, "initMouseEvent altKey setter should short-circuit");
+      assert_false(ev.shiftKey, "initMouseEvent shiftKey setter should short-circuit");
+      assert_false(ev.metaKey, "initMouseEvent metaKey setter should short-circuit");
+      assert_equals(ev.button, 0, "initMouseEvent button setter should short-circuit");
+    }
+  },
+  'CustomEvent': {
+    'constructor': function() { return new CustomEvent("type") },
+    'init': function(ev) { ev.initCustomEvent("type2", true, true, 1) },
+    'check': function(ev) {
+      assert_equals(ev.detail, null, "initCustomEvent detail setter should short-circuit");
+    }
+  },
+  'UIEvent': {
+    'constructor': function() { return new UIEvent("type") },
+    'init': function(ev) { ev.initUIEvent("type2", true, true, window, 1) },
+    'check': function(ev) {
+      assert_equals(ev.view, null, "initUIEvent view setter should short-circuit");
+      assert_equals(ev.detail, 0, "initUIEvent detail setter should short-circuit");
+    }
+  },
+  'Event': {
+    'constructor': function() { return new Event("type") },
+    'init': function(ev) { ev.initEvent("type2", true, true) },
+    'check': function(ev) {
+      assert_equals(ev.bubbles, false, "initEvent bubbles setter should short-circuit");
+      assert_equals(ev.cancelable, false, "initEvent cancelable setter should short-circuit");
+      assert_equals(ev.type, "type", "initEvent type setter should short-circuit");
+    }
+  }
+};
+
+var names = Object.keys(events);
+for (var i = 0; i < names.length; i++) {
+  var t = async_test("Calling init" + names[i] + " while dispatching.");
+  t.step(function() {
+    var e = events[names[i]].constructor();
+
+    var target = document.createElement("div")
+    target.addEventListener("type", t.step_func(function() {
+      events[names[i]].init(e);
+
+      var o = e;
+      while ((o = Object.getPrototypeOf(o))) {
+        if (!(o.constructor.name in events)) {
+          break;
+        }
+        events[o.constructor.name].check(e);
+      }
+    }), false);
+
+    assert_equals(target.dispatchEvent(e), true, "dispatchEvent must return true")
+  });
+  t.done();
+}
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/resources/eventrecorder.js b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/resources/eventrecorder.js
new file mode 100644
index 0000000..936b136
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/resources/eventrecorder.js
@@ -0,0 +1,283 @@
+// interface EventRecorder {
+//    static void start();
+//    static void stop();
+//    static void clearRecords();
+//    static sequence<EventRecord> getRecords();
+//    static void configure(EventRecorderOptions options);
+// };
+// * getRecords
+//   * returns an array of EventRecord objects; the array represents the sequence of events captured at anytime after the last clear()
+//             call, between when the recorder was started and stopped (including multiple start/stop pairs)
+// * configure
+//   * sets options that should apply to the recorder. If the recorder has any existing records, than this API throws an exception.
+// * start
+//   * starts/un-pauses the recorder
+// * stop
+//   * stops/pauses the recorder
+// * clear
+//   * purges all recorded records
+
+// ----------------------
+
+// dictionary EventRecorderOptions {
+//    sequence<SupportedEventTypes> mergeEventTypes;
+//    ObjectNamedMap objectMap;
+// };
+// * mergeEventTypes
+//   * a list of event types that should be consolidated into one record when all of the following conditions are true:
+//     1) The events are of the same type and follow each other chronologically
+//     2) The events' currentTarget is the same
+//   * The default is an empty list (no event types are merged).
+// * objectMap
+//   * Sets up a series
+
+// dictionary ObjectNamedMap {
+//    //<keys will be 'targetTestID' names, with values of the objects which they label>
+// };
+//   * targetTestID = the string identifier that the associated target object should be known as (for purposes of unique identification. This
+//                    need not be the same as the Node's id attribute if it has one. If no 'targetTestID' string mapping is provided via this
+//                    map, but is encountered later when recording specific events, a generic targetTestID of 'UNKNOWN_OBJECT' is used.
+
+// ----------------------
+
+// dictionary EventRecord {
+//    unsigned long chronologicalOrder;
+//    unsigned long sequentialOccurrences;
+//    sequence<EventRecord>? nestedEvents;
+//    DOMString interfaceType;
+//    EventRecordDetails event;
+// };
+// * chronologicalOrder
+//   * Since some events may be dispatched re-entrantly (e.g., while existing events are being dispatched), and others may be merged
+//     given the 'mergeEventTypes' option in the EventRecorder, this value is the actual chronological order that the event fired
+// * sequentialOccurrences
+//   * If this event was fired multiple times in a row (see the 'mergeEventTypes' option), this value is the count of occurrences.
+//     A value of 1 means this was the only occurrence of this event (that no events were merged with it). A value greater than 1
+//     indicates that the event occurred that many times in a row.
+// * nestedEvents
+//   * The holds all the events that were sequentially dispatched synchronously while the current event was still being dispatched
+//     (e.g., between the time that this event listener was triggered and when it returned).
+//   * Has the value null if no nested events were recorded during the invocation of this listener.
+// * interfaceType
+//   * The string indicating which Event object (or derived Event object type) the recorded event object instance is based on.
+// * event
+//   * Access to the recorded event properties for the event instance (not the actual event instance itself). A snapshot of the
+//     enumerable properties of the event object instance at the moment the listener was first triggered.
+
+// ----------------------
+
+// dictionary EventRecordDetails {
+//    //<recorded property names with their values for all enumerable properties of the event object instance>
+// };
+// * EventRecordDetails
+//   * For records with 'sequentialOccurrences' > 1, only the first occurence is recorded (subsequent event details are dropped).
+//   * Object reference values (e.g., event.target, event.currentTarget, etc.) are replaced with their mapped 'targetTestID' string.
+//     If no 'targetTestID' string mapping is available for a particular object, the value 'UNKNOWN_OBJECT' is returned.
+
+// ----------------------
+
+// [NoInterfaceObject]
+// interface EventRecorderRegistration {
+//    void addRecordedEventListener(SupportedEventTypes type, EventListener? handler, optional boolean capturePhase = false);
+//    void removeRecordedEventListener(SupportedEventTypes type, EventListener? handler, optional boolean capturePhase = false);
+// };
+// Node implements EventRecorderRegistration;
+//
+// enum SupportedEventTypes = {
+//    "mousemove",
+//    etc...
+// };
+// * addRecordedEventListener
+//   * handler =      pass null if you want only a default recording of the event (and don't need any other special handling). Otherwise,
+//                    the handler will be invoked normally as part of the event's dispatch.
+//   * <other params> are the same as those defined on addEventListener/removeEventListenter APIs (see DOM4)
+//   * Use this API *instead of* addEventListener to record your events for testing purposes.
+
+(function EventRecorderScope(global) {
+   "use strict";
+
+   if (global.EventRecorder)
+      return; // Already initialized.
+
+   // WeakMap polyfill
+   if (!global.WeakMap) {
+      throw new Error("EventRecorder depends on WeakMap! Please polyfill for completeness to run in this user agent!");
+   }
+
+   // Globally applicable variables
+   var allRecords = [];
+   var recording = false;
+   var rawOrder = 1;
+   var mergeTypesTruthMap = {}; // format of { eventType: true, ... }
+   var eventsInScope = []; // Tracks synchronous event dispatches
+   var handlerMap = new WeakMap(); // Keeps original handlers (so that they can be used to un-register for events.
+
+   // Find all Event Object Constructors on the global and add them to the map along with their name (sans 'Event')
+   var eventConstructorsNameMap = new WeakMap(); // format of key: hostObject, value: alias to use.
+   var regex = /[A-Z][A-Za-z0-9]+Event$/;
+   Object.getOwnPropertyNames(global).forEach(function (propName) {
+        if (regex.test(propName))
+         eventConstructorsNameMap.set(global[propName], propName);
+   });
+   var knownObjectsMap = eventConstructorsNameMap;
+
+   Object.defineProperty(global, "EventRecorder", {
+      writable: true,
+      configurable: true,
+      value: Object.create(null, {
+         start: {
+            enumerable: true, configurable: true, writable: true, value: function start() { recording = true; }
+         },
+         stop: {
+            enumerable: true, configurable: true, writable: true, value: function stop() { recording = false; }
+         },
+         clearRecords: {
+            enumerable: true, configurable: true, writable: true, value: function clearRecords() {
+               rawOrder = 1;
+               allRecords = [];
+            }
+         },
+         getRecords: {
+            enumerable: true, configurable: true, writable: true, value: function getRecords() { return allRecords; }
+         },
+         configure: {
+            enumerable: true, configurable: true, writable: true, value: function configure(options) {
+               if (allRecords.length > 0)
+                  throw new Error("Wrong time to call me: EventRecorder.configure must only be called when no recorded events are present. Try 'clearRecords' first.");
+
+               // Un-configure existing options by calling again with no options set...
+               mergeTypesTruthMap = {};
+               knownObjectsMap = eventConstructorsNameMap;
+
+               if (!(options instanceof Object))
+                  return;
+               // Sanitize the passed object (tease-out getter functions)
+               var sanitizedOptions = {};
+               for (var x in options) {
+                  sanitizedOptions[x] = options[x];
+               }
+               if (sanitizedOptions.mergeEventTypes && Array.isArray(sanitizedOptions.mergeEventTypes)) {
+                  sanitizedOptions.mergeEventTypes.forEach(function (eventType) {
+                     if (typeof eventType == "string")
+                        mergeTypesTruthMap[eventType] = true;
+                  });
+               }
+               if (sanitizedOptions.objectMap && (sanitizedOptions.objectMap instanceof Object)) {
+                  for (var y in sanitizedOptions.objectMap) {
+                     knownObjectsMap.set(sanitizedOptions.objectMap[y], y);
+                  }
+               }
+            }
+         }
+      })
+   });
+
+   function EventRecord(rawEvent) {
+      this.chronologicalOrder = rawOrder++;
+      this.sequentialOccurrences = 1;
+      this.nestedEvents = null; // potentially a []
+      this.interfaceType = knownObjectsMap.get(rawEvent.constructor);
+      if (!this.interfaceType) // In case (somehow) this event's constructor is not named something with an 'Event' suffix...
+         this.interfaceType = rawEvent.constructor.toString();
+      this.event = new CloneObjectLike(rawEvent);
+   }
+
+   // Only enumerable props including prototype-chain (non-recursive), w/no functions.
+   function CloneObjectLike(object) {
+      for (var prop in object) {
+         var val = object[prop];
+         if (Array.isArray(val))
+            this[prop] = CloneArray(val);
+         else if (typeof val == "function")
+            continue;
+         else if ((typeof val == "object") && (val != null)) {
+            this[prop] = knownObjectsMap.get(val);
+            if (this[prop] === undefined)
+               this[prop] = "UNKNOWN_OBJECT (" + val.toString() + ")";
+         }
+         else
+            this[prop] = val;
+      }
+   }
+
+   function CloneArray(array) {
+      var dup = [];
+      for (var i = 0, len = array.length; i < len; i++) {
+         var val = array[i]
+         if (typeof val == "undefined")
+            throw new Error("Ugg. Sparce arrays are not supported. Sorry!");
+         else if (Array.isArray(val))
+            dup[i] = "UNKNOWN_ARRAY";
+         else if (typeof val == "function")
+            dup[i] = "UNKNOWN_FUNCTION";
+         else if ((typeof val == "object") && (val != null)) {
+            dup[i] = knownObjectsMap.get(val);
+            if (dup[i] === undefined)
+               dup[i] = "UNKNOWN_OBJECT (" + val.toString() + ")";
+         }
+         else
+            dup[i] = val;
+      }
+      return dup;
+   }
+
+   function generateRecordedEventHandlerWithCallback(callback) {
+      return function(e) {
+         if (recording) {
+            // Setup the scope for any synchronous events
+            eventsInScope.push(recordEvent(e));
+            callback.call(this, e);
+            eventsInScope.pop();
+         }
+      }
+   }
+
+   function recordedEventHandler(e) {
+      if (recording)
+         recordEvent(e);
+   }
+
+   function recordEvent(e) {
+      var record = new EventRecord(e);
+      var recordList = allRecords;
+      // Adjust which sequential list to use depending on scope
+      if (eventsInScope.length > 0) {
+         recordList = eventsInScope[eventsInScope.length - 1].nestedEvents;
+         if (recordList == null) // This top-of-stack event record hasn't had any nested events yet.
+            recordList = eventsInScope[eventsInScope.length - 1].nestedEvents = [];
+      }
+      if (mergeTypesTruthMap[e.type] && (recordList.length > 0)) {
+         var tail = recordList[recordList.length-1];
+         // Same type and currentTarget?
+         if ((tail.event.type == record.event.type) && (tail.event.currentTarget == record.event.currentTarget)) {
+            tail.sequentialOccurrences++;
+            return;
+         }
+      }
+      recordList.push(record);
+      return record;
+   }
+
+   Object.defineProperties(Node.prototype, {
+      addRecordedEventListener: {
+         enumerable: true, writable: true, configurable: true,
+         value: function addRecordedEventListener(type, handler, capture) {
+            if (handler == null)
+               this.addEventListener(type, recordedEventHandler, capture);
+            else {
+               var subvertedHandler = generateRecordedEventHandlerWithCallback(handler);
+               handlerMap.set(handler, subvertedHandler);
+               this.addEventListener(type, subvertedHandler, capture);
+            }
+         }
+      },
+      removeRecordedEventListener: {
+         enumerable: true, writable: true, configurable: true,
+         value: function addRecordedEventListener(type, handler, capture) {
+            var alternateHandlerUsed = handlerMap.get(handler);
+            this.removeEventListenter(type, alternateHandlerUsed ? alternateHandlerUsed : recordedEventHandler, capture);
+         }
+      }
+   });
+
+})(window);
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-and-window-error-event-expected.txt b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-and-window-error-event-expected.txt
new file mode 100644
index 0000000..2b6afc3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-and-window-error-event-expected.txt
@@ -0,0 +1,5 @@
+CONSOLE ERROR: line 22: Uncaught Error: Error from listener
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught Error: Error from listener
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-and-window-error-event.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-and-window-error-event.html
new file mode 100644
index 0000000..17523d0f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-and-window-error-event.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Throwing in event listener generates an error event on the window object</title>
+
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<script>
+    var t = async_test("Throwing in event listener generates an error event on the window object");
+
+    var errorEvent = false;
+
+    window.onerror = function(e) {
+        errorEvent = e;
+    }
+
+    document.addEventListener('DOMContentLoaded', t.step_func(function(){
+        var element = document.getElementById('bim');
+
+        element.addEventListener('click', function(){
+            throw new Error('Error from listener');
+        });
+
+        element.click();
+
+        assert_equals(typeof errorEvent, 'string');
+        t.done();
+    }));
+</script>
+
+<div id="bim">
+</div>
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-when-all-have-not-run-yet.html b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-when-all-have-not-run-yet.html
new file mode 100644
index 0000000..7becbb1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/uievents/throwing-in-listener-when-all-have-not-run-yet.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Throwing in event listener</title>
+
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<script>
+    setup({allow_uncaught_exception:true})
+    var t = async_test("Throwing in event listener");
+
+    document.addEventListener('DOMContentLoaded', t.step_func(function(){
+        var element = document.getElementById('bim');
+
+        var secondCalled = false;
+
+        element.addEventListener('click', function(){
+            throw new Error('Error from listener');
+        });
+        element.addEventListener('click', t.step_func(function(){ secondCalled = true; }), false);
+
+        element.click();
+
+        assert_true(secondCalled);
+        t.done();
+    }));
+</script>
+
+<div id="bim">
+</div>
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html
index 713774ac..e338cb07 100644
--- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html
+++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html
@@ -82,6 +82,8 @@
             expect_config('org.w3.clearkey', [{}], {}, 'Empty configuration');
 
             // Various combinations of supportedConfigurations.
+            // TODO(jrummell): Specifying contentType without codecs is
+            // deprecated, so this test should fail. http://crbug.com/605661.
             expect_config('org.w3.clearkey', [{
                 initDataTypes: ['webm'],
                 audioCapabilities: [{contentType: 'audio/webm'}],
@@ -92,6 +94,8 @@
                 videoCapabilities: [{contentType: 'video/webm'}],
             }, 'Basic supported configuration');
 
+            // TODO(jrummell): Specifying contentType without codecs is
+            // deprecated, so this test should fail. http://crbug.com/605661.
             expect_config('org.w3.clearkey', [{
                 initDataTypes: ['fakeidt', 'webm'],
                 audioCapabilities: [{contentType: 'audio/fake'}, {contentType: 'audio/webm'}],
@@ -254,11 +258,17 @@
             }, 'Codecs=');
 
             expect_config('org.w3.clearkey', [{
-                videoCapabilities: [{contentType: 'VIDEO/WEBM; CODECS="vp8"'}],
+                videoCapabilities: [{contentType: 'VIDEO/WEBM; codecs="vp8"'}],
             }], {
-                videoCapabilities: [{contentType: 'VIDEO/WEBM; CODECS="vp8"'}],
+                videoCapabilities: [{contentType: 'VIDEO/WEBM; codecs="vp8"'}],
             }, 'VIDEO/WEBM');
 
+            expect_config('org.w3.clearkey', [{
+                videoCapabilities: [{contentType: 'video/webm; CODECS="vp8"'}],
+            }], {
+                videoCapabilities: [{contentType: 'video/webm; CODECS="vp8"'}],
+            }, 'CODECS=');
+
             // Unrecognized attributes are not allowed.
             // TODO(jrummell): Unrecognized attributes are ignored currently.
             // http://crbug.com/449690
diff --git a/third_party/WebKit/LayoutTests/permissionclient/audio-permissions-expected.txt b/third_party/WebKit/LayoutTests/permissionclient/audio-permissions-expected.txt
index 2ec46927..d71576f 100644
--- a/third_party/WebKit/LayoutTests/permissionclient/audio-permissions-expected.txt
+++ b/third_party/WebKit/LayoutTests/permissionclient/audio-permissions-expected.txt
@@ -1,7 +1,7 @@
-PERMISSION CLIENT: allowMedia((file test):media/content/silence.oga): true
-PERMISSION CLIENT: allowMedia((file test):media/content/silence.oga?nocache): false
-PERMISSION CLIENT: allowMedia((file test):media/content/silence.oga): false
-PERMISSION CLIENT: allowMedia((file test):media/content/silence.oga): false
+MockContentSettingsClient: allowMedia((file test):media/content/silence.oga): true
+MockContentSettingsClient: allowMedia((file test):media/content/silence.oga?nocache): false
+MockContentSettingsClient: allowMedia((file test):media/content/silence.oga): false
+MockContentSettingsClient: allowMedia((file test):media/content/silence.oga): false
 
 PASS: first sound loaded
 
diff --git a/third_party/WebKit/LayoutTests/permissionclient/image-permissions-expected.txt b/third_party/WebKit/LayoutTests/permissionclient/image-permissions-expected.txt
index 22a9cd3..010ae1a 100644
--- a/third_party/WebKit/LayoutTests/permissionclient/image-permissions-expected.txt
+++ b/third_party/WebKit/LayoutTests/permissionclient/image-permissions-expected.txt
@@ -1,11 +1,11 @@
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): true
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): false
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): false
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif?nocache): false
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): false
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): false
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): false
-PERMISSION CLIENT: allowImage((file test):permissionclient/resources/boston.gif): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): true
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif?nocache): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): false
+MockContentSettingsClient: allowImage((file test):permissionclient/resources/boston.gif): false
 Blocked images can be reloaded, so neither onload nor onerror is called. Only check here that onload is never called when image is blocked.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/permissionclient/script-permissions-expected.txt b/third_party/WebKit/LayoutTests/permissionclient/script-permissions-expected.txt
index 8f28f02..a5a60721 100644
--- a/third_party/WebKit/LayoutTests/permissionclient/script-permissions-expected.txt
+++ b/third_party/WebKit/LayoutTests/permissionclient/script-permissions-expected.txt
@@ -1,2 +1,2 @@
-PERMISSION CLIENT: allowScriptFromSource((file test):permissionclient/resources/script.js): false
+MockContentSettingsClient: allowScriptFromSource((file test):permissionclient/resources/script.js): false
 
diff --git a/third_party/WebKit/LayoutTests/permissionclient/video-permissions-expected.txt b/third_party/WebKit/LayoutTests/permissionclient/video-permissions-expected.txt
index eabbc45..16f3816f 100644
--- a/third_party/WebKit/LayoutTests/permissionclient/video-permissions-expected.txt
+++ b/third_party/WebKit/LayoutTests/permissionclient/video-permissions-expected.txt
@@ -1,7 +1,7 @@
-PERMISSION CLIENT: allowMedia((file test):media/content/test.ogv): true
-PERMISSION CLIENT: allowMedia((file test):media/content/test.ogv?nocache): false
-PERMISSION CLIENT: allowMedia((file test):media/content/test.ogv): false
-PERMISSION CLIENT: allowMedia((file test):media/content/test.ogv): false
+MockContentSettingsClient: allowMedia((file test):media/content/test.ogv): true
+MockContentSettingsClient: allowMedia((file test):media/content/test.ogv?nocache): false
+MockContentSettingsClient: allowMedia((file test):media/content/test.ogv): false
+MockContentSettingsClient: allowMedia((file test):media/content/test.ogv): false
  
 PASS: first video loaded
 
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
index f05e8a7c..50b9228 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
index 986b6f11..13db562 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/emphasis-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/emphasis-expected.txt
deleted file mode 100644
index 648c745..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/emphasis-expected.txt
+++ /dev/null
@@ -1,198 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x553
-  LayoutBlockFlow {HTML} at (0,0) size 800x553
-    LayoutBlockFlow {BODY} at (8,8) size 784x537
-      LayoutBlockFlow {DIV} at (4,4) size 366x146 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,3) size 273x28
-          text run at (3,3) width 273: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 214x28
-          LayoutText {#text} at (3,45) size 214x28
-            text run at (3,45) width 214: "consectetur adipiscing"
-        LayoutText {#text} at (216,45) size 134x28
-          text run at (216,45) width 7: " "
-          text run at (222,45) width 128: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 110x28
-          LayoutText {#text} at (3,73) size 110x28
-            text run at (3,73) width 110: "odio sapien"
-        LayoutText {#text} at (112,73) size 334x70
-          text run at (112,73) width 13: ", "
-          text run at (124,73) width 213: "lobortis eu iaculis vel,"
-          text run at (3,115) width 208: "scelerisque nec dolor."
-      LayoutText {#text} at (374,119) size 6x28
-        text run at (374,119) width 6: " "
-      LayoutBlockFlow {DIV} at (384,4) size 366x146 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,3) size 273x28
-          text run at (3,3) width 273: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 214x28
-          LayoutText {#text} at (3,45) size 214x28
-            text run at (3,45) width 214: "consectetur adipiscing"
-        LayoutText {#text} at (216,45) size 134x28
-          text run at (216,45) width 7: " "
-          text run at (222,45) width 128: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 110x28
-          LayoutText {#text} at (3,73) size 110x28
-            text run at (3,73) width 110: "odio sa\x{300}pien"
-        LayoutText {#text} at (112,73) size 334x70
-          text run at (112,73) width 13: ", "
-          text run at (124,73) width 213: "lobortis eu iaculis vel,"
-          text run at (3,115) width 208: "scelerisque nec dolor."
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {DIV} at (4,160) size 366x167 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,18) size 70x28
-          text run at (3,18) width 70: "Lorem "
-        LayoutInline {SPAN} at (0,0) size 60x28
-          LayoutText {#text} at (72,18) size 60x28
-            text run at (72,18) width 60: "ipsum"
-        LayoutText {#text} at (131,18) size 7x28
-          text run at (131,18) width 7: " "
-        LayoutInline {SPAN} at (0,0) size 52x28
-          LayoutText {#text} at (137,18) size 52x28
-            text run at (137,18) width 52: "dolor"
-        LayoutText {#text} at (188,18) size 7x28
-          text run at (188,18) width 7: " "
-        LayoutInline {SPAN} at (0,0) size 23x28
-          LayoutText {#text} at (194,18) size 23x28
-            text run at (194,18) width 23: "sit"
-        LayoutText {#text} at (216,18) size 7x28
-          text run at (216,18) width 7: " "
-        LayoutInline {SPAN} at (0,0) size 48x28
-          LayoutText {#text} at (222,18) size 48x28
-            text run at (222,18) width 48: "amet"
-        LayoutText {#text} at (269,18) size 7x28
-          text run at (269,18) width 7: ","
-        LayoutInline {SPAN} at (0,0) size 110x28
-          LayoutText {#text} at (3,63) size 110x28
-            text run at (3,63) width 110: "consectetur"
-        LayoutText {#text} at (112,63) size 7x28
-          text run at (112,63) width 7: " "
-        LayoutInline {SPAN} at (0,0) size 99x28
-          LayoutText {#text} at (118,63) size 99x28
-            text run at (118,63) width 99: "adipiscing"
-        LayoutText {#text} at (216,63) size 7x28
-          text run at (216,63) width 7: " "
-        LayoutInline {SPAN} at (0,0) size 32x28
-          LayoutText {#text} at (222,63) size 32x28
-            text run at (222,63) width 32: "elit"
-        LayoutText {#text} at (253,63) size 13x28
-          text run at (253,63) width 13: ". "
-        LayoutInline {SPAN} at (0,0) size 85x28
-          LayoutText {#text} at (265,63) size 85x28
-            text run at (265,63) width 85: "Aliquam"
-        LayoutText {#text} at (349,63) size 7x28
-          text run at (349,63) width 7: ","
-        LayoutInline {SPAN} at (0,0) size 43x28
-          LayoutText {#text} at (3,108) size 43x28
-            text run at (3,108) width 43: "odio"
-        LayoutText {#text} at (45,108) size 7x28
-          text run at (45,108) width 7: " "
-        LayoutInline {SPAN} at (0,0) size 62x28
-          LayoutText {#text} at (51,108) size 62x28
-            text run at (51,108) width 62: "sapien"
-        LayoutText {#text} at (112,108) size 13x28
-          text run at (112,108) width 13: ", "
-        LayoutInline {SPAN} at (0,0) size 75x28
-          LayoutText {#text} at (124,108) size 75x28
-            text run at (124,108) width 75: "lobortis"
-        LayoutText {#text} at (198,108) size 334x56
-          text run at (198,108) width 7: " "
-          text run at (204,108) width 133: "eu iaculis vel,"
-          text run at (3,136) width 208: "scelerisque nec dolor."
-      LayoutText {#text} at (374,296) size 6x28
-        text run at (374,296) width 6: " "
-      LayoutBlockFlow {DIV} at (384,158) size 366x156 [border: (3px solid #000000)]
-        LayoutText {#text} at (15,3) size 28x70
-          text run at (15,3) width 70: "Lorem "
-        LayoutInline {SPAN} at (0,0) size 28x60
-          LayoutText {#text} at (15,72) size 28x60
-            text run at (15,72) width 59: "ipsum"
-        LayoutText {#text} at (0,0) size 0x0
-        LayoutInline {SPAN} at (0,0) size 28x51
-          LayoutText {#text} at (58,3) size 28x51
-            text run at (58,3) width 51: "dolor"
-        LayoutText {#text} at (58,53) size 28x7
-          text run at (58,53) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 28x24
-          LayoutText {#text} at (58,59) size 28x24
-            text run at (58,59) width 23: "sit"
-        LayoutText {#text} at (58,82) size 28x7
-          text run at (58,82) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 28x47
-          LayoutText {#text} at (58,88) size 28x47
-            text run at (58,88) width 47: "amet"
-        LayoutText {#text} at (58,134) size 28x7
-          text run at (58,134) width 6: ","
-        LayoutInline {SPAN} at (0,0) size 28x110
-          LayoutText {#text} at (98,3) size 28x110
-            text run at (98,3) width 110: "consectetur"
-        LayoutText {#text} at (0,0) size 0x0
-        LayoutInline {SPAN} at (0,0) size 28x99
-          LayoutText {#text} at (143,3) size 28x99
-            text run at (143,3) width 99: "adipiscing"
-        LayoutText {#text} at (143,101) size 28x7
-          text run at (143,101) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 28x32
-          LayoutText {#text} at (143,107) size 28x32
-            text run at (143,107) width 31: "elit"
-        LayoutText {#text} at (143,138) size 28x7
-          text run at (143,138) width 6: "."
-        LayoutInline {SPAN} at (0,0) size 28x84
-          LayoutText {#text} at (188,3) size 28x84
-            text run at (188,3) width 84: "Aliquam"
-        LayoutText {#text} at (188,86) size 28x13
-          text run at (188,86) width 12: ", "
-        LayoutInline {SPAN} at (0,0) size 28x44
-          LayoutText {#text} at (188,98) size 28x44
-            text run at (188,98) width 43: "odio"
-        LayoutText {#text} at (0,0) size 0x0
-        LayoutInline {SPAN} at (0,0) size 28x62
-          LayoutText {#text} at (233,3) size 28x62
-            text run at (233,3) width 62: "sapien"
-        LayoutText {#text} at (233,64) size 28x13
-          text run at (233,64) width 12: ", "
-        LayoutInline {SPAN} at (0,0) size 28x74
-          LayoutText {#text} at (233,76) size 28x74
-            text run at (233,76) width 74: "lobortis"
-        LayoutText {#text} at (261,3) size 84x146
-          text run at (261,3) width 133: "eu iaculis vel,"
-          text run at (289,3) width 146: "scelerisque nec"
-          text run at (317,3) width 56: "dolor."
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {DIV} at (4,335) size 366x198 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,13) size 273x28
-          text run at (3,13) width 273: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 214x28
-          LayoutText {#text} at (3,61) size 214x28
-            text run at (3,61) width 214: "consectetur adipiscing"
-        LayoutText {#text} at (216,61) size 134x28
-          text run at (216,61) width 7: " "
-          text run at (222,61) width 128: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 110x28
-          LayoutText {#text} at (3,109) size 110x28
-            text run at (3,109) width 110: "odio sapien"
-        LayoutText {#text} at (112,109) size 334x76
-          text run at (112,109) width 13: ", "
-          text run at (124,109) width 213: "lobortis eu iaculis vel,"
-          text run at (3,157) width 208: "scelerisque nec dolor."
-      LayoutText {#text} at (374,492) size 6x28
-        text run at (374,492) width 6: " "
-      LayoutBlockFlow {DIV} at (384,377) size 366x146 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,3) size 273x28
-          text run at (3,3) width 273: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 214x28
-          LayoutText {#text} at (3,45) size 214x28
-            text run at (3,45) width 214: "consectetur adipiscing"
-        LayoutText {#text} at (216,45) size 134x28
-          text run at (216,45) width 7: " "
-          text run at (222,45) width 128: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 110x28
-          LayoutText {#text} at (3,73) size 110x28
-            text run at (3,73) width 110: "odio sapien"
-        LayoutText {#text} at (112,73) size 334x70
-          text run at (112,73) width 13: ", "
-          text run at (124,73) width 213: "lobortis eu iaculis vel,"
-          text run at (3,115) width 208: "scelerisque nec dolor."
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutText {#text} at (0,0) size 0x0
-selection start: position 10 of child 0 {#text} of child 1 {SPAN} of child 10 {DIV} of body
-selection end:   position 7 of child 0 {#text} of child 3 {SPAN} of child 10 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
index d21f1325..f390e5c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
index aef78d1..8fa9399 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew-expected.png
index 7d608d9..54543b0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
index b10c33a..b86622bf 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
index dd6db84..f36fd022 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/editing/selection/5057506-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/editing/selection/5057506-expected.txt
index 40a6900..03d610b 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/editing/selection/5057506-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/editing/selection/5057506-expected.txt
@@ -1,4 +1,4 @@
-ALERT: 35
+ALERT: 34
 ALERT: 108
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
index fed0f52..a43eb9b8 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
index a8ab22ef..a1ecd9c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.png
index 9da59f5..af48c0db 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.txt
index cba1020..648c745 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/emphasis-expected.txt
@@ -18,7 +18,7 @@
         LayoutText {#text} at (112,73) size 334x70
           text run at (112,73) width 13: ", "
           text run at (124,73) width 213: "lobortis eu iaculis vel,"
-          text run at (3,115) width 209: "scelerisque nec dolor."
+          text run at (3,115) width 208: "scelerisque nec dolor."
       LayoutText {#text} at (374,119) size 6x28
         text run at (374,119) width 6: " "
       LayoutBlockFlow {DIV} at (384,4) size 366x146 [border: (3px solid #000000)]
@@ -36,7 +36,7 @@
         LayoutText {#text} at (112,73) size 334x70
           text run at (112,73) width 13: ", "
           text run at (124,73) width 213: "lobortis eu iaculis vel,"
-          text run at (3,115) width 209: "scelerisque nec dolor."
+          text run at (3,115) width 208: "scelerisque nec dolor."
       LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {DIV} at (4,160) size 366x167 [border: (3px solid #000000)]
         LayoutText {#text} at (3,18) size 70x28
@@ -97,7 +97,7 @@
         LayoutText {#text} at (198,108) size 334x56
           text run at (198,108) width 7: " "
           text run at (204,108) width 133: "eu iaculis vel,"
-          text run at (3,136) width 209: "scelerisque nec dolor."
+          text run at (3,136) width 208: "scelerisque nec dolor."
       LayoutText {#text} at (374,296) size 6x28
         text run at (374,296) width 6: " "
       LayoutBlockFlow {DIV} at (384,158) size 366x156 [border: (3px solid #000000)]
@@ -156,7 +156,7 @@
         LayoutText {#text} at (261,3) size 84x146
           text run at (261,3) width 133: "eu iaculis vel,"
           text run at (289,3) width 146: "scelerisque nec"
-          text run at (317,3) width 57: "dolor."
+          text run at (317,3) width 56: "dolor."
       LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {DIV} at (4,335) size 366x198 [border: (3px solid #000000)]
         LayoutText {#text} at (3,13) size 273x28
@@ -173,7 +173,7 @@
         LayoutText {#text} at (112,109) size 334x76
           text run at (112,109) width 13: ", "
           text run at (124,109) width 213: "lobortis eu iaculis vel,"
-          text run at (3,157) width 209: "scelerisque nec dolor."
+          text run at (3,157) width 208: "scelerisque nec dolor."
       LayoutText {#text} at (374,492) size 6x28
         text run at (374,492) width 6: " "
       LayoutBlockFlow {DIV} at (384,377) size 366x146 [border: (3px solid #000000)]
@@ -191,7 +191,7 @@
         LayoutText {#text} at (112,73) size 334x70
           text run at (112,73) width 13: ", "
           text run at (124,73) width 213: "lobortis eu iaculis vel,"
-          text run at (3,115) width 209: "scelerisque nec dolor."
+          text run at (3,115) width 208: "scelerisque nec dolor."
       LayoutText {#text} at (0,0) size 0x0
       LayoutText {#text} at (0,0) size 0x0
 selection start: position 10 of child 0 {#text} of child 1 {SPAN} of child 10 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/editing/selection/5057506-expected.txt b/third_party/WebKit/LayoutTests/platform/win/editing/selection/5057506-expected.txt
index c9d02d3..09d02551 100644
--- a/third_party/WebKit/LayoutTests/platform/win/editing/selection/5057506-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/editing/selection/5057506-expected.txt
@@ -1,4 +1,4 @@
-ALERT: 35
+ALERT: 34
 ALERT: 107.5
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/international/vertical-text-metrics-test-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/text/international/vertical-text-metrics-test-expected.txt
index a5f5c9f..6193f61 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/international/vertical-text-metrics-test-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/international/vertical-text-metrics-test-expected.txt
@@ -10,10 +10,10 @@
 string「あ、変っ!」。
 string「あ、変っ!」。
 
-width=220
+width=219
 width=37
 width=37
-width=220
+width=219
 width=37
 width=37
 
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
index b629db3..fc40f89 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/text/international/vertical-text-metrics-test-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/fast/text/international/vertical-text-metrics-test-expected.txt
index 1c9cb482c..bc2da6ca 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/fast/text/international/vertical-text-metrics-test-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/text/international/vertical-text-metrics-test-expected.txt
@@ -10,10 +10,10 @@
 string「あ、変っ!」。
 string「あ、変っ!」。
 
-width=220
+width=219
 width=32
 width=32
-width=220
+width=219
 width=32
 width=32
 
diff --git a/third_party/WebKit/LayoutTests/virtual/android/media/mediadocument/resources/standalone-audio.html b/third_party/WebKit/LayoutTests/virtual/android/media/mediadocument/resources/standalone-audio.html
index 970526fb..13c8802a 100644
--- a/third_party/WebKit/LayoutTests/virtual/android/media/mediadocument/resources/standalone-audio.html
+++ b/third_party/WebKit/LayoutTests/virtual/android/media/mediadocument/resources/standalone-audio.html
@@ -32,9 +32,8 @@
 
 .div2 {
   text-align: center;
-  position: absolute;
-  left: 0;
-  right: 0;
+  height: 0;
+  flex: none;
 }
 
 .video {
@@ -49,4 +48,4 @@
 <video controls autoplay class="video"><source src="../../../../../media/content/silence.wav"></video>
 <div class="div2"><a href="../../../../../media/content/silence.wav" download class="button1">SAVE</a></div>
 </div>
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp
index 2d0af914d..639633f 100644
--- a/third_party/WebKit/Source/core/frame/Deprecation.cpp
+++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -359,7 +359,7 @@
         return willBeRemoved("'results' attribute", 53, "5738199536107520");
 
     case UseCounter::TouchDragUserGestureUsedCrossOrigin:
-        return willBeRemoved("Performing sensitive operations in iframes on touch events which don't represent a tap gesture", 52, "https://www.chromestatus.com/features/5649871251963904");
+        return willBeRemoved("Performing sensitive operations in iframes on touch events which don't represent a tap gesture", 52, "5649871251963904");
 
     case UseCounter::WebAnimationsEasingAsFunctionLinear:
         return String::format("Specifying animation easing as a function is deprecated and all support will be removed in %s, at which point this will throw a TypeError. This warning may have been triggered by the Web Animations or Polymer polyfills. See http://crbug.com/601672 for details.", milestoneString(54));
@@ -367,6 +367,9 @@
     case UseCounter::WindowPostMessageWithLegacyTargetOriginArgument:
         return replacedWillBeRemoved("'window.postMessage(message, transferables, targetOrigin)'", "'window.postMessage(message, targetOrigin, transferables)'", 54, "5719033043222528");
 
+    case UseCounter::EncryptedMediaAllSelectedContentTypesMissingCodecs:
+        return "contentType strings without codecs will not be supported by requestMediaKeySystemAccess() in the future. Please specify the desired codec(s) as part of the contentType.";
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
         return String();
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 477d6665..ed51fa1 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -398,10 +398,11 @@
     const bool frameSizeChanged = oldRect.size() != newRect.size();
 
     m_needsScrollbarsUpdate = frameSizeChanged;
-    // If layout is clean then update scrollbars immediately otherwise wait
-    // for layout to ensure content size is correct too.
-    if (!needsLayout())
-        updateScrollbarsIfNeeded();
+    // TODO(wjmaclean): find out why scrollbars fail to resize for complex
+    // subframes after changing the zoom level. For now always calling
+    // updateScrollbarsIfNeeded() here fixes the issue, but it would be good to
+    // discover the deeper cause of this. http://crbug.com/607987.
+    updateScrollbarsIfNeeded();
 
     frameRectsChanged();
 
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 13971d6..5b2de29 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1138,6 +1138,8 @@
         FileAPINativeLineEndings = 1320,
         PointerEventAttributeCount = 1321,
         CompositedReplication = 1322,
+        EncryptedMediaAllSelectedContentTypesHaveCodecs = 1323,
+        EncryptedMediaAllSelectedContentTypesMissingCodecs = 1324,
 
         // Add new features immediately above this line. Don't change assigned
         // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
index b98afbf..f867787 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
@@ -45,7 +45,6 @@
 #include "platform/graphics/ImageBuffer.h"
 #include "platform/graphics/gpu/Extensions3DUtil.h"
 #include "public/platform/WebCanvas.h"
-#include "public/platform/WebGraphicsContext3D.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/html/MediaDocument.cpp b/third_party/WebKit/Source/core/html/MediaDocument.cpp
index 3c758c3..8636dfa 100644
--- a/third_party/WebKit/Source/core/html/MediaDocument.cpp
+++ b/third_party/WebKit/Source/core/html/MediaDocument.cpp
@@ -196,10 +196,9 @@
         anchor->addEventListener(EventTypeNames::click, listener, false);
         HTMLDivElement* buttonContainer = HTMLDivElement::create(*document());
         buttonContainer->setAttribute(styleAttr,
-            "position: absolute;"
             "text-align: center;"
-            "left: 0;"
-            "right: 0;");
+            "height: 0;"
+            "flex: none");
         buttonContainer->appendChild(anchor);
         div->appendChild(buttonContainer);
         recordDownloadMetric(MediaDocumentDownloadButtonShown);
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
index 3e1360d..06e73dba 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
@@ -117,7 +117,7 @@
     , m_hasMarkupTruncation(false)
     , m_widthAvailableToChildrenChanged(false)
     , m_heightAvailableToChildrenChanged(false)
-    , m_hasOnlySelfCollapsingChildren(false)
+    , m_isSelfCollapsing(false)
     , m_descendantsWithFloatsMarkedForLayout(false)
     , m_hasPositionedObjects(false)
     , m_hasPercentHeightDescendants(false)
@@ -744,68 +744,6 @@
     }
 }
 
-bool LayoutBlock::isSelfCollapsingBlock() const
-{
-    // We are not self-collapsing if we
-    // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
-    // (b) are a table,
-    // (c) have border/padding,
-    // (d) have a min-height
-    // (e) have specified that one of our margins can't collapse using a CSS extension
-    // (f) establish a new block formatting context.
-
-    // The early exit must be done before we check for clean layout.
-    // We should be able to give a quick answer if the box is a relayout boundary.
-    // Being a relayout boundary implies a block formatting context, and also
-    // our internal layout shouldn't affect our container in any way.
-    if (createsNewFormattingContext())
-        return false;
-
-    // Placeholder elements are not laid out until the dimensions of their parent text control are known, so they
-    // don't get layout until their parent has had layout - this is unique in the layout tree and means
-    // when we call isSelfCollapsingBlock on them we find that they still need layout.
-    ASSERT(!needsLayout() || (node() && node()->isElementNode() && toElement(node())->shadowPseudoId() == "-webkit-input-placeholder"));
-
-    if (logicalHeight() > 0
-        || isTable() || borderAndPaddingLogicalHeight()
-        || style()->logicalMinHeight().isPositive()
-        || style()->marginBeforeCollapse() == MarginCollapseSeparate || style()->marginAfterCollapse() == MarginCollapseSeparate)
-        return false;
-
-    Length logicalHeightLength = style()->logicalHeight();
-    bool hasAutoHeight = logicalHeightLength.isAuto();
-    if (logicalHeightLength.hasPercent() && !document().inQuirksMode()) {
-        hasAutoHeight = true;
-        for (LayoutBlock* cb = containingBlock(); !cb->isLayoutView(); cb = cb->containingBlock()) {
-            if (cb->style()->logicalHeight().isFixed() || cb->isTableCell())
-                hasAutoHeight = false;
-        }
-    }
-
-    // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
-    // on whether we have content that is all self-collapsing or not.
-    // TODO(alancutter): Make this work correctly for calc lengths.
-    if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.hasPercent()) && logicalHeightLength.isZero())) {
-        // If the block has inline children, see if we generated any line boxes.  If we have any
-        // line boxes, then we can't be self-collapsing, since we have content.
-        if (childrenInline())
-            return !firstLineBox();
-
-        // Whether or not we collapse is dependent on whether all our normal flow children
-        // are also self-collapsing.
-        if (m_hasOnlySelfCollapsingChildren)
-            return true;
-        for (LayoutBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
-            if (child->isFloatingOrOutOfFlowPositioned())
-                continue;
-            if (!child->isSelfCollapsingBlock())
-                return false;
-        }
-        return true;
-    }
-    return false;
-}
-
 void LayoutBlock::startDelayUpdateScrollInfo()
 {
     if (gDelayUpdateScrollInfo == 0) {
@@ -1549,24 +1487,6 @@
     return minimumValueForLength(style()->textIndent(), cw);
 }
 
-void LayoutBlock::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest)
-{
-    if (logicalTop >= logicalBottom)
-        return;
-
-    RootInlineBox* lowestDirtyLine = lastRootBox();
-    RootInlineBox* afterLowest = lowestDirtyLine;
-    while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) {
-        afterLowest = lowestDirtyLine;
-        lowestDirtyLine = lowestDirtyLine->prevRootBox();
-    }
-
-    while (afterLowest && afterLowest != highest && (afterLowest->lineBottomWithLeading() >= logicalTop || afterLowest->lineBottomWithLeading() < 0)) {
-        afterLowest->markDirty();
-        afterLowest = afterLowest->prevRootBox();
-    }
-}
-
 bool LayoutBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset) const
 {
     if (!scrollsOverflow())
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.h b/third_party/WebKit/Source/core/layout/LayoutBlock.h
index 073fa932..120557d2e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlock.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBlock.h
@@ -405,8 +405,6 @@
 
     void addChildIgnoringContinuation(LayoutObject* newChild, LayoutObject* beforeChild) override;
 
-    bool isSelfCollapsingBlock() const override;
-
     TrackedLayoutBoxListHashSet* positionedObjectsInternal() const;
     TrackedLayoutBoxListHashSet* percentHeightDescendantsInternal() const;
 
@@ -443,8 +441,6 @@
     LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = nullptr) final;
     bool isInlineBoxWrapperActuallyChild() const;
 
-    void markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest = nullptr);
-
     Position positionForBox(InlineBox*, bool start = true) const;
     PositionWithAffinity positionForPointWithInlineChildren(const LayoutPoint&);
 
@@ -503,7 +499,7 @@
     unsigned m_hasMarkupTruncation : 1;
     unsigned m_widthAvailableToChildrenChanged  : 1;
     unsigned m_heightAvailableToChildrenChanged  : 1;
-    mutable unsigned m_hasOnlySelfCollapsingChildren : 1;
+    unsigned m_isSelfCollapsing : 1; // True if margin-before and margin-after are adjoining.
     mutable unsigned m_descendantsWithFloatsMarkedForLayout : 1;
 
     unsigned m_hasPositionedObjects : 1;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
index 9e94f5d..078c1b0 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
@@ -297,8 +297,74 @@
 
 bool LayoutBlockFlow::isSelfCollapsingBlock() const
 {
-    m_hasOnlySelfCollapsingChildren = LayoutBlock::isSelfCollapsingBlock();
-    return m_hasOnlySelfCollapsingChildren;
+    if (needsLayout()) {
+        // Sometimes we don't lay out objects in DOM order (column spanners being one such relevant
+        // type of object right here). As long as the object in question establishes a new
+        // formatting context, that's nothing to worry about, though.
+        ASSERT(createsNewFormattingContext());
+        return false;
+    }
+    ASSERT(!m_isSelfCollapsing == !checkIfIsSelfCollapsingBlock());
+    return m_isSelfCollapsing;
+}
+
+bool LayoutBlockFlow::checkIfIsSelfCollapsingBlock() const
+{
+    // We are not self-collapsing if we
+    // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
+    // (b) have border/padding,
+    // (c) have a min-height
+    // (d) have specified that one of our margins can't collapse using a CSS extension
+    // (e) establish a new block formatting context.
+
+    // The early exit must be done before we check for clean layout.
+    // We should be able to give a quick answer if the box is a relayout boundary.
+    // Being a relayout boundary implies a block formatting context, and also
+    // our internal layout shouldn't affect our container in any way.
+    if (createsNewFormattingContext())
+        return false;
+
+    // Placeholder elements are not laid out until the dimensions of their parent text control are known, so they
+    // don't get layout until their parent has had layout - this is unique in the layout tree and means
+    // when we call isSelfCollapsingBlock on them we find that they still need layout.
+    ASSERT(!needsLayout() || (node() && node()->isElementNode() && toElement(node())->shadowPseudoId() == "-webkit-input-placeholder"));
+
+    if (logicalHeight() > LayoutUnit()
+        || borderAndPaddingLogicalHeight()
+        || style()->logicalMinHeight().isPositive()
+        || style()->marginBeforeCollapse() == MarginCollapseSeparate || style()->marginAfterCollapse() == MarginCollapseSeparate)
+        return false;
+
+    Length logicalHeightLength = style()->logicalHeight();
+    bool hasAutoHeight = logicalHeightLength.isAuto();
+    if (logicalHeightLength.hasPercent() && !document().inQuirksMode()) {
+        hasAutoHeight = true;
+        for (LayoutBlock* cb = containingBlock(); !cb->isLayoutView(); cb = cb->containingBlock()) {
+            if (cb->style()->logicalHeight().isFixed() || cb->isTableCell())
+                hasAutoHeight = false;
+        }
+    }
+
+    // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
+    // on whether we have content that is all self-collapsing or not.
+    // TODO(alancutter): Make this work correctly for calc lengths.
+    if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.hasPercent()) && logicalHeightLength.isZero())) {
+        // If the block has inline children, see if we generated any line boxes.  If we have any
+        // line boxes, then we can't be self-collapsing, since we have content.
+        if (childrenInline())
+            return !firstLineBox();
+
+        // Whether or not we collapse is dependent on whether all our normal flow children
+        // are also self-collapsing.
+        for (LayoutBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
+            if (child->isFloatingOrOutOfFlowPositioned())
+                continue;
+            if (!child->isSelfCollapsingBlock())
+                return false;
+        }
+        return true;
+    }
+    return false;
 }
 
 void LayoutBlockFlow::layoutBlock(bool relayoutChildren)
@@ -306,11 +372,6 @@
     ASSERT(needsLayout());
     ASSERT(isInlineBlockOrInlineTable() || !isInline());
 
-    // If we are self-collapsing with self-collapsing descendants this will get set to save us burrowing through our
-    // descendants every time in |isSelfCollapsingBlock|. We reset it here so that |isSelfCollapsingBlock| attempts to burrow
-    // at least once and so that it always gives a reliable result reflecting the latest layout.
-    m_hasOnlySelfCollapsingChildren = false;
-
     if (!relayoutChildren && simplifiedLayout())
         return;
 
@@ -347,6 +408,7 @@
         positionDialog();
 
     clearNeedsLayout();
+    m_isSelfCollapsing = checkIfIsSelfCollapsingBlock();
 }
 
 inline bool LayoutBlockFlow::layoutBlockFlow(bool relayoutChildren, LayoutUnit &pageLogicalHeight, SubtreeLayoutScope& layoutScope)
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h
index 0c77987..67fba0f4 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h
@@ -561,6 +561,7 @@
     LayoutUnit m_paintInvalidationLogicalBottom;
 
     bool isSelfCollapsingBlock() const override;
+    bool checkIfIsSelfCollapsingBlock() const;
 
 protected:
     OwnPtr<LayoutBlockFlowRareData> m_rareData;
@@ -599,6 +600,7 @@
     bool matchedEndLine(LineLayoutState&, const InlineBidiResolver&, const InlineIterator& endLineStart, const BidiStatus& endLineStatus);
     void deleteEllipsisLineBoxes();
     void checkLinesForTextOverflow();
+    void markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest = nullptr);
     // Positions new floats and also adjust all floats encountered on the line if any of them
     // have to move to the next page/column.
     void positionDialog();
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
index 24d2466..d4b3a8b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
@@ -1568,12 +1568,6 @@
         deleteEllipsisLineBoxes();
 
     if (firstChild()) {
-        // In full layout mode, clear the line boxes of children upfront. Otherwise,
-        // siblings can run into stale root lineboxes during layout. Then layout
-        // the replaced elements later. In partial layout mode, line boxes are not
-        // deleted and only dirtied. In that case, we can layout the replaced
-        // elements at the same time.
-        Vector<LayoutBox*> replacedChildren;
         for (InlineWalker walker(LineLayoutBlockFlow(this)); !walker.atEnd(); walker.advance()) {
             LayoutObject* o = walker.current().layoutObject();
 
@@ -1594,12 +1588,9 @@
                         markLinesDirtyInBlockRange(toLayoutBox(o)->logicalTop(), toLayoutBox(o)->logicalBottom());
                     }
                 } else if (isFullLayout || o->needsLayout()) {
-                    // Replaced element.
+                    // Atomic inline.
                     box->dirtyLineBoxes(isFullLayout);
-                    if (isFullLayout)
-                        replacedChildren.append(box);
-                    else
-                        o->layoutIfNeeded();
+                    o->layoutIfNeeded();
                 }
             } else if (o->isText() || (o->isLayoutInline() && !walker.atEndOfInline())) {
                 if (!o->isText())
@@ -1613,9 +1604,6 @@
                 setContainsInlineWithOutlineAndContinuation(true);
         }
 
-        for (size_t i = 0; i < replacedChildren.size(); i++)
-            replacedChildren[i]->layoutIfNeeded();
-
         layoutRunsAndFloats(layoutState);
     }
 
@@ -2020,6 +2008,24 @@
     }
 }
 
+void LayoutBlockFlow::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest)
+{
+    if (logicalTop >= logicalBottom)
+        return;
+
+    RootInlineBox* lowestDirtyLine = lastRootBox();
+    RootInlineBox* afterLowest = lowestDirtyLine;
+    while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) {
+        afterLowest = lowestDirtyLine;
+        lowestDirtyLine = lowestDirtyLine->prevRootBox();
+    }
+
+    while (afterLowest && afterLowest != highest && (afterLowest->lineBottomWithLeading() >= logicalTop || afterLowest->lineBottomWithLeading() < LayoutUnit())) {
+        afterLowest->markDirty();
+        afterLowest = afterLowest->prevRootBox();
+    }
+}
+
 LayoutUnit LayoutBlockFlow::startAlignedOffsetForLine(LayoutUnit position, IndentTextOrNot indentText)
 {
     ETextAlign textAlign = style()->textAlign();
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 7ac36217..66cb9ed 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -140,6 +140,7 @@
     unsigned m_bitfields2;
     LayoutRect rect; // Stores the previous paint invalidation rect.
     LayoutPoint position; // Stores the previous position from the paint invalidation container.
+    DisplayItemCacheGeneration cacheGeneration;
 };
 
 static_assert(sizeof(LayoutObject) == sizeof(SameSizeAsLayoutObject), "LayoutObject should stay small");
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 67de173..c6139fb 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1938,6 +1938,8 @@
     // For slimmingPaintInvalidation, this stores the previous paint offset.
     // TODO(wangxianzhu): Rename this to m_previousPaintOffset when we enable slimmingPaintInvalidation.
     LayoutPoint m_previousPositionFromPaintInvalidationBacking;
+
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
 };
 
 // FIXME: remove this once the layout object lifecycle ASSERTS are no longer hit.
diff --git a/third_party/WebKit/Source/core/layout/api/LayoutViewItem.h b/third_party/WebKit/Source/core/layout/api/LayoutViewItem.h
index 8c6c7f4..049f932 100644
--- a/third_party/WebKit/Source/core/layout/api/LayoutViewItem.h
+++ b/third_party/WebKit/Source/core/layout/api/LayoutViewItem.h
@@ -85,6 +85,29 @@
         return toView()->invalidatePaintForSelection();
     }
 
+//    bool hitTest(HitTestResult&);
+//    bool hitTestNoLifecycleUpdate(HitTestResult&);
+
+    unsigned hitTestCount() const
+    {
+        return toView()->hitTestCount();
+    }
+
+    unsigned hitTestCacheHits() const
+    {
+        return toView()->hitTestCacheHits();
+    }
+
+    void clearHitTestCache()
+    {
+        toView()->clearHitTestCache();
+    }
+
+    void invalidatePaintForViewAndCompositedLayers()
+    {
+        toView()->invalidatePaintForViewAndCompositedLayers();
+    }
+
 private:
     LayoutView* toView() { return toLayoutView(layoutObject()); }
     const LayoutView* toView() const { return toLayoutView(layoutObject()); }
diff --git a/third_party/WebKit/Source/core/layout/api/LineLayoutSVGInlineText.h b/third_party/WebKit/Source/core/layout/api/LineLayoutSVGInlineText.h
index f67f3d2..06cbd997 100644
--- a/third_party/WebKit/Source/core/layout/api/LineLayoutSVGInlineText.h
+++ b/third_party/WebKit/Source/core/layout/api/LineLayoutSVGInlineText.h
@@ -32,6 +32,11 @@
         return toSVGInlineText()->metricsList();
     }
 
+    SVGTextLayoutAttributes& layoutAttributes()
+    {
+        return *toSVGInlineText()->layoutAttributes();
+    }
+
     bool characterStartsNewTextChunk(int position) const
     {
         return toSVGInlineText()->characterStartsNewTextChunk(position);
diff --git a/third_party/WebKit/Source/core/layout/line/InlineBox.cpp b/third_party/WebKit/Source/core/layout/line/InlineBox.cpp
index 4da8c45..f9e3fe6 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineBox.cpp
+++ b/third_party/WebKit/Source/core/layout/line/InlineBox.cpp
@@ -43,6 +43,7 @@
     LayoutPoint b;
     LayoutUnit c;
     uint32_t d : 32;
+    DisplayItemCacheGeneration cacheGeneration;
 #if ENABLE(ASSERT)
     bool f;
 #endif
diff --git a/third_party/WebKit/Source/core/layout/line/InlineBox.h b/third_party/WebKit/Source/core/layout/line/InlineBox.h
index 7963a98c..56bf5edbf 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineBox.h
+++ b/third_party/WebKit/Source/core/layout/line/InlineBox.h
@@ -425,6 +425,8 @@
 private:
     InlineBoxBitfields m_bitfields;
 
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
+
 #if ENABLE(ASSERT)
     bool m_hasBadParent;
 #endif
diff --git a/third_party/WebKit/Source/core/layout/line/InlineFlowBox.cpp b/third_party/WebKit/Source/core/layout/line/InlineFlowBox.cpp
index a1430388..4a7f9c9 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineFlowBox.cpp
+++ b/third_party/WebKit/Source/core/layout/line/InlineFlowBox.cpp
@@ -1236,7 +1236,7 @@
     return result;
 }
 
-void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector<InlineBox*>& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation, void* userData) const
+void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector<InlineBox*>& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation) const
 {
     InlineBox* leaf = firstLeafChild();
 
@@ -1279,12 +1279,10 @@
                 ++it;
             }
             Vector<InlineBox*>::iterator last = it;
-            if (customReverseImplementation) {
-                ASSERT(userData);
-                (*customReverseImplementation)(userData, first, last);
-            } else {
+            if (customReverseImplementation)
+                (*customReverseImplementation)(first, last);
+            else
                 std::reverse(first, last);
-            }
         }
         ++minLevel;
     }
diff --git a/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h b/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h
index c024ac0..d1dd6e3 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h
+++ b/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h
@@ -92,8 +92,8 @@
     InlineBox* firstLeafChild() const;
     InlineBox* lastLeafChild() const;
 
-    typedef void (*CustomInlineBoxRangeReverse)(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last);
-    void collectLeafBoxesInLogicalOrder(Vector<InlineBox*>&, CustomInlineBoxRangeReverse customReverseImplementation = 0, void* userData = nullptr) const;
+    typedef void (*CustomInlineBoxRangeReverse)(Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last);
+    void collectLeafBoxesInLogicalOrder(Vector<InlineBox*>&, CustomInlineBoxRangeReverse customReverseImplementation = 0) const;
 
     void setConstructed() final
     {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp
index 723159ad..21ba55ca 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGText.cpp
@@ -184,6 +184,8 @@
 void LayoutSVGText::layout()
 {
     ASSERT(needsLayout());
+    // This flag is set and reset as needed only within this function.
+    ASSERT(!m_needsReordering);
     LayoutAnalyzer::Scope analyzer(*this);
 
     bool updateParentBoundaries = false;
@@ -193,9 +195,6 @@
         updateParentBoundaries = true;
     }
 
-    // This flag is set and reset as needed only within this function.
-    ASSERT(!m_needsReordering);
-
     // When laying out initially, build the character data map and propagate
     // resulting layout attributes to all LayoutSVGInlineText children in the
     // subtree.
@@ -256,8 +255,7 @@
     LayoutUnit paintInvalidationLogicalBottom;
     layoutInlineChildren(true, paintInvalidationLogicalTop, paintInvalidationLogicalBottom, afterEdge);
 
-    if (m_needsReordering)
-        m_needsReordering = false;
+    m_needsReordering = false;
 
     // If we don't have any line boxes, then make sure the frame rect is still cleared.
     if (!firstLineBox())
diff --git a/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.cpp b/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.cpp
index 4e1b635d..4da94e4 100644
--- a/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.cpp
@@ -276,35 +276,45 @@
     m_lineLayoutBoxes.clear();
 }
 
-bool SVGTextLayoutEngine::currentLogicalCharacterAttributes(SVGTextLayoutAttributes*& logicalAttributes)
+const SVGTextLayoutAttributes* SVGTextLayoutEngine::nextLogicalAttributes()
 {
-    if (m_layoutAttributesPosition == m_layoutAttributes.size())
-        return false;
-
-    logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
-    ASSERT(logicalAttributes);
-
-    if (m_logicalCharacterOffset != logicalAttributes->context()->textLength())
-        return true;
-
+    ASSERT(m_layoutAttributesPosition < m_layoutAttributes.size());
     ++m_layoutAttributesPosition;
     if (m_layoutAttributesPosition == m_layoutAttributes.size())
-        return false;
+        return nullptr;
 
-    logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
     m_logicalMetricsListOffset = 0;
     m_logicalCharacterOffset = 0;
-    return true;
+    return m_layoutAttributes[m_layoutAttributesPosition];
 }
 
-bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes*& logicalAttributes, SVGTextMetrics& logicalMetrics)
+const SVGTextLayoutAttributes* SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextMetrics& logicalMetrics)
 {
+    // If we're consumed all layout attributes, there can be no more metrics.
+    if (m_layoutAttributesPosition == m_layoutAttributes.size())
+        return nullptr;
+
+    const SVGTextLayoutAttributes* logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
+    // If we reached the end of the text node associated with the current set
+    // of layout attributes, try to move to the next text node/set of layout
+    // attributes.
+    ASSERT(m_logicalCharacterOffset <= logicalAttributes->context()->textLength());
+    if (m_logicalCharacterOffset == logicalAttributes->context()->textLength()) {
+        logicalAttributes = nextLogicalAttributes();
+        if (!logicalAttributes)
+            return nullptr;
+    }
+
+    // We have set of layout attributes. Find the first non-collapsed text
+    // metrics cell.
     const Vector<SVGTextMetrics>* metricsList = &logicalAttributes->context()->metricsList();
     unsigned metricsListSize = metricsList->size();
     while (true) {
+        // If we run out of metrics, move to the next set of layout attributes.
         if (m_logicalMetricsListOffset == metricsListSize) {
-            if (!currentLogicalCharacterAttributes(logicalAttributes))
-                return false;
+            logicalAttributes = nextLogicalAttributes();
+            if (!logicalAttributes)
+                return nullptr;
 
             metricsList = &logicalAttributes->context()->metricsList();
             metricsListSize = metricsList->size();
@@ -320,11 +330,11 @@
         }
 
         // Stop if we found the next valid logical text metrics object.
-        return true;
+        return logicalAttributes;
     }
 
     ASSERT_NOT_REACHED();
-    return true;
+    return nullptr;
 }
 
 void SVGTextLayoutEngine::advanceToNextLogicalCharacter(const SVGTextMetrics& logicalMetrics)
@@ -368,18 +378,14 @@
             continue;
         }
 
-        SVGTextLayoutAttributes* logicalAttributes = nullptr;
-        if (!currentLogicalCharacterAttributes(logicalAttributes))
-            break;
-
-        ASSERT(logicalAttributes);
         SVGTextMetrics logicalMetrics(SVGTextMetrics::SkippedSpaceMetrics);
-        if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics))
+        const SVGTextLayoutAttributes* logicalAttributes = currentLogicalCharacterMetrics(logicalMetrics);
+        if (!logicalAttributes)
             break;
 
-        SVGCharacterDataMap& characterDataMap = logicalAttributes->characterDataMap();
+        const SVGCharacterDataMap& characterDataMap = logicalAttributes->characterDataMap();
         SVGCharacterData data;
-        SVGCharacterDataMap::iterator it = characterDataMap.find(m_logicalCharacterOffset + 1);
+        SVGCharacterDataMap::const_iterator it = characterDataMap.find(m_logicalCharacterOffset + 1);
         if (it != characterDataMap.end())
             data = it->value;
 
diff --git a/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.h b/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.h
index cdac575..368551b92 100644
--- a/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.h
+++ b/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngine.h
@@ -71,19 +71,19 @@
     void layoutInlineTextBox(SVGInlineTextBox*);
     void layoutTextOnLineOrPath(SVGInlineTextBox*, LineLayoutSVGInlineText, const ComputedStyle&);
 
-    bool currentLogicalCharacterAttributes(SVGTextLayoutAttributes*&);
-    bool currentLogicalCharacterMetrics(SVGTextLayoutAttributes*&, SVGTextMetrics&);
+    const SVGTextLayoutAttributes* nextLogicalAttributes();
+    const SVGTextLayoutAttributes* currentLogicalCharacterMetrics(SVGTextMetrics&);
     void advanceToNextLogicalCharacter(const SVGTextMetrics&);
 
-private:
+    // Logical iteration state.
     Vector<SVGTextLayoutAttributes*>& m_layoutAttributes;
+    unsigned m_layoutAttributesPosition;
+    unsigned m_logicalCharacterOffset;
+    unsigned m_logicalMetricsListOffset;
 
     Vector<SVGInlineTextBox*> m_lineLayoutBoxes;
 
     SVGTextFragment m_currentTextFragment;
-    unsigned m_layoutAttributesPosition;
-    unsigned m_logicalCharacterOffset;
-    unsigned m_logicalMetricsListOffset;
     SVGInlineTextMetricsIterator m_visualMetricsIterator;
     FloatPoint m_textPosition;
     bool m_isVerticalText;
diff --git a/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp b/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp
index 23ee1f9..18675ed 100644
--- a/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp
@@ -27,10 +27,10 @@
 #include "core/layout/api/LineLayoutBlockFlow.h"
 #include "core/layout/api/LineLayoutSVGInlineText.h"
 #include "core/layout/svg/LayoutSVGText.h"
+#include "core/layout/svg/SVGTextLayoutEngine.h"
 #include "core/layout/svg/line/SVGInlineFlowBox.h"
 #include "core/layout/svg/line/SVGInlineTextBox.h"
 #include "core/paint/SVGRootInlineBoxPainter.h"
-#include "core/svg/SVGTextPathElement.h"
 
 namespace blink {
 
@@ -55,7 +55,7 @@
         return;
 
     if (textRoot.needsReordering())
-        reorderValueLists(layoutAttributes);
+        reorderValueLists();
 
     // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
     SVGTextLayoutEngine characterLayout(layoutAttributes);
@@ -153,83 +153,52 @@
     return closestLeaf ? closestLeaf : lastLeaf;
 }
 
-static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes* firstAttributes, SVGTextLayoutAttributes* lastAttributes, unsigned firstPosition, unsigned lastPosition)
+static inline void swapPositioningValuesInTextBoxes(SVGInlineTextBox* firstTextBox, SVGInlineTextBox* lastTextBox)
 {
-    SVGCharacterDataMap::iterator itFirst = firstAttributes->characterDataMap().find(firstPosition + 1);
-    SVGCharacterDataMap::iterator itLast = lastAttributes->characterDataMap().find(lastPosition + 1);
-    bool firstPresent = itFirst != firstAttributes->characterDataMap().end();
-    bool lastPresent = itLast != lastAttributes->characterDataMap().end();
+    LineLayoutSVGInlineText firstTextNode = LineLayoutSVGInlineText(firstTextBox->getLineLayoutItem());
+    SVGCharacterDataMap& firstCharacterDataMap = firstTextNode.layoutAttributes().characterDataMap();
+    SVGCharacterDataMap::iterator itFirst = firstCharacterDataMap.find(firstTextBox->start() + 1);
+    if (itFirst == firstCharacterDataMap.end())
+        return;
+    LineLayoutSVGInlineText lastTextNode = LineLayoutSVGInlineText(lastTextBox->getLineLayoutItem());
+    SVGCharacterDataMap& lastCharacterDataMap = lastTextNode.layoutAttributes().characterDataMap();
+    SVGCharacterDataMap::iterator itLast = lastCharacterDataMap.find(lastTextBox->start() + 1);
+    if (itLast == lastCharacterDataMap.end())
+        return;
     // We only want to perform the swap if both inline boxes are absolutely
     // positioned.
-    if (!firstPresent || !lastPresent)
-        return;
     std::swap(itFirst->value, itLast->value);
 }
 
-static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes*>& attributes, LineLayoutSVGInlineText firstContext, LineLayoutSVGInlineText lastContext, SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last)
+static inline void reverseInlineBoxRangeAndValueListsIfNeeded(Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
 {
-    first = 0;
-    last = 0;
-
-    unsigned attributesSize = attributes.size();
-    for (unsigned i = 0; i < attributesSize; ++i) {
-        SVGTextLayoutAttributes* current = attributes[i];
-        if (!first && firstContext.isEqual(current->context()))
-            first = current;
-        if (!last && lastContext.isEqual(current->context()))
-            last = current;
-        if (first && last)
-            break;
-    }
-
-    ASSERT(first);
-    ASSERT(last);
-}
-
-static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
-{
-    ASSERT(userData);
-    Vector<SVGTextLayoutAttributes*>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes*>*>(userData);
-
-    // This is a copy of std::reverse(first, last). It additionally assures that the metrics map within the layoutObjects belonging to the InlineBoxes are reordered as well.
+    // This is a copy of std::reverse(first, last). It additionally assures
+    // that the metrics map within the layoutObjects belonging to the
+    // InlineBoxes are reordered as well.
     while (true)  {
         if (first == last || first == --last)
             return;
 
-        if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
-            InlineBox* temp = *first;
-            *first = *last;
-            *last = temp;
-            ++first;
-            continue;
-        }
+        if ((*last)->isSVGInlineTextBox() && (*first)->isSVGInlineTextBox()) {
+            SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first);
+            SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last);
 
-        SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first);
-        SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last);
-
-        // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
-        if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
-            LineLayoutSVGInlineText firstContext = LineLayoutSVGInlineText(firstTextBox->getLineLayoutItem());
-            LineLayoutSVGInlineText lastContext = LineLayoutSVGInlineText(lastTextBox->getLineLayoutItem());
-
-            SVGTextLayoutAttributes* firstAttributes = nullptr;
-            SVGTextLayoutAttributes* lastAttributes = nullptr;
-            findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes);
-            swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start());
+            // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
+            if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len())
+                swapPositioningValuesInTextBoxes(firstTextBox, lastTextBox);
         }
 
         InlineBox* temp = *first;
         *first = *last;
         *last = temp;
-
         ++first;
     }
 }
 
-void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes*>& attributes)
+void SVGRootInlineBox::reorderValueLists()
 {
     Vector<InlineBox*> leafBoxesInLogicalOrder;
-    collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes);
+    collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded);
 }
 
 bool SVGRootInlineBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
diff --git a/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.h b/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.h
index 1b73d0a..338ff7c 100644
--- a/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.h
+++ b/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.h
@@ -25,7 +25,6 @@
 
 #include "core/layout/api/LineLayoutBlockFlow.h"
 #include "core/layout/line/RootInlineBox.h"
-#include "core/layout/svg/SVGTextLayoutEngine.h"
 
 namespace blink {
 
@@ -53,7 +52,7 @@
     bool nodeAtPoint(HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) final;
 
 private:
-    void reorderValueLists(Vector<SVGTextLayoutAttributes*>&);
+    void reorderValueLists();
     void layoutChildBoxes(InlineFlowBox*, LayoutRect* = nullptr);
     void layoutRootBox(const LayoutRect&);
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index fe77281..fd6d49a 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -113,6 +113,7 @@
         void* pointer;
         LayoutRect rect;
     } previousPaintStatus;
+    DisplayItemCacheGeneration cacheGeneration;
 };
 
 static_assert(sizeof(PaintLayer) == sizeof(SameSizeAsPaintLayer), "PaintLayer should stay small");
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index 59682ee..8257e88 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -885,6 +885,8 @@
     LayoutRect m_previousPaintDirtyRect;
 
     OwnPtr<PaintLayerRareData> m_rareData;
+
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/testing/Internals.cpp b/third_party/WebKit/Source/core/testing/Internals.cpp
index a229bf01..be9f12d 100644
--- a/third_party/WebKit/Source/core/testing/Internals.cpp
+++ b/third_party/WebKit/Source/core/testing/Internals.cpp
@@ -100,8 +100,8 @@
 #include "core/layout/LayoutMenuList.h"
 #include "core/layout/LayoutObject.h"
 #include "core/layout/LayoutTreeAsText.h"
-#include "core/layout/LayoutView.h"
 #include "core/layout/api/LayoutMenuListItem.h"
+#include "core/layout/api/LayoutViewItem.h"
 #include "core/layout/compositing/CompositedLayerMapping.h"
 #include "core/layout/compositing/PaintLayerCompositor.h"
 #include "core/loader/DocumentLoader.h"
@@ -140,7 +140,6 @@
 #include "platform/weborigin/SchemeRegistry.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebConnectionType.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "public/platform/WebLayer.h"
 #include "wtf/InstanceCounter.h"
@@ -338,7 +337,7 @@
         return 0;
     }
 
-    return doc->layoutView()->hitTestCount();
+    return doc->layoutViewItem().hitTestCount();
 }
 
 unsigned Internals::hitTestCacheHits(Document* doc, ExceptionState& exceptionState) const
@@ -348,7 +347,7 @@
         return 0;
     }
 
-    return doc->layoutView()->hitTestCacheHits();
+    return doc->layoutViewItem().hitTestCacheHits();
 }
 
 Element* Internals::elementFromPoint(Document* doc, double x, double y, bool ignoreClipping, bool allowChildFrameContent, ExceptionState& exceptionState) const
@@ -358,7 +357,7 @@
         return 0;
     }
 
-    if (!doc->layoutView())
+    if (doc->layoutViewItem().isNull())
         return 0;
 
     HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active;
@@ -379,10 +378,10 @@
         return;
     }
 
-    if (!doc->layoutView())
+    if (doc->layoutViewItem().isNull())
         return;
 
-    doc->layoutView()->clearHitTestCache();
+    doc->layoutViewItem().clearHitTestCache();
 }
 
 bool Internals::isPreloaded(const String& url)
@@ -1438,8 +1437,9 @@
     if (ScrollingCoordinator* scrollingCoordinator = document->page()->scrollingCoordinator())
         scrollingCoordinator->updateAfterCompositingChangeIfNeeded();
 
-    if (LayoutView* view = document->layoutView()) {
-        if (PaintLayerCompositor* compositor = view->compositor()) {
+    LayoutViewItem view = document->layoutViewItem();
+    if (!view.isNull()) {
+        if (PaintLayerCompositor* compositor = view.compositor()) {
             if (GraphicsLayer* rootLayer = compositor->rootGraphicsLayer()) {
                 LayerRectList* rects = LayerRectList::create();
                 accumulateLayerRectList(compositor, rootLayer, rects);
@@ -1502,9 +1502,9 @@
 
     LocalFrame* frame = document->frame();
     FrameView* frameView = document->view();
-    LayoutView* layoutView = document->layoutView();
+    LayoutViewItem layoutViewItem = document->layoutViewItem();
 
-    if (!layoutView)
+    if (layoutViewItem.isNull())
         return nullptr;
 
     float zoomFactor = frame->pageZoomFactor();
@@ -1524,7 +1524,7 @@
 
     HeapVector<Member<Node>> matches;
     HitTestResult result(request, point, topPadding, rightPadding, bottomPadding, leftPadding);
-    layoutView->hitTest(result);
+    layoutViewItem.hitTest(result);
     copyToVector(result.listBasedTestResult(), matches);
 
     return StaticNodeList::adopt(matches);
@@ -1999,8 +1999,9 @@
         return;
     }
 
-    if (LayoutView *layoutView = document->layoutView())
-        layoutView->invalidatePaintForViewAndCompositedLayers();
+    LayoutViewItem layoutViewItem = document->layoutViewItem();
+    if (!layoutViewItem.isNull())
+        layoutViewItem.invalidatePaintForViewAndCompositedLayers();
 }
 
 void Internals::startTrackingPaintInvalidationObjects()
@@ -2251,7 +2252,7 @@
 void Internals::forceCompositingUpdate(Document* document, ExceptionState& exceptionState)
 {
     ASSERT(document);
-    if (!document->layoutView()) {
+    if (document->layoutViewItem().isNull()) {
         exceptionState.throwDOMException(InvalidAccessError, "The document provided is invalid.");
         return;
     }
diff --git a/third_party/WebKit/Source/devtools/devtools.gypi b/third_party/WebKit/Source/devtools/devtools.gypi
index 396d7d5..5cc199e6 100644
--- a/third_party/WebKit/Source/devtools/devtools.gypi
+++ b/third_party/WebKit/Source/devtools/devtools.gypi
@@ -591,11 +591,13 @@
             'front_end/profiler/TopDownProfileDataGrid.js',
         ],
         'devtools_resources_js_files': [
+            'front_end/resources/appManifestView.css',
             'front_end/resources/indexedDBViews.css',
             'front_end/resources/resourcesPanel.css',
             'front_end/resources/resourcesSidebar.css',
             'front_end/resources/serviceWorkerCacheViews.css',
             'front_end/resources/serviceWorkersView.css',
+            'front_end/resources/AppManifestView.js',
             'front_end/resources/ApplicationCacheItemsView.js',
             'front_end/resources/CookieItemsView.js',
             'front_end/resources/DatabaseModel.js',
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/AppManifestView.js b/third_party/WebKit/Source/devtools/front_end/resources/AppManifestView.js
new file mode 100644
index 0000000..773a775a
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/resources/AppManifestView.js
@@ -0,0 +1,152 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @implements {WebInspector.TargetManager.Observer}
+ */
+WebInspector.AppManifestView = function()
+{
+    WebInspector.VBox.call(this, true);
+    this.registerRequiredCSS("resources/appManifestView.css");
+
+    this._contentBox = this.contentElement.createChild("div", "app-content-box");
+    this._contentBox.createChild("div", "app-manifest-title").textContent = WebInspector.UIString("App Manifest");
+    this._urlElement = this._contentBox.createChild("div", "app-manifest-url link");
+    this._identitySection = this._createSection(WebInspector.UIString("Identity"));
+    this._presentationSection = this._createSection(WebInspector.UIString("Presentation"));
+    var iconsSection = this._createSection(WebInspector.UIString("Icons"));
+    this._iconsList = iconsSection.createChild("div", "app-manifest-icons");
+    this._errorsSection = this._createSection(WebInspector.UIString("Errors and warnings"));
+    this._errorsList = this._errorsSection.createChild("div", "app-manifest-errors");
+
+    this._nameField = this._createField(this._identitySection, WebInspector.UIString("Name"));
+    this._shortNameField = this._createField(this._identitySection, WebInspector.UIString("Short name"));
+
+    this._startURLField = this._createField(this._presentationSection, WebInspector.UIString("Start URL"));
+
+    var themeColorField = this._createField(this._presentationSection, WebInspector.UIString("Theme color"));
+    this._themeColorSwatch = WebInspector.ColorSwatch.create();
+    themeColorField.appendChild(this._themeColorSwatch);
+
+    var backgroundColorField = this._createField(this._presentationSection, WebInspector.UIString("Background color"));
+    this._backgroundColorSwatch = WebInspector.ColorSwatch.create();
+    backgroundColorField.appendChild(this._backgroundColorSwatch);
+
+    this._orientationField = this._createField(this._presentationSection, WebInspector.UIString("Orientation"));
+    this._displayField = this._createField(this._presentationSection, WebInspector.UIString("Display"));
+
+    WebInspector.targetManager.observeTargets(this);
+}
+
+WebInspector.AppManifestView.prototype = {
+    /**
+     * @param {string} title
+     * @return {!Element}
+     */
+    _createSection: function(title)
+    {
+        var section = this._contentBox.createChild("div", "app-manifest-section");
+        section.createChild("div", "app-manifest-section-title").textContent = title;
+        return section;
+    },
+
+    /**
+     * @param {string} title
+     * @param {!Element} section
+     * @return {!Element}
+     */
+    _createField: function(section, title)
+    {
+        var row = section.createChild("div", "app-manifest-field");
+        row.createChild("div", "app-manifest-field-name").textContent = title;
+        return row.createChild("div", "app-manifest-field-value");
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.Target} target
+     */
+    targetAdded: function(target)
+    {
+        if (this._target)
+            return;
+        this._target = target;
+
+        this._updateManifest();
+        WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.MainFrameNavigated, this._updateManifest, this);
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.Target} target
+     */
+    targetRemoved: function(target)
+    {
+    },
+
+    _updateManifest: function()
+    {
+        this._target.resourceTreeModel.fetchAppManifest(this._renderManifest.bind(this));
+    },
+
+    /**
+     * @param {string} url
+     * @param {?string} data
+     * @param {!Array<!PageAgent.AppManifestError>} errors
+     */
+    _renderManifest: function(url, data, errors)
+    {
+        this._urlElement.removeChildren();
+        if (url)
+            this._urlElement.appendChild(WebInspector.linkifyResourceAsNode(url, undefined, undefined, undefined, undefined, url));
+        this._errorsList.removeChildren();
+        this._errorsSection.classList.toggle("hidden", !errors.length);
+        for (var error of errors)
+            this._errorsList.appendChild(createLabel(error.message, error.critical ? "error-icon" : "warning-icon"));
+
+        if (!data)
+            data = "{}";
+
+        var parsedManifest = JSON.parse(data);
+        this._nameField.textContent = stringProperty("name");
+        this._shortNameField.textContent = stringProperty("short_name");
+        this._startURLField.removeChildren();
+        var startURL = stringProperty("start_url");
+        if (startURL)
+            this._startURLField.appendChild(WebInspector.linkifyResourceAsNode(/** @type {string} */(WebInspector.ParsedURL.completeURL(url, startURL)), undefined, undefined, undefined, undefined, startURL));
+
+        this._themeColorSwatch.classList.toggle("hidden", !stringProperty("theme_color"));
+        this._themeColorSwatch.setColorText(stringProperty("theme_color") || "white");
+        this._backgroundColorSwatch.classList.toggle("hidden", !stringProperty("background_color"));
+        this._backgroundColorSwatch.setColorText(stringProperty("background_color") || "white");
+
+        this._orientationField.textContent = stringProperty("orientation");
+        this._displayField.textContent = stringProperty("display");
+
+        var icons = parsedManifest["icons"] || [];
+        this._iconsList.removeChildren();
+        for (var icon of icons) {
+            var title = (icon["sizes"] || "") + "\n" + (icon["type"] || "");
+            var field = this._createField(this._iconsList, title);
+            var imageElement = field.createChild("img");
+            imageElement.src = WebInspector.ParsedURL.completeURL(url, icon["src"]);
+        }
+
+        /**
+         * @param {string} name
+         * @return {string}
+         */
+        function stringProperty(name)
+        {
+            var value = parsedManifest[name];
+            if (typeof value !== "string")
+                return "";
+            return value;
+        }
+    },
+
+    __proto__: WebInspector.VBox.prototype
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js
index 186c070..0bd9dc5d 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js
@@ -47,44 +47,29 @@
     this.panelSidebarElement().appendChild(this._sidebarTree.element);
     this.setDefaultFocusedElement(this._sidebarTree.element);
 
-    var storageTreeElement = new TreeElement(WebInspector.UIString("Storage"), true);
-    storageTreeElement.listItemElement.classList.add("storage-group-list-item");
-    storageTreeElement.setCollapsible(false);
-    storageTreeElement.selectable = false;
-    this._sidebarTree.appendChild(storageTreeElement);
+    this._applicationTreeElement = this._addSidebarSection(WebInspector.UIString("Application"));
+    var manifestTreeElement = new WebInspector.AppManifestTreeElement(this);
+    this._applicationTreeElement.appendChild(manifestTreeElement);
 
+    var storageTreeElement = this._addSidebarSection(WebInspector.UIString("Storage"));
     this.localStorageListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Local Storage"), "LocalStorage", ["domstorage-storage-tree-item", "local-storage"]);
     storageTreeElement.appendChild(this.localStorageListTreeElement);
-
     this.sessionStorageListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Session Storage"), "SessionStorage", ["domstorage-storage-tree-item", "session-storage"]);
     storageTreeElement.appendChild(this.sessionStorageListTreeElement);
-
     this.indexedDBListTreeElement = new WebInspector.IndexedDBTreeElement(this);
     storageTreeElement.appendChild(this.indexedDBListTreeElement);
-
     this.databasesListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Web SQL"), "Databases", ["database-storage-tree-item"]);
     storageTreeElement.appendChild(this.databasesListTreeElement);
-
     this.cookieListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Cookies"), "Cookies", ["cookie-storage-tree-item"]);
     storageTreeElement.appendChild(this.cookieListTreeElement);
 
-    var cacheTreeElement = new TreeElement(WebInspector.UIString("Cache"), true);
-    cacheTreeElement.listItemElement.classList.add("storage-group-list-item");
-    cacheTreeElement.setCollapsible(false);
-    cacheTreeElement.selectable = false;
-    this._sidebarTree.appendChild(cacheTreeElement);
-
+    var cacheTreeElement = this._addSidebarSection(WebInspector.UIString("Cache"));
     this.cacheStorageListTreeElement = new WebInspector.ServiceWorkerCacheTreeElement(this);
     cacheTreeElement.appendChild(this.cacheStorageListTreeElement);
-
     this.applicationCacheListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Application Cache"), "ApplicationCache", ["application-cache-storage-tree-item"]);
     cacheTreeElement.appendChild(this.applicationCacheListTreeElement);
 
-    this.resourcesListTreeElement = new TreeElement(WebInspector.UIString("Frames"), true);
-    this.resourcesListTreeElement.listItemElement.classList.add("storage-group-list-item");
-    this.resourcesListTreeElement.setCollapsible(false);
-    this.resourcesListTreeElement.selectable = false;
-    this._sidebarTree.appendChild(this.resourcesListTreeElement);
+    this.resourcesListTreeElement = this._addSidebarSection(WebInspector.UIString("Frames"));
 
     var mainContainer = new WebInspector.VBox();
     this.storageViews = mainContainer.element.createChild("div", "vbox flex-auto");
@@ -113,6 +98,20 @@
 }
 
 WebInspector.ResourcesPanel.prototype = {
+    /**
+     * @param {string} title
+     * @return {!TreeElement}
+     */
+    _addSidebarSection: function(title)
+    {
+        var treeElement = new TreeElement(title, true);
+        treeElement.listItemElement.classList.add("storage-group-list-item");
+        treeElement.setCollapsible(false);
+        treeElement.selectable = false;
+        this._sidebarTree.appendChild(treeElement);
+        return treeElement;
+    },
+
     wasShown: function()
     {
         if (!this._sidebarTree.selectedTreeElement) {
@@ -135,7 +134,7 @@
 
         if (target.serviceWorkerManager) {
             this.serviceWorkersTreeElement = new WebInspector.ServiceWorkersTreeElement(this);
-            this._sidebarTree.insertChild(this.serviceWorkersTreeElement, 0);
+            this._applicationTreeElement.insertChild(this.serviceWorkersTreeElement, 0);
         }
 
         this._databaseModel = WebInspector.DatabaseModel.fromTarget(target);
@@ -244,8 +243,12 @@
         this.cookieListTreeElement.removeChildren();
         this.cacheStorageListTreeElement.removeChildren();
 
-        if (this.visibleView && !(this.visibleView instanceof WebInspector.StorageCategoryView) && !(this.visibleView instanceof WebInspector.ServiceWorkersView))
+        if (this.visibleView && !(this.visibleView instanceof WebInspector.StorageCategoryView)
+            && !(this.visibleView instanceof WebInspector.ServiceWorkersView)
+            && !(this.visibleView instanceof WebInspector.AppManifestView)) {
             this.visibleView.detach();
+            delete this.visibleView;
+        }
 
         this._storageViewToolbar.removeToolbarItems();
 
@@ -546,30 +549,6 @@
     },
 
     /**
-     * @param {!WebInspector.Widget} view
-     */
-    showIndexedDB: function(view)
-    {
-        this._innerShowView(view);
-    },
-
-    /**
-     * @param {!WebInspector.Widget} view
-     */
-    showServiceWorkerCache: function(view)
-    {
-        this._innerShowView(view);
-    },
-
-    /**
-     * @param {!WebInspector.Widget} view
-     */
-     showServiceWorkersView: function(view)
-    {
-        this._innerShowView(view);
-    },
-
-    /**
      * @param {!WebInspector.DOMStorage} domStorage
      */
     _showDOMStorage: function(domStorage)
@@ -1430,7 +1409,7 @@
         if (!this._view)
             this._view = new WebInspector.ServiceWorkerCacheView(this._model, this._cache);
 
-        this._storagePanel.showServiceWorkerCache(this._view);
+        this._storagePanel._innerShowView(this._view);
         return false;
     },
 
@@ -1472,13 +1451,47 @@
         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
         if (!this._view)
             this._view = new WebInspector.ServiceWorkersView();
-        this._storagePanel.showServiceWorkersView(this._view);
+        this._storagePanel._innerShowView(this._view);
         return false;
     },
 
     __proto__: WebInspector.BaseStorageTreeElement.prototype
 }
 
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ * @param {!WebInspector.ResourcesPanel} storagePanel
+ */
+WebInspector.AppManifestTreeElement = function(storagePanel)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, WebInspector.UIString("Manifest"), [], false);
+}
+
+WebInspector.AppManifestTreeElement.prototype = {
+    /**
+     * @return {string}
+     */
+    get itemURL()
+    {
+        return "manifest://";
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        if (!this._view)
+            this._view = new WebInspector.AppManifestView();
+        this._storagePanel._innerShowView(this._view);
+        return false;
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
 
 /**
  * @constructor
@@ -1686,7 +1699,7 @@
         if (!this._view)
             this._view = new WebInspector.IDBDatabaseView(this._database);
 
-        this._storagePanel.showIndexedDB(this._view);
+        this._storagePanel._innerShowView(this._view);
         return false;
     },
 
@@ -1814,7 +1827,7 @@
         if (!this._view)
             this._view = new WebInspector.IDBDataView(this._model, this._databaseId, this._objectStore, null);
 
-        this._storagePanel.showIndexedDB(this._view);
+        this._storagePanel._innerShowView(this._view);
         return false;
     },
 
@@ -1899,7 +1912,7 @@
         if (!this._view)
             this._view = new WebInspector.IDBDataView(this._model, this._databaseId, this._objectStore, this._index);
 
-        this._storagePanel.showIndexedDB(this._view);
+        this._storagePanel._innerShowView(this._view);
         return false;
     },
 
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/appManifestView.css b/third_party/WebKit/Source/devtools/front_end/resources/appManifestView.css
new file mode 100644
index 0000000..03ea63f
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/resources/appManifestView.css
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 The Chromium 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 {
+    background-color: #f9f9f9;
+}
+
+.app-content-box {
+    background-color: white;
+    white-space: nowrap;
+    overflow: auto;
+}
+
+.app-manifest-title {
+    font-size: 14px;
+    padding: 12px 24px;
+}
+
+.app-manifest-url {
+    font-size: 12px;
+    padding: 0 24px 12px;
+    border-bottom: 1px solid rgb(230, 230, 230);
+}
+
+.app-manifest-section {
+    display: flex;
+    padding: 12px 6px;
+    border-bottom: 1px solid rgb(230, 230, 230);
+    flex-direction: column;
+}
+
+.app-manifest-section-title {
+    margin-bottom: 10px;
+    padding-left: 18px;
+}
+
+.app-manifest-field {
+    display: flex;
+    margin-top: 1px;
+    margin-bottom: 10px;
+}
+
+.app-manifest-field-name {
+    color: #888;
+    flex: 0 0 128px;
+    text-align: right;
+    padding: 0 6px;
+    white-space: pre;
+}
+
+.app-manifest-field-value {
+    flex: auto;
+    padding: 0 6px;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.app-manifest-field-value .color-swatch {
+    width: 16px;
+    height: 16px;
+    display: block;
+}
+
+.app-manifest-errors {
+    padding-left: 12px;
+    display: flex;
+    flex-direction: column;
+}
+
+.app-manifest-errors label {
+    padding: 4px;
+}
+
+.app-manifest-icons {
+    display: flex;
+    flex-direction: column;
+}
+
+.app-manifest-icons > img {
+    flex: none;
+    max-width: 200px;
+    max-height: 200px;
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/module.json b/third_party/WebKit/Source/devtools/front_end/resources/module.json
index ecd13d5..82b2209f1 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/resources/module.json
@@ -15,6 +15,7 @@
     ],
     "dependencies": ["source_frame", "ui_lazy", "components_lazy"],
     "scripts": [
+        "AppManifestView.js",
         "ApplicationCacheItemsView.js",
         "CookieItemsView.js",
         "DatabaseModel.js",
@@ -29,6 +30,7 @@
         "ServiceWorkersView.js"
     ],
     "resources": [
+        "appManifestView.css",
         "indexedDBViews.css",
         "resourcesPanel.css",
         "resourcesSidebar.css",
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css b/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css
index d65a4f0c..f16606d 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css
+++ b/third_party/WebKit/Source/devtools/front_end/resources/resourcesSidebar.css
@@ -118,10 +118,6 @@
     content: url(Images/serviceWorker.svg);
 }
 
-li.service-workers-tree-item {
-    padding: 16px 0;
-}
-
 :focus .service-workers-tree-item.selected .icon {
     -webkit-filter: invert();
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
index d6f98629..0539a60 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
@@ -461,6 +461,28 @@
         this._agent.reload(bypassCache, scriptToEvaluateOnLoad);
     },
 
+    /**
+     * @param {function(string, ?string,!Array<!PageAgent.AppManifestError>)} callback
+     */
+    fetchAppManifest: function(callback)
+    {
+        this._agent.getAppManifest(myCallback);
+        /**
+         * @param {?Protocol.Error} protocolError
+         * @param {string} url
+         * @param {!Array<!PageAgent.AppManifestError>} errors
+         * @param {string=} data
+         */
+        function myCallback(protocolError, url, errors, data)
+        {
+            if (protocolError) {
+                callback(url, null, []);
+                return;
+            }
+            callback(url, data || null, errors);
+        }
+    },
+
     __proto__: WebInspector.SDKModel.prototype
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
index 82dbe3b..88ac526 100644
--- a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
@@ -741,14 +741,14 @@
     this.registerRequiredCSS("security/originView.css");
     this.registerRequiredCSS("security/lockIcon.css");
 
-    var titleSection = this.element.createChild("div", "origin-view-section title-section");
+    var titleSection = this.element.createChild("div", "title-section");
     titleSection.createChild("div", "origin-view-title").textContent = WebInspector.UIString("Origin");
     var originDisplay = titleSection.createChild("div", "origin-display");
     this._originLockIcon = originDisplay.createChild("span", "security-property");
     this._originLockIcon.classList.add("security-property-" + originState.securityState);
     // TODO(lgarron): Highlight the origin scheme. https://crbug.com/523589
     originDisplay.createChild("span", "origin").textContent = origin;
-    var originNetworkLink = originDisplay.createChild("div", "link");
+    var originNetworkLink = titleSection.createChild("div", "link");
     originNetworkLink.textContent = WebInspector.UIString("View requests in Network Panel");
     function showOriginRequestsInNetworkPanel()
     {
diff --git a/third_party/WebKit/Source/devtools/front_end/security/originView.css b/third_party/WebKit/Source/devtools/front_end/security/originView.css
index 539ebf0..ba8273ef8 100644
--- a/third_party/WebKit/Source/devtools/front_end/security/originView.css
+++ b/third_party/WebKit/Source/devtools/front_end/security/originView.css
@@ -3,6 +3,11 @@
  * found in the LICENSE file.
  */
 
+.title-section {
+    padding-bottom: 10px;
+    border-bottom: 1px solid rgb(230, 230, 230);
+}
+
 .security-origin-view {
     overflow-x: hidden;
     overflow-y: scroll;
@@ -11,16 +16,18 @@
 }
 
 .security-origin-view .origin-view-section {
-    padding: 1.5em 1.5em 1.5em;
     border-bottom: 1px solid rgb(230, 230, 230);
-}
-
-.security-origin-view .title-section {
-    padding-bottom: 1.5em;
+    padding: 12px 6px;
 }
 
 .security-origin-view .origin-display {
-  padding-left: 18px;
+    padding-left: 38px;
+    display: flex;
+    align-items: center;
+}
+
+.title-section > .link {
+    padding: 6px 0 0 39px
 }
 
 .security-origin-view .origin-display .security-property {
@@ -31,22 +38,17 @@
 }
 
 .security-origin-view .origin-view-title {
-    font-size: 1.25em;
-    margin-top: 0.5em;
-    margin-bottom: 0.25em;
+    font-size: 15px;
+    padding: 12px 24px;
 }
 
-.security-origin-view .origin-view-section-title {
-    font-weight: bold;
-    font-size: 1em;
-    margin-bottom: 0.25em;
-}
+.security-origin-view .origin-view-section-title {margin-bottom: 10px;padding-left: 18px;}
 
 .security-origin-view .details-table-row {
     display: flex;
     white-space: nowrap;
     overflow: hidden;
-    margin-top: 6px;
+    line-height: 22px;
 }
 
 .security-origin-view .details-table-row > div {
@@ -55,7 +57,7 @@
 
 .security-origin-view .details-table-row > div:first-child {
     color: rgb(140, 140, 140);
-    width: 7em;
+    width: 128px;
     margin-right: 1em;
     flex: none;
     display: flex;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
index fd66994..60c298b 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
@@ -1254,6 +1254,19 @@
 }
 
 /**
+ * @param {string} title
+ * @param {string} iconClass
+ * @return {!Element}
+ */
+function createLabel(title, iconClass)
+{
+    var element = createElement("label", "dt-icon-label");
+    element.createChild("span").textContent = title;
+    element.type = iconClass;
+    return element;
+}
+
+/**
  * @param {string=} title
  * @param {boolean=} checked
  * @param {string=} subtitle
diff --git a/third_party/WebKit/Source/devtools/protocol.json b/third_party/WebKit/Source/devtools/protocol.json
index 6d3e88e5..5a14e031 100644
--- a/third_party/WebKit/Source/devtools/protocol.json
+++ b/third_party/WebKit/Source/devtools/protocol.json
@@ -143,7 +143,7 @@
             {
                 "id": "ScreencastFrameMetadata",
                 "type": "object",
-                "description": "Screencast frame metadata",
+                "description": "Screencast frame metadata.",
                 "properties": [
                     { "name": "offsetTop", "type": "number", "hidden": true, "description": "Top offset in DIP." },
                     { "name": "pageScaleFactor", "type": "number", "hidden": true, "description": "Page scale factor." },
@@ -157,10 +157,22 @@
             },
             {
                 "id": "DialogType",
-                "description": "Javascript dialog type",
+                "description": "Javascript dialog type.",
                 "type": "string",
                 "enum": ["alert", "confirm", "prompt", "beforeunload"],
                 "hidden": true
+            },
+            {
+                "id": "AppManifestError",
+                "description": "Error while paring app manifest.",
+                "type": "object",
+                "properties": [
+                  { "name": "message", "type": "string", "description": "Error message." },
+                  { "name": "critical", "type": "integer", "description": "If criticial, this is a non-recoverable parse error." },
+                  { "name": "line", "type": "integer", "description": "Error line." },
+                  { "name": "column", "type": "integer", "description": "Error column." }
+                ],
+                "hidden": true
             }
         ],
         "commands": [
@@ -448,6 +460,16 @@
                 "description": "Sets overlay message."
             },
             {
+                "name": "getAppManifest",
+                "hidden": true,
+                "returns": [
+                    { "name": "url", "type": "string", "description": "Manifest location." },
+                    { "name": "errors", "type": "array", "items": { "$ref": "AppManifestError" } },
+                    { "name": "data", "type": "string", "optional": true, "description": "Manifest content." }
+                ],
+                "handlers": ["none"]
+            },
+            {
                 "name": "requestAppBanner",
                 "hidden": true,
                 "handlers": ["browser"]
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
index 5e17912..df035cca 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -1417,7 +1417,7 @@
     Node* node = hitTestResult.innerNode();
 
     // MediaDocument has a special shadow root for displaying the save button.
-    bool allowNodeInShadowTree = node->ownerDocument()->isMediaDocument() && RuntimeEnabledFeatures::mediaDocumentDownloadButtonEnabled();
+    bool allowNodeInShadowTree = node->document().isMediaDocument() && RuntimeEnabledFeatures::mediaDocumentDownloadButtonEnabled();
 
     // Allow the hit test to return media control buttons.
     if (node->isInShadowTree() && (!isHTMLInputElement(*node) || !node->isMediaControlElement()) && !allowNodeInShadowTree)
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp b/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
index fbeffbb..4115732 100644
--- a/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
+++ b/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
@@ -27,6 +27,7 @@
 #include "public/platform/WebVector.h"
 #include "wtf/Vector.h"
 #include "wtf/text/WTFString.h"
+#include <algorithm>
 
 namespace blink {
 
@@ -79,6 +80,11 @@
     return result;
 }
 
+static bool AreCodecsSpecified(const WebMediaKeySystemMediaCapability& capability)
+{
+    return !capability.codecs.isEmpty();
+}
+
 // This class allows capabilities to be checked and a MediaKeySystemAccess
 // object to be created asynchronously.
 class MediaKeySystemAccessInitializer final : public EncryptedMediaRequest {
@@ -111,6 +117,12 @@
     // See http://crbug.com/482277
     void checkVideoCapabilityRobustness() const;
 
+    // Generate deprecation warning and log UseCounter if configuration
+    // contains only container-only contentType strings.
+    // TODO(jrummell): Remove once this is no longer allowed.
+    // See http://crbug.com/605661.
+    void checkEmptyCodecs(const WebMediaKeySystemConfiguration&);
+
     Member<ScriptPromiseResolver> m_resolver;
     const String m_keySystem;
     WebVector<WebMediaKeySystemConfiguration> m_supportedConfigurations;
@@ -154,6 +166,8 @@
 
 void MediaKeySystemAccessInitializer::requestSucceeded(WebContentDecryptionModuleAccess* access)
 {
+    checkEmptyCodecs(access->getConfiguration());
+
     m_resolver->resolve(new MediaKeySystemAccess(m_keySystem, adoptPtr(access)));
     m_resolver.clear();
 }
@@ -202,6 +216,33 @@
     }
 }
 
+void MediaKeySystemAccessInitializer::checkEmptyCodecs(const WebMediaKeySystemConfiguration& config)
+{
+    // This is only checking for empty codecs in the selected configuration,
+    // as apps may pass container only contentType strings for compatibility
+    // with other implementations.
+    // This will only check that all returned capabilities do not contain
+    // codecs. This avoids alerting on configurations that will continue
+    // to succeed in the future once strict checking is enforced.
+    bool areAllAudioCodecsEmpty = false;
+    if (config.hasAudioCapabilities && !config.audioCapabilities.isEmpty()) {
+        areAllAudioCodecsEmpty = std::find_if(config.audioCapabilities.begin(), config.audioCapabilities.end(), AreCodecsSpecified)
+            == config.audioCapabilities.end();
+    }
+
+    bool areAllVideoCodecsEmpty = false;
+    if (config.hasVideoCapabilities && !config.videoCapabilities.isEmpty()) {
+        areAllVideoCodecsEmpty = std::find_if(config.videoCapabilities.begin(), config.videoCapabilities.end(), AreCodecsSpecified)
+            == config.videoCapabilities.end();
+    }
+
+    if (areAllAudioCodecsEmpty || areAllVideoCodecsEmpty) {
+        Deprecation::countDeprecation(m_resolver->getExecutionContext(), UseCounter::EncryptedMediaAllSelectedContentTypesMissingCodecs);
+    } else {
+        UseCounter::count(m_resolver->getExecutionContext(), UseCounter::EncryptedMediaAllSelectedContentTypesHaveCodecs);
+    }
+}
+
 } // namespace
 
 ScriptPromise NavigatorRequestMediaKeySystemAccess::requestMediaKeySystemAccess(
diff --git a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
index 0cf2048..401e199 100644
--- a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "core/dom/ExceptionCode.h"
 #include "modules/webaudio/AudioBasicProcessorHandler.h"
+#include "platform/Histogram.h"
 
 namespace blink {
 
@@ -17,6 +18,13 @@
     setHandler(AudioBasicProcessorHandler::create(
         AudioHandler::NodeTypeIIRFilter, *this, sampleRate,
         adoptPtr(new IIRProcessor(sampleRate, 1, feedforwardCoef, feedbackCoef))));
+
+    // Histogram of the IIRFilter order.  createIIRFilter ensures that the length of |feedbackCoef|
+    // is in the range [1, IIRFilter::kMaxOrder + 1].  The order is one less than the length of this
+    // vector.
+    DEFINE_STATIC_LOCAL(SparseHistogram, filterOrderHistogram, ("WebAudio.IIRFilterNode.Order"));
+
+    filterOrderHistogram.sample(feedbackCoef.size() - 1);
 }
 
 DEFINE_TRACE(IIRFilterNode)
diff --git a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
index 78c2559..1cb6bc8 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
@@ -25,7 +25,6 @@
 #include "modules/webgl/WebGLUniformLocation.h"
 #include "modules/webgl/WebGLVertexArrayObject.h"
 #include "platform/CheckedInt.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "wtf/OwnPtr.h"
 #include "wtf/PassOwnPtr.h"
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h
index 7867930..0fbbd9e 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h
@@ -32,10 +32,6 @@
 #include "wtf/RefCounted.h"
 
 namespace blink {
-class WebGraphicsContext3D;
-}
-
-namespace blink {
 
 class WebGLSharedObject;
 class WebGLRenderingContextBase;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLContextObject.h b/third_party/WebKit/Source/modules/webgl/WebGLContextObject.h
index 842323c..014ef0e 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLContextObject.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLContextObject.h
@@ -29,10 +29,6 @@
 #include "modules/webgl/WebGLObject.h"
 
 namespace blink {
-class WebGraphicsContext3D;
-}
-
-namespace blink {
 
 class WebGLRenderingContextBase;
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLObject.cpp b/third_party/WebKit/Source/modules/webgl/WebGLObject.cpp
index 5ef85c1..9b0e2ab2 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLObject.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLObject.cpp
@@ -26,7 +26,6 @@
 #include "modules/webgl/WebGLObject.h"
 
 #include "modules/webgl/WebGLRenderingContextBase.h"
-#include "public/platform/WebGraphicsContext3D.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLObject.h b/third_party/WebKit/Source/modules/webgl/WebGLObject.h
index 89ef92a..7ee2002 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLObject.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLObject.h
@@ -31,10 +31,6 @@
 #include "platform/heap/Handle.h"
 #include "wtf/Assertions.h"
 
-namespace blink {
-class WebGraphicsContext3D;
-}
-
 namespace gpu {
 namespace gles2 {
 class GLES2Interface;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index 828b8752..26f07ba3 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -91,7 +91,6 @@
 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
 #include "platform/graphics/gpu/DrawingBuffer.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "public/platform/functional/WebFunction.h"
 #include "wtf/Functional.h"
@@ -1060,7 +1059,7 @@
     m_extensions.clear();
 
     // Context must be removed from the group prior to the destruction of the
-    // WebGraphicsContext3D, otherwise shared objects may not be properly deleted.
+    // GL context, otherwise shared objects may not be properly deleted.
     m_contextGroup->removeContext(this);
 
     destroyContext();
@@ -3548,7 +3547,7 @@
         }
         return true;
     case GL_FLOAT:
-        if (extensionEnabled(OESTextureFloatName)) {
+        if (extensionEnabled(OESTextureFloatName) || extensionEnabled(OESTextureHalfFloatName)) {
             if (buffer && buffer->type() != DOMArrayBufferView::TypeFloat32) {
                 synthesizeGLError(GL_INVALID_OPERATION, "readPixels", "type FLOAT but ArrayBufferView not Float32Array");
                 return false;
@@ -4052,14 +4051,14 @@
 
     if (!canvas->is3D()) {
         ImageBuffer* buffer = canvas->buffer();
-        if (!buffer->copyToPlatformTexture(webContext(), contextGL(), targetTexture, targetInternalformat, targetType,
+        if (!buffer->copyToPlatformTexture(contextGL(), targetTexture, targetInternalformat, targetType,
             targetLevel, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
             ASSERT_NOT_REACHED();
         }
     } else {
         WebGLRenderingContextBase* gl = toWebGLRenderingContextBase(canvas->renderingContext());
         ScopedTexture2DRestorer restorer(gl);
-        if (!gl->drawingBuffer()->copyToPlatformTexture(webContext(), contextGL(), targetTexture, targetInternalformat, targetType,
+        if (!gl->drawingBuffer()->copyToPlatformTexture(contextGL(), targetTexture, targetInternalformat, targetType,
             targetLevel, m_unpackPremultiplyAlpha, !m_unpackFlipY, BackBuffer)) {
             ASSERT_NOT_REACHED();
         }
@@ -4156,7 +4155,7 @@
                 video->paintCurrentFrame(imageBuffer->canvas(), IntRect(0, 0, video->videoWidth(), video->videoHeight()), nullptr);
 
                 // This is a straight GPU-GPU copy, any necessary color space conversion was handled in the paintCurrentFrameInContext() call.
-                if (imageBuffer->copyToPlatformTexture(webContext(), contextGL(), texture->object(), internalformat, type,
+                if (imageBuffer->copyToPlatformTexture(contextGL(), texture->object(), internalformat, type,
                     level, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
                     return;
                 }
@@ -5128,8 +5127,21 @@
 ScriptValue WebGLRenderingContextBase::getIntParameter(ScriptState* scriptState, GLenum pname)
 {
     GLint value = 0;
-    if (!isContextLost())
+    if (!isContextLost()) {
         contextGL()->GetIntegerv(pname, &value);
+        switch (pname) {
+        case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
+        case GL_IMPLEMENTATION_COLOR_READ_TYPE:
+            if (value == 0) {
+                // This indicates read framebuffer is incomplete and an
+                // INVALID_OPERATION has been generated.
+                return ScriptValue::createNull(scriptState);
+            }
+            break;
+        default:
+            break;
+        }
+    }
     return WebGLAny(scriptState, value);
 }
 
@@ -5923,7 +5935,7 @@
         attributes, canvas()->document().topDocument().url(), 0, &glInfo));
     RefPtr<DrawingBuffer> buffer;
     if (contextProvider) {
-        // Construct a new drawing buffer with the new WebGraphicsContext3D.
+        // Construct a new drawing buffer with the new GL context.
         buffer = createDrawingBuffer(contextProvider.release());
         // If DrawingBuffer::create() fails to allocate a fbo, |drawingBuffer| is set to null.
     }
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index 97edb87..0ec43369 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -45,7 +45,6 @@
 #include "platform/graphics/gpu/DrawingBuffer.h"
 #include "platform/graphics/gpu/Extensions3DUtil.h"
 #include "platform/graphics/gpu/WebGLImageConversion.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "wtf/OwnPtr.h"
 #include "wtf/text/WTFString.h"
 
@@ -380,7 +379,6 @@
     void forceRestoreContext();
     void loseContextImpl(LostContextMode, AutoRecoveryMethod);
 
-    WebGraphicsContext3D* webContext() const { return drawingBuffer()->context(); }
     gpu::gles2::GLES2Interface* contextGL() const
     {
         DrawingBuffer* d = drawingBuffer();
@@ -1016,8 +1014,8 @@
         DontDisplayInConsole
     };
 
-    // Wrapper for WebGraphicsContext3D::synthesizeGLError that sends a message
-    // to the JavaScript console.
+    // Reports an error to glGetError, sends a message to the JavaScript
+    // console.
     void synthesizeGLError(GLenum, const char* functionName, const char* description, ConsoleDisplayPreference = DisplayInConsole);
     void emitGLWarning(const char* function, const char* reason);
 
diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h
index f2e7a239..295f016 100644
--- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h
+++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h
@@ -87,6 +87,7 @@
 
 private:
     WebScrollbar& m_scrollbar;
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
index aac5c704..9895364d 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
@@ -36,7 +36,6 @@
 #include "platform/graphics/gpu/SharedContextRateLimiter.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebCompositorSupport.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "public/platform/WebScheduler.h"
 #include "public/platform/WebTraceLocation.h"
@@ -690,15 +689,6 @@
 }
 
 
-WebGraphicsContext3D* Canvas2DLayerBridge::context()
-{
-    // Check on m_layer is necessary because context() may be called during
-    // the destruction of m_layer
-    if (m_layer && !m_destructionInProgress)
-        checkSurfaceValid(); // To ensure rate limiter is disabled if context is lost.
-    return m_contextProvider ? m_contextProvider->context3d() : 0;
-}
-
 gpu::gles2::GLES2Interface* Canvas2DLayerBridge::contextGL()
 {
     // Check on m_layer is necessary because contextGL() may be called during
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
index 0654fde..4b83705 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
@@ -58,7 +58,6 @@
 class Canvas2DLayerBridgeHistogramLogger;
 class Canvas2DLayerBridgeTest;
 class ImageBuffer;
-class WebGraphicsContext3D;
 class WebGraphicsContext3DProvider;
 class SharedContextRateLimiter;
 
@@ -184,7 +183,6 @@
     };
 
     Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider>, const IntSize&, int msaaSampleCount, OpacityMode, AccelerationMode);
-    WebGraphicsContext3D* context();
     gpu::gles2::GLES2Interface* contextGL();
     void startRecording();
     void skipQueuedDrawCommands();
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
index 16de9dd..351a4a2e 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
@@ -72,11 +72,6 @@
         m_grContext = adoptRef(GrContext::Create(kOpenGL_GrBackend, reinterpret_cast<GrBackendContext>(glInterface.get())));
     }
 
-    WebGraphicsContext3D* context3d() override
-    {
-        return nullptr;
-    }
-
     GrContext* grContext() override
     {
         return m_grContext.get();
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
index 27e66fc..c89a757 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
@@ -359,6 +359,8 @@
     OwnPtr<PaintController> m_paintController;
 
     IntRect m_previousInterestRect;
+
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
index 624db43..60c4b349 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
@@ -39,7 +39,6 @@
 #include "platform/transforms/TranslateTransformOperation.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebCompositorSupport.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebLayer.h"
 #include "public/platform/WebLayerTreeView.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
index 09f6df4..79220c7 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
@@ -48,7 +48,6 @@
 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebExternalTextureMailbox.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "skia/ext/texture_handle.h"
 #include "third_party/skia/include/core/SkPicture.h"
@@ -179,7 +178,7 @@
     return m_surface->layer();
 }
 
-bool ImageBuffer::copyToPlatformTexture(WebGraphicsContext3D* context, gpu::gles2::GLES2Interface* gl, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
+bool ImageBuffer::copyToPlatformTexture(gpu::gles2::GLES2Interface* gl, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
 {
     if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(GL_TEXTURE_2D, internalFormat, destType, level))
         return false;
@@ -248,7 +247,6 @@
     OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
     if (!provider)
         return false;
-    WebGraphicsContext3D* context3D = provider->context3d();
     gpu::gles2::GLES2Interface* gl = provider->contextGL();
     Platform3DObject textureId = m_surface->getBackingTextureHandleForOverwrite();
     if (!textureId)
@@ -256,7 +254,7 @@
 
     gl->Flush();
 
-    return drawingBuffer->copyToPlatformTexture(context3D, gl, textureId, GL_RGBA,
+    return drawingBuffer->copyToPlatformTexture(gl, textureId, GL_RGBA,
         GL_UNSIGNED_BYTE, 0, true, false, sourceBuffer);
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.h b/third_party/WebKit/Source/platform/graphics/ImageBuffer.h
index 05bc3f88..71d75652 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.h
+++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.h
@@ -66,7 +66,6 @@
 class ImageBufferClient;
 class IntPoint;
 class IntRect;
-class WebGraphicsContext3D;
 
 enum Multiply {
     Premultiplied,
@@ -125,7 +124,7 @@
     // with textures that are RGB or RGBA format, UNSIGNED_BYTE type and level 0, as specified in
     // Extensions3D::canUseCopyTextureCHROMIUM().
     // Destroys the TEXTURE_2D binding for the active texture unit of the passed context
-    bool copyToPlatformTexture(WebGraphicsContext3D*, gpu::gles2::GLES2Interface*, Platform3DObject, GLenum, GLenum, GLint, bool, bool);
+    bool copyToPlatformTexture(gpu::gles2::GLES2Interface*, Platform3DObject, GLenum, GLenum, GLint, bool, bool);
 
     bool copyRenderingResultsFromDrawingBuffer(DrawingBuffer*, SourceDrawingBuffer);
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
index 82f55a9..bd99908 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
@@ -40,7 +40,6 @@
 #include "public/platform/WebCompositorSupport.h"
 #include "public/platform/WebExternalBitmap.h"
 #include "public/platform/WebExternalTextureLayer.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "wtf/CheckedNumeric.h"
 #include "wtf/typed_arrays/ArrayBufferContents.h"
@@ -137,7 +136,6 @@
     , m_readFramebufferBinding(0)
     , m_activeTextureUnit(GL_TEXTURE0)
     , m_contextProvider(std::move(contextProvider))
-    , m_context(m_contextProvider->context3d())
     , m_gl(m_contextProvider->contextGL())
     , m_extensionsUtil(std::move(extensionsUtil))
     , m_size(-1, -1)
@@ -192,11 +190,6 @@
     }
 }
 
-WebGraphicsContext3D* DrawingBuffer::context()
-{
-    return m_context;
-}
-
 gpu::gles2::GLES2Interface* DrawingBuffer::contextGL()
 {
     return m_gl;
@@ -480,7 +473,7 @@
     return true;
 }
 
-bool DrawingBuffer::copyToPlatformTexture(WebGraphicsContext3D* context, gpu::gles2::GLES2Interface* gl, GLuint texture, GLenum internalFormat,
+bool DrawingBuffer::copyToPlatformTexture(gpu::gles2::GLES2Interface* gl, GLuint texture, GLenum internalFormat,
     GLenum destType, GLint level, bool premultiplyAlpha, bool flipY, SourceDrawingBuffer sourceBuffer)
 {
     if (m_contentsChanged) {
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
index 4d1e7be..e44d8d51 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
@@ -37,7 +37,6 @@
 #include "platform/graphics/gpu/WebGLImageConversion.h"
 #include "public/platform/WebExternalTextureLayerClient.h"
 #include "public/platform/WebExternalTextureMailbox.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -63,7 +62,6 @@
 class ImageBuffer;
 class WebExternalBitmap;
 class WebExternalTextureLayer;
-class WebGraphicsContext3D;
 class WebGraphicsContext3DProvider;
 class WebLayer;
 
@@ -161,7 +159,6 @@
 
     WebLayer* platformLayer();
 
-    WebGraphicsContext3D* context();
     gpu::gles2::GLES2Interface* contextGL();
     WebGraphicsContext3DProvider* contextProvider();
 
@@ -170,7 +167,7 @@
     void mailboxReleased(const WebExternalTextureMailbox&, bool lostResource = false) override;
 
     // Destroys the TEXTURE_2D binding for the owned context
-    bool copyToPlatformTexture(WebGraphicsContext3D*, gpu::gles2::GLES2Interface*, GLuint texture, GLenum internalFormat,
+    bool copyToPlatformTexture(gpu::gles2::GLES2Interface*, GLuint texture, GLenum internalFormat,
         GLenum destType, GLint level, bool premultiplyAlpha, bool flipY, SourceDrawingBuffer);
 
     void setPackAlignment(GLint param);
@@ -313,8 +310,8 @@
     GLenum m_activeTextureUnit;
 
     OwnPtr<WebGraphicsContext3DProvider> m_contextProvider;
-    WebGraphicsContext3D* m_context; // Lifetime is tied to the m_contextProvider.
-    gpu::gles2::GLES2Interface* m_gl; // Lifetime is tied to the m_contextProvider.
+    // Lifetime is tied to the m_contextProvider.
+    gpu::gles2::GLES2Interface* m_gl;
     OwnPtr<Extensions3DUtil> m_extensionsUtil;
     IntSize m_size;
     const bool m_discardFramebufferSupported;
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
index 45dab22..50691b1 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
@@ -38,7 +38,6 @@
 #include "platform/graphics/gpu/Extensions3DUtil.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebExternalTextureMailbox.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "public/platform/functional/WebFunction.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -241,7 +240,6 @@
     {
     }
 
-    WebGraphicsContext3D* context3d() override { return nullptr; }
     gpu::gles2::GLES2Interface* contextGL() override { return m_gl.get(); }
     // Not used by WebGL code.
     GrContext* grContext() override { return nullptr; }
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/SharedContextRateLimiter.cpp b/third_party/WebKit/Source/platform/graphics/gpu/SharedContextRateLimiter.cpp
index 0086d81..8460ab13 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/SharedContextRateLimiter.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/SharedContextRateLimiter.cpp
@@ -6,7 +6,6 @@
 
 #include "platform/graphics/gpu/Extensions3DUtil.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebGraphicsContext3D.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 
 #ifndef GL_COMMANDS_COMPLETED_CHROMIUM
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.cpp
index eeb5b2f..1bb0c82 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.cpp
@@ -5,11 +5,15 @@
 #include "platform/graphics/paint/DisplayItemClient.h"
 
 #if ENABLE(ASSERT)
-
 #include "wtf/HashSet.h"
+#endif
 
 namespace blink {
 
+DisplayItemCacheGeneration::Generation DisplayItemCacheGeneration::s_nextGeneration = 1;
+
+#if ENABLE(ASSERT)
+
 HashSet<const DisplayItemClient*>* liveDisplayItemClients = nullptr;
 
 DisplayItemClient::DisplayItemClient()
@@ -29,6 +33,6 @@
     return liveDisplayItemClients && liveDisplayItemClients->contains(&client);
 }
 
-} // namespace blink
-
 #endif // ENABLE(ASSERT)
+
+} // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
index 784aa638..9aeabff1 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
@@ -11,6 +11,40 @@
 
 namespace blink {
 
+// Holds a unique cache generation id of display items and paint controllers.
+//
+// A paint controller sets its cache generation to DisplayItemCacheGeneration::next()
+// at the end of each commitNewDisplayItems, and updates the cache generation of each
+// client with cached drawings by calling DisplayItemClient::setDisplayItemsCached().
+// A display item is treated as validly cached in a paint controller if its cache generation
+// matches the paint controller's cache generation.
+//
+// SPv1 only: If a display item is painted on multiple paint controllers, because cache
+// generations are unique, the client's cache generation matches the last paint controller
+// only. The client will be treated as invalid on other paint controllers regardless if
+// it's validly cached by these paint controllers. The situation is very rare (about 0.07%
+// clients were painted on multiple paint controllers) so the performance penalty is trivial.
+class PLATFORM_EXPORT DisplayItemCacheGeneration {
+    DISALLOW_NEW();
+public:
+    DisplayItemCacheGeneration() : m_value(kInvalidGeneration) { }
+
+    void invalidate() { m_value = kInvalidGeneration; }
+    static DisplayItemCacheGeneration next() { return DisplayItemCacheGeneration(s_nextGeneration++); }
+    bool matches(const DisplayItemCacheGeneration& other)
+    {
+        return m_value != kInvalidGeneration && other.m_value != kInvalidGeneration && m_value == other.m_value;
+    }
+
+private:
+    typedef uint32_t Generation;
+    DisplayItemCacheGeneration(Generation value) : m_value(value) { }
+
+    static const Generation kInvalidGeneration = 0;
+    static Generation s_nextGeneration;
+    Generation m_value;
+};
+
 // The interface for objects that can be associated with display items.
 // A DisplayItemClient object should live at least longer than the document cycle
 // in which its display items are created during painting.
@@ -31,12 +65,29 @@
     // offset by offsetFromLayoutObjectWithSubpixelAccumulation().
     virtual LayoutRect visualRect() const = 0;
 
+    virtual bool displayItemsAreCached(DisplayItemCacheGeneration) const = 0;
+    virtual void setDisplayItemsCached(DisplayItemCacheGeneration) const = 0;
+    virtual void setDisplayItemsUncached() const = 0;
+
 #if ENABLE(ASSERT)
     // Tests if a DisplayItemClient object has been created and has not been deleted yet.
     static bool isAlive(const DisplayItemClient&);
 #endif
+
+protected:
 };
 
+#define DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION \
+    bool displayItemsAreCached(DisplayItemCacheGeneration cacheGeneration) const final { return m_cacheGeneration.matches(cacheGeneration); } \
+    void setDisplayItemsCached(DisplayItemCacheGeneration cacheGeneration) const final { m_cacheGeneration = cacheGeneration; } \
+    void setDisplayItemsUncached() const final { m_cacheGeneration.invalidate(); } \
+    mutable DisplayItemCacheGeneration m_cacheGeneration;
+
+#define DISPLAY_ITEM_CACHE_STATUS_UNCACHEABLE_IMPLEMENTATION \
+    bool displayItemsAreCached(DisplayItemCacheGeneration) const final { return false; } \
+    void setDisplayItemsCached(DisplayItemCacheGeneration) const final { } \
+    void setDisplayItemsUncached() const final { }
+
 inline bool operator==(const DisplayItemClient& client1, const DisplayItemClient& client2) { return &client1 == &client2; }
 inline bool operator!=(const DisplayItemClient& client1, const DisplayItemClient& client2) { return &client1 != &client2; }
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
index ea460b37..af1afd9 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -138,9 +138,8 @@
 void PaintController::invalidateUntracked(const DisplayItemClient& client)
 {
     // This can be called during painting, but we can't invalidate already painted clients.
+    client.setDisplayItemsUncached();
     ASSERT(!m_newDisplayItemIndicesByClient.contains(&client));
-    updateValidlyCachedClientsIfNeeded();
-    m_validlyCachedClients.remove(&client);
 }
 
 void PaintController::invalidateAll()
@@ -148,8 +147,7 @@
     // Can only be called during layout/paintInvalidation, not during painting.
     ASSERT(m_newDisplayItemList.isEmpty());
     m_currentPaintArtifact.reset();
-    m_validlyCachedClients.clear();
-    m_validlyCachedClientsDirty = false;
+    m_currentCacheGeneration.invalidate();
 
     if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && m_trackedPaintInvalidationObjects)
         m_trackedPaintInvalidationObjects->append("##ALL##");
@@ -157,10 +155,10 @@
 
 bool PaintController::clientCacheIsValid(const DisplayItemClient& client) const
 {
+    ASSERT(DisplayItemClient::isAlive(client));
     if (skippingCache())
         return false;
-    updateValidlyCachedClientsIfNeeded();
-    return m_validlyCachedClients.contains(&client);
+    return client.displayItemsAreCached(m_currentCacheGeneration);
 }
 
 void PaintController::invalidatePaintOffset(const DisplayItemClient& client)
@@ -236,12 +234,10 @@
     for (; context.nextItemToIndex != currentEnd; ++context.nextItemToIndex) {
         const DisplayItem& item = *context.nextItemToIndex;
         ASSERT(item.hasValidClient());
-        if (item.isCacheable() && clientCacheIsValid(item.client())) {
-            if (id.matches(item))
-                return context.nextItemToIndex++;
-
+        if (id.matches(item))
+            return context.nextItemToIndex++;
+        if (item.isCacheable())
             addItemToIndexIfNeeded(item, context.nextItemToIndex - m_currentPaintArtifact.getDisplayItemList().begin(), context.displayItemIndicesByClient);
-        }
     }
     return currentEnd;
 }
@@ -296,9 +292,6 @@
         "num_non_cached_new_items", (int)m_newDisplayItemList.size() - m_numCachedNewItems);
     m_numCachedNewItems = 0;
 
-    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
-        m_clientsCheckedPaintInvalidation.clear();
-
     // These data structures are used during painting only.
     ASSERT(m_scopeStack.isEmpty());
     m_scopeStack.clear();
@@ -320,12 +313,10 @@
 
         m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_newPaintChunks.releasePaintChunks());
         m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes);
-        m_validlyCachedClientsDirty = true;
+        updateCacheGeneration();
         return;
     }
 
-    updateValidlyCachedClientsIfNeeded();
-
     // Stores indices to valid DrawingDisplayItems in m_currentDisplayItems that have not been matched
     // by CachedDisplayItems during synchronized matching. The indexed items will be matched
     // by later out-of-order CachedDisplayItems in m_newDisplayItemList. This ensures that when
@@ -403,7 +394,7 @@
     m_currentPaintArtifact = PaintArtifact(std::move(updatedList), m_newPaintChunks.releasePaintChunks());
 
     m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes);
-    m_validlyCachedClientsDirty = true;
+    updateCacheGeneration();
 }
 
 size_t PaintController::approximateUnsharedMemoryUsage() const
@@ -431,22 +422,12 @@
     return memoryUsage;
 }
 
-void PaintController::updateValidlyCachedClientsIfNeeded() const
+void PaintController::updateCacheGeneration()
 {
-    if (!m_validlyCachedClientsDirty)
-        return;
-
-    m_validlyCachedClients.clear();
-    m_validlyCachedClientsDirty = false;
-
-    const DisplayItemClient* lastAddedClient = nullptr;
+    m_currentCacheGeneration = DisplayItemCacheGeneration::next();
     for (const DisplayItem& displayItem : m_currentPaintArtifact.getDisplayItemList()) {
-        if (&displayItem.client() == lastAddedClient)
-            continue;
-        if (displayItem.isCacheable()) {
-            lastAddedClient = &displayItem.client();
-            m_validlyCachedClients.add(lastAddedClient);
-        }
+        if (displayItem.isCacheable())
+            displayItem.client().setDisplayItemsCached(m_currentCacheGeneration);
     }
 }
 
@@ -513,7 +494,7 @@
         ASSERT_NOT_REACHED();
     }
 
-    if (newItem.isCacheable() && !m_validlyCachedClients.contains(&newItem.client())) {
+    if (newItem.isCacheable() && !clientCacheIsValid(newItem.client())) {
         showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: invalidated in cached subsequence", &newItem, &oldItem);
         ASSERT_NOT_REACHED();
     }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
index b9edbba..eda39ff 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
@@ -163,15 +163,6 @@
         return m_trackedPaintInvalidationObjects ? *m_trackedPaintInvalidationObjects : Vector<String>();
     }
 
-    bool clientHasCheckedPaintInvalidation(const DisplayItemClient& client) const
-    {
-        return m_clientsCheckedPaintInvalidation.contains(&client);
-    }
-    void setClientHasCheckedPaintInvalidation(const DisplayItemClient& client)
-    {
-        m_clientsCheckedPaintInvalidation.add(&client);
-    }
-
 #if ENABLE(ASSERT)
     void assertDisplayItemClientsAreLive();
 #endif
@@ -179,21 +170,19 @@
 protected:
     PaintController()
         : m_newDisplayItemList(kInitialDisplayItemListCapacityBytes)
-        , m_validlyCachedClientsDirty(false)
         , m_constructionDisabled(false)
         , m_subsequenceCachingDisabled(false)
         , m_textPainted(false)
         , m_imagePainted(false)
         , m_skippingCacheCount(0)
         , m_numCachedNewItems(0)
-        , m_nextScope(1) { }
+        , m_nextScope(1)
+    { }
 
 private:
     // Set new item state (scopes, cache skipping, etc) for a new item.
     void processNewItem(DisplayItem&);
 
-    void updateValidlyCachedClientsIfNeeded() const;
-
 #ifndef NDEBUG
     WTF::String displayItemListAsDebugString(const DisplayItemList&) const;
 #endif
@@ -220,6 +209,8 @@
 
     void commitNewDisplayItemsInternal(const LayoutSize& offsetFromLayoutObject);
 
+    void updateCacheGeneration();
+
     // The last complete paint artifact.
     // In SPv2, this includes paint chunks as well as display items.
     PaintArtifact m_currentPaintArtifact;
@@ -228,18 +219,6 @@
     DisplayItemList m_newDisplayItemList;
     PaintChunker m_newPaintChunks;
 
-    // Contains all clients having valid cached paintings if updated.
-    // It's lazily updated in updateValidlyCachedClientsIfNeeded().
-    // TODO(wangxianzhu): In the future we can replace this with client-side repaint flags
-    // to avoid the cost of building and querying the hash table.
-    mutable HashSet<const DisplayItemClient*> m_validlyCachedClients;
-    mutable bool m_validlyCachedClientsDirty;
-
-    // Used during painting. Contains clients that have checked paint invalidation and
-    // are known to be valid.
-    // TODO(wangxianzhu): Use client side flag to avoid const of hash table.
-    HashSet<const DisplayItemClient*> m_clientsCheckedPaintInvalidation;
-
 #if ENABLE(ASSERT)
     // Set of clients which had paint offset changes since the last commit. This is used for
     // ensuring paint offsets are only updated once and are the same in all phases.
@@ -275,6 +254,8 @@
 #endif
 
     OwnPtr<Vector<String>> m_trackedPaintInvalidationObjects;
+
+    DisplayItemCacheGeneration m_currentCacheGeneration;
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
index 64ea8257..2e34c4fc3 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
@@ -21,7 +21,9 @@
 public:
     PaintControllerTest()
         : m_paintController(PaintController::create())
-        , m_originalSlimmingPaintV2Enabled(RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { }
+        , m_originalSlimmingPaintV2Enabled(RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+    {
+    }
 
     IntRect visualRect(const PaintArtifact& paintArtifact, unsigned index)
     {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/README.md b/third_party/WebKit/Source/platform/graphics/paint/README.md
index 5e5fb62e..28d916d9 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/README.md
+++ b/third_party/WebKit/Source/platform/graphics/paint/README.md
@@ -179,9 +179,52 @@
 
 At this point, the paint artifact is ready to be drawn or composited.
 
-*** aside
-TODO(jbroman): Explain invalidation.
-***
+### Paint result caching and invalidation
+
+See [Display item caching](../../../core/paint/README.md#paint-result-caching)
+and [Paint invalidation](../../../core/paint/README.md#paint-invalidation) for
+more details about how caching and invalidation are handled in blink core
+module using `PaintController` API.
+
+We use 'cache generation' which is a unique id of cache status in each
+`DisplayItemClient` and `PaintController` to determine if the client is validly
+cached by a `PaintController`.
+
+A paint controller sets its cache generation to
+`DisplayItemCacheGeneration::next()` at the end of each
+`commitNewDisplayItems()`, and updates the cache generation of each client with
+cached drawings by calling `DisplayItemClient::setDisplayItemsCached()`.
+A display item is treated as validly cached in a paint controller if its cache
+generation matches the paint controller's cache generation.
+
+`kInvalidCacheGeneration` is a special cache generation value which matches no
+other cache generations. When a `DisplayItemClient` is invalidated, we set its
+cache generation to `kInvalidCacheGeneration`. When a `PaintController` is
+cleared (e.g. when the corresponding `GraphicsLayer` is fully invalidated), we
+also set its cache generation to `kInvalidCacheGeneration`.
+
+For now we use a uint32_t variable to store cache generation. Assuming there is
+an animation in 60fps needing main-thread repaint, the cache generation will
+overflow after 2^32/86400/60 = 828 days. The time may be shorter if there are
+multiple animating `PaintController`s in the same process. When it overflows,
+we may treat some object that is not cached as cached if the following
+conditions are all met:
+*   the object was painted when the cache generation was *n*;
+*   the object has been neither painted nor invalidated since cache generation
+    *n*;
+*   when the cache generation wraps back to exact *n*, the object happens to be
+    painted again.
+As the paint controller doesn't have cached display items for the object, there
+will be corrupted painting or assertion failure. The chance is too low to be
+concerned.
+
+SPv1 only: If a display item is painted on multiple paint controllers, because
+cache generations are unique, the client's cache generation matches the last
+paint controller only. The client will be treated as invalid on other paint
+controllers regardless if it's validly cached by these paint controllers.
+The situation is very rare (about 0.07% clients were painted on multiple paint
+controllers in a [Cluster Telemetry run](https://ct.skia.org/chromium_perf_runs)
+(run 803) so the performance penalty is trivial.
 
 ## Paint artifact compositor
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h
index f2652e7a..1bb4839 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.h
@@ -43,6 +43,8 @@
     LayoutRect visualRect() const final { return LayoutRect(); }
 
 private:
+    DISPLAY_ITEM_CACHE_STATUS_UNCACHEABLE_IMPLEMENTATION
+
     OwnPtr<PaintController> m_paintController;
     OwnPtr<GraphicsContext> m_context;
     FloatRect m_bounds;
diff --git a/third_party/WebKit/Source/platform/scroll/Scrollbar.h b/third_party/WebKit/Source/platform/scroll/Scrollbar.h
index 197e691..43d20a6 100644
--- a/third_party/WebKit/Source/platform/scroll/Scrollbar.h
+++ b/third_party/WebKit/Source/platform/scroll/Scrollbar.h
@@ -231,6 +231,8 @@
 
     bool m_trackNeedsRepaint;
     bool m_thumbNeedsRepaint;
+
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
 };
 
 DEFINE_TYPE_CASTS(Scrollbar, Widget, widget, widget->isScrollbar(), widget.isScrollbar());
diff --git a/third_party/WebKit/Source/platform/testing/FakeDisplayItemClient.h b/third_party/WebKit/Source/platform/testing/FakeDisplayItemClient.h
index ea301b4..e43997df 100644
--- a/third_party/WebKit/Source/platform/testing/FakeDisplayItemClient.h
+++ b/third_party/WebKit/Source/platform/testing/FakeDisplayItemClient.h
@@ -21,6 +21,8 @@
     LayoutRect visualRect() const override { return m_visualRect; }
 
 private:
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
+
     String m_name;
     LayoutRect m_visualRect;
 };
diff --git a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
index e431145..fba9b17 100644
--- a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
+++ b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
@@ -27,6 +27,7 @@
 private:
     FloatRect m_rect;
     Color m_color;
+    DISPLAY_ITEM_CACHE_STATUS_IMPLEMENTATION
 };
 
 PassRefPtr<SkPicture> TestPaintArtifact::DummyRectClient::makePicture() const
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js b/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js
index 70aeacf..1a9ae17f 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js
+++ b/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js
@@ -599,9 +599,8 @@
         // NOTE: This list contains only not native Command Line API methods. For full list: V8Console.
         // NOTE: Argument names of these methods will be printed in the console, so use pretty names!
         var members = [ "$", "$$", "$x", "monitorEvents", "unmonitorEvents", "getEventListeners" ];
-        var commandLineAPIImpl = this._commandLineAPIImpl;
         for (var member of members)
-            nativeCommandLineAPI[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
+            nativeCommandLineAPI[member] = CommandLineAPIImpl[member];
         var functionToStringMap = new Map([
             ["$",          "function $(selector, [startNode]) { [Command Line API] }"],
             ["$$",         "function $$(selector, [startNode]) { [Command Line API] }"],
@@ -1148,187 +1147,179 @@
     __proto__: null
 }
 
+var CommandLineAPIImpl = { __proto__: null }
+
 /**
- * @constructor
+ * @param {string} selector
+ * @param {!Node=} opt_startNode
+ * @return {*}
  */
-function CommandLineAPIImpl()
+CommandLineAPIImpl.$ = function (selector, opt_startNode)
 {
+    if (CommandLineAPIImpl._canQuerySelectorOnNode(opt_startNode))
+        return opt_startNode.querySelector(selector);
+
+    return inspectedGlobalObject.document.querySelector(selector);
 }
 
-CommandLineAPIImpl.prototype = {
-    /**
-     * @param {string} selector
-     * @param {!Node=} opt_startNode
-     * @return {*}
-     */
-    $: function (selector, opt_startNode)
-    {
-        if (this._canQuerySelectorOnNode(opt_startNode))
-            return opt_startNode.querySelector(selector);
+/**
+ * @param {string} selector
+ * @param {!Node=} opt_startNode
+ * @return {*}
+ */
+CommandLineAPIImpl.$$ = function (selector, opt_startNode)
+{
+    if (CommandLineAPIImpl._canQuerySelectorOnNode(opt_startNode))
+        return slice(opt_startNode.querySelectorAll(selector));
+    return slice(inspectedGlobalObject.document.querySelectorAll(selector));
+}
 
-        return inspectedGlobalObject.document.querySelector(selector);
-    },
+/**
+ * @param {!Node=} node
+ * @return {boolean}
+ */
+CommandLineAPIImpl._canQuerySelectorOnNode = function(node)
+{
+    return !!node && InjectedScriptHost.subtype(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
+}
 
-    /**
-     * @param {string} selector
-     * @param {!Node=} opt_startNode
-     * @return {*}
-     */
-    $$: function (selector, opt_startNode)
-    {
-        if (this._canQuerySelectorOnNode(opt_startNode))
-            return slice(opt_startNode.querySelectorAll(selector));
-        return slice(inspectedGlobalObject.document.querySelectorAll(selector));
-    },
-
-    /**
-     * @param {!Node=} node
-     * @return {boolean}
-     */
-    _canQuerySelectorOnNode: function(node)
-    {
-        return !!node && InjectedScriptHost.subtype(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
-    },
-
-    /**
-     * @param {string} xpath
-     * @param {!Node=} opt_startNode
-     * @return {*}
-     */
-    $x: function(xpath, opt_startNode)
-    {
-        var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedGlobalObject.document;
-        var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult.ANY_TYPE, null);
-        switch (result.resultType) {
-        case XPathResult.NUMBER_TYPE:
-            return result.numberValue;
-        case XPathResult.STRING_TYPE:
-            return result.stringValue;
-        case XPathResult.BOOLEAN_TYPE:
-            return result.booleanValue;
-        default:
-            var nodes = [];
-            var node;
-            while (node = result.iterateNext())
-                push(nodes, node);
-            return nodes;
-        }
-    },
-
-    /**
-     * @param {!Object} object
-     * @param {!Array.<string>|string=} opt_types
-     */
-    monitorEvents: function(object, opt_types)
-    {
-        if (!object || !object.addEventListener || !object.removeEventListener)
-            return;
-        var types = this._normalizeEventTypes(opt_types);
-        for (var i = 0; i < types.length; ++i) {
-            object.removeEventListener(types[i], this._logEvent, false);
-            object.addEventListener(types[i], this._logEvent, false);
-        }
-    },
-
-    /**
-     * @param {!Object} object
-     * @param {!Array.<string>|string=} opt_types
-     */
-    unmonitorEvents: function(object, opt_types)
-    {
-        if (!object || !object.addEventListener || !object.removeEventListener)
-            return;
-        var types = this._normalizeEventTypes(opt_types);
-        for (var i = 0; i < types.length; ++i)
-            object.removeEventListener(types[i], this._logEvent, false);
-    },
-
-    /**
-     * @param {!Node} node
-     * @return {!Object|undefined}
-     */
-    getEventListeners: function(node)
-    {
-        var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(node));
-        if (!result)
-            return;
-
-        // TODO(dtapuska): Remove this one closure compiler is updated
-        // to handle EventListenerOptions and passive event listeners
-        // has shipped. Don't JSDoc these otherwise it will fail.
-        // @param {boolean} capture
-        // @param {boolean} passive
-        // @return {boolean|undefined|{capture: (boolean|undefined), passive: boolean}}
-        function eventListenerOptions(capture, passive)
-        {
-            return {"capture": capture, "passive": passive};
-        }
-
-        /**
-         * @param {!Node} node
-         * @param {string} type
-         * @param {function()} listener
-         * @param {boolean} capture
-         * @param {boolean} passive
-         */
-        function removeEventListenerWrapper(node, type, listener, capture, passive)
-        {
-            node.removeEventListener(type, listener, eventListenerOptions(capture, passive));
-        }
-
-        /** @this {{type: string, listener: function(), useCapture: boolean, passive: boolean}} */
-        var removeFunc = function()
-        {
-            removeEventListenerWrapper(node, this.type, this.listener, this.useCapture, this.passive);
-        }
-        for (var type in result) {
-            var listeners = result[type];
-            for (var i = 0, listener; listener = listeners[i]; ++i) {
-                listener["type"] = type;
-                listener["remove"] = removeFunc;
-            }
-        }
-        return result;
-    },
-
-    /**
-     * @param {!Array.<string>|string=} types
-     * @return {!Array.<string>}
-     */
-    _normalizeEventTypes: function(types)
-    {
-        if (typeof types === "undefined")
-            types = ["mouse", "key", "touch", "pointer", "control", "load", "unload", "abort", "error", "select", "input", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation"];
-        else if (typeof types === "string")
-            types = [types];
-
-        var result = [];
-        for (var i = 0; i < types.length; ++i) {
-            if (types[i] === "mouse")
-                push(result, "click", "dblclick", "mousedown", "mouseeenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mouseleave", "mousewheel");
-            else if (types[i] === "key")
-                push(result, "keydown", "keyup", "keypress", "textInput");
-            else if (types[i] === "touch")
-                push(result, "touchstart", "touchmove", "touchend", "touchcancel");
-            else if (types[i] === "pointer")
-                push(result, "pointerover", "pointerout", "pointerenter", "pointerleave", "pointerdown", "pointerup", "pointermove", "pointercancel", "gotpointercapture", "lostpointercapture");
-            else if (types[i] === "control")
-                push(result, "resize", "scroll", "zoom", "focus", "blur", "select", "input", "change", "submit", "reset");
-            else
-                push(result, types[i]);
-        }
-        return result;
-    },
-
-    /**
-     * @param {!Event} event
-     */
-    _logEvent: function(event)
-    {
-        inspectedGlobalObject.console.log(event.type, event);
+/**
+ * @param {string} xpath
+ * @param {!Node=} opt_startNode
+ * @return {*}
+ */
+CommandLineAPIImpl.$x = function(xpath, opt_startNode)
+{
+    var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedGlobalObject.document;
+    var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult.ANY_TYPE, null);
+    switch (result.resultType) {
+    case XPathResult.NUMBER_TYPE:
+        return result.numberValue;
+    case XPathResult.STRING_TYPE:
+        return result.stringValue;
+    case XPathResult.BOOLEAN_TYPE:
+        return result.booleanValue;
+    default:
+        var nodes = [];
+        var node;
+        while (node = result.iterateNext())
+            push(nodes, node);
+        return nodes;
     }
 }
 
-injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
+/**
+ * @param {!Object} object
+ * @param {!Array.<string>|string=} opt_types
+ */
+CommandLineAPIImpl.monitorEvents = function(object, opt_types)
+{
+    if (!object || !object.addEventListener || !object.removeEventListener)
+        return;
+    var types = CommandLineAPIImpl._normalizeEventTypes(opt_types);
+    for (var i = 0; i < types.length; ++i) {
+        object.removeEventListener(types[i], CommandLineAPIImpl._logEvent, false);
+        object.addEventListener(types[i], CommandLineAPIImpl._logEvent, false);
+    }
+}
+
+/**
+ * @param {!Object} object
+ * @param {!Array.<string>|string=} opt_types
+ */
+CommandLineAPIImpl.unmonitorEvents = function(object, opt_types)
+{
+    if (!object || !object.addEventListener || !object.removeEventListener)
+        return;
+    var types = CommandLineAPIImpl._normalizeEventTypes(opt_types);
+    for (var i = 0; i < types.length; ++i)
+        object.removeEventListener(types[i], CommandLineAPIImpl._logEvent, false);
+}
+
+/**
+ * @param {!Node} node
+ * @return {!Object|undefined}
+ */
+CommandLineAPIImpl.getEventListeners = function(node)
+{
+    var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(node));
+    if (!result)
+        return;
+
+    // TODO(dtapuska): Remove this one closure compiler is updated
+    // to handle EventListenerOptions and passive event listeners
+    // has shipped. Don't JSDoc these otherwise it will fail.
+    // @param {boolean} capture
+    // @param {boolean} passive
+    // @return {boolean|undefined|{capture: (boolean|undefined), passive: boolean}}
+    function eventListenerOptions(capture, passive)
+    {
+        return {"capture": capture, "passive": passive};
+    }
+
+    /**
+     * @param {!Node} node
+     * @param {string} type
+     * @param {function()} listener
+     * @param {boolean} capture
+     * @param {boolean} passive
+     */
+    function removeEventListenerWrapper(node, type, listener, capture, passive)
+    {
+        node.removeEventListener(type, listener, eventListenerOptions(capture, passive));
+    }
+
+    /** @this {{type: string, listener: function(), useCapture: boolean, passive: boolean}} */
+    var removeFunc = function()
+    {
+        removeEventListenerWrapper(node, this.type, this.listener, this.useCapture, this.passive);
+    }
+    for (var type in result) {
+        var listeners = result[type];
+        for (var i = 0, listener; listener = listeners[i]; ++i) {
+            listener["type"] = type;
+            listener["remove"] = removeFunc;
+        }
+    }
+    return result;
+}
+
+/**
+ * @param {!Array.<string>|string=} types
+ * @return {!Array.<string>}
+ */
+CommandLineAPIImpl._normalizeEventTypes = function(types)
+{
+    if (typeof types === "undefined")
+        types = ["mouse", "key", "touch", "pointer", "control", "load", "unload", "abort", "error", "select", "input", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation"];
+    else if (typeof types === "string")
+        types = [types];
+
+    var result = [];
+    for (var i = 0; i < types.length; ++i) {
+        if (types[i] === "mouse")
+            push(result, "click", "dblclick", "mousedown", "mouseeenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mouseleave", "mousewheel");
+        else if (types[i] === "key")
+            push(result, "keydown", "keyup", "keypress", "textInput");
+        else if (types[i] === "touch")
+            push(result, "touchstart", "touchmove", "touchend", "touchcancel");
+        else if (types[i] === "pointer")
+            push(result, "pointerover", "pointerout", "pointerenter", "pointerleave", "pointerdown", "pointerup", "pointermove", "pointercancel", "gotpointercapture", "lostpointercapture");
+        else if (types[i] === "control")
+            push(result, "resize", "scroll", "zoom", "focus", "blur", "select", "input", "change", "submit", "reset");
+        else
+            push(result, types[i]);
+    }
+    return result;
+}
+
+/**
+ * @param {!Event} event
+ */
+CommandLineAPIImpl._logEvent = function(event)
+{
+    inspectedGlobalObject.console.log(event.type, event);
+}
+
 return injectedScript;
 })
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InspectedContext.cpp b/third_party/WebKit/Source/platform/v8_inspector/InspectedContext.cpp
index b00a55d..49cbf65 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/InspectedContext.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/InspectedContext.cpp
@@ -44,9 +44,7 @@
 
     v8::Isolate* isolate = m_debugger->isolate();
     v8::Local<v8::Object> global = info.context->Global();
-    v8::Local<v8::Object> console;
-    if (!V8Console::createConsole(this, info.hasMemoryOnConsole).ToLocal(&console))
-        return;
+    v8::Local<v8::Object> console = V8Console::createConsole(this, info.hasMemoryOnConsole);
     if (!global->Set(info.context, toV8StringInternalized(isolate, "console"), console).FromMaybe(false))
         return;
     m_console.Reset(isolate, console);
diff --git a/third_party/WebKit/Source/platform/v8_inspector/JavaScriptCallFrame.h b/third_party/WebKit/Source/platform/v8_inspector/JavaScriptCallFrame.h
index 24fc0eb9..6b75f42 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/JavaScriptCallFrame.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/JavaScriptCallFrame.h
@@ -64,10 +64,8 @@
     int callV8FunctionReturnInt(const char* name) const;
 
     v8::Isolate* m_isolate;
-    OwnPtr<JavaScriptCallFrame> m_caller;
     v8::Global<v8::Context> m_debuggerContext;
     v8::Global<v8::Object> m_callFrame;
-    v8::Global<v8::FunctionTemplate> m_wrapperTemplate;
 };
 
 using JavaScriptCallFrames = protocol::Vector<OwnPtr<JavaScriptCallFrame>>;
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8Console.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8Console.cpp
index be0f20b7..e1fc09eb 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8Console.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8Console.cpp
@@ -239,30 +239,14 @@
     }
 };
 
-v8::MaybeLocal<v8::Object> createObjectWithClassName(V8DebuggerImpl* debugger, v8::Local<v8::Context> context, const char* className)
-{
-    v8::Isolate* isolate = debugger->isolate();
-    v8::Local<v8::FunctionTemplate> functionTemplate;
-    String16 functionTemplateName = String16("V8Console#") + className;
-    if (!debugger->functionTemplate(functionTemplateName).ToLocal(&functionTemplate)) {
-        functionTemplate = v8::FunctionTemplate::New(isolate);
-        functionTemplate->SetClassName(toV8StringInternalized(isolate, className));
-        debugger->setFunctionTemplate(functionTemplateName, functionTemplate);
-    }
-    v8::Local<v8::Function> constructor;
-    if (!functionTemplate->GetFunction(context).ToLocal(&constructor))
-        return v8::MaybeLocal<v8::Object>();
-    return constructor->NewInstance(context, 0, nullptr);
-}
-
-void createBoundFunctionProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> obj, v8::Local<v8::Object> prototype, const char* name, v8::FunctionCallback callback)
+void createBoundFunctionProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> console, const char* name, v8::FunctionCallback callback)
 {
     v8::Local<v8::String> funcName = toV8StringInternalized(context->GetIsolate(), name);
     v8::Local<v8::Function> func;
-    if (!v8::Function::New(context, callback, obj).ToLocal(&func))
+    if (!v8::Function::New(context, callback, console).ToLocal(&func))
         return;
     func->SetName(funcName);
-    if (!prototype->Set(context, funcName, func).FromMaybe(false))
+    if (!console->Set(context, funcName, func).FromMaybe(false))
         return;
 }
 
@@ -632,46 +616,37 @@
     }
 }
 
-v8::MaybeLocal<v8::Object> V8Console::createConsole(InspectedContext* inspectedContext, bool hasMemoryAttribute)
+v8::Local<v8::Object> V8Console::createConsole(InspectedContext* inspectedContext, bool hasMemoryAttribute)
 {
     v8::Local<v8::Context> context = inspectedContext->context();
     v8::Isolate* isolate = context->GetIsolate();
     v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
 
-    v8::Local<v8::Object> console;
-    if (!createObjectWithClassName(inspectedContext->debugger(), context, "Console").ToLocal(&console))
-        return v8::MaybeLocal<v8::Object>();
+    v8::Local<v8::Object> console = v8::Object::New(isolate);
 
-    v8::Local<v8::Object> prototype;
-    if (!createObjectWithClassName(inspectedContext->debugger(), context, "ConsolePrototype").ToLocal(&prototype))
-        return v8::MaybeLocal<v8::Object>();
-
-    createBoundFunctionProperty(context, console, prototype, "debug", V8Console::debugCallback);
-    createBoundFunctionProperty(context, console, prototype, "error", V8Console::errorCallback);
-    createBoundFunctionProperty(context, console, prototype, "info", V8Console::infoCallback);
-    createBoundFunctionProperty(context, console, prototype, "log", V8Console::logCallback);
-    createBoundFunctionProperty(context, console, prototype, "warn", V8Console::warnCallback);
-    createBoundFunctionProperty(context, console, prototype, "dir", V8Console::dirCallback);
-    createBoundFunctionProperty(context, console, prototype, "dirxml", V8Console::dirxmlCallback);
-    createBoundFunctionProperty(context, console, prototype, "table", V8Console::tableCallback);
-    createBoundFunctionProperty(context, console, prototype, "trace", V8Console::traceCallback);
-    createBoundFunctionProperty(context, console, prototype, "group", V8Console::groupCallback);
-    createBoundFunctionProperty(context, console, prototype, "groupCollapsed", V8Console::groupCollapsedCallback);
-    createBoundFunctionProperty(context, console, prototype, "groupEnd", V8Console::groupEndCallback);
-    createBoundFunctionProperty(context, console, prototype, "clear", V8Console::clearCallback);
-    createBoundFunctionProperty(context, console, prototype, "count", V8Console::countCallback);
-    createBoundFunctionProperty(context, console, prototype, "assert", V8Console::assertCallback);
-    createBoundFunctionProperty(context, console, prototype, "markTimeline", V8Console::markTimelineCallback);
-    createBoundFunctionProperty(context, console, prototype, "profile", V8Console::profileCallback);
-    createBoundFunctionProperty(context, console, prototype, "profileEnd", V8Console::profileEndCallback);
-    createBoundFunctionProperty(context, console, prototype, "timeline", V8Console::timelineCallback);
-    createBoundFunctionProperty(context, console, prototype, "timelineEnd", V8Console::timelineEndCallback);
-    createBoundFunctionProperty(context, console, prototype, "time", V8Console::timeCallback);
-    createBoundFunctionProperty(context, console, prototype, "timeEnd", V8Console::timeEndCallback);
-    createBoundFunctionProperty(context, console, prototype, "timeStamp", V8Console::timeStampCallback);
-
-    if (!console->SetPrototype(context, prototype).FromMaybe(false))
-        return v8::MaybeLocal<v8::Object>();
+    createBoundFunctionProperty(context, console, "debug", V8Console::debugCallback);
+    createBoundFunctionProperty(context, console, "error", V8Console::errorCallback);
+    createBoundFunctionProperty(context, console, "info", V8Console::infoCallback);
+    createBoundFunctionProperty(context, console, "log", V8Console::logCallback);
+    createBoundFunctionProperty(context, console, "warn", V8Console::warnCallback);
+    createBoundFunctionProperty(context, console, "dir", V8Console::dirCallback);
+    createBoundFunctionProperty(context, console, "dirxml", V8Console::dirxmlCallback);
+    createBoundFunctionProperty(context, console, "table", V8Console::tableCallback);
+    createBoundFunctionProperty(context, console, "trace", V8Console::traceCallback);
+    createBoundFunctionProperty(context, console, "group", V8Console::groupCallback);
+    createBoundFunctionProperty(context, console, "groupCollapsed", V8Console::groupCollapsedCallback);
+    createBoundFunctionProperty(context, console, "groupEnd", V8Console::groupEndCallback);
+    createBoundFunctionProperty(context, console, "clear", V8Console::clearCallback);
+    createBoundFunctionProperty(context, console, "count", V8Console::countCallback);
+    createBoundFunctionProperty(context, console, "assert", V8Console::assertCallback);
+    createBoundFunctionProperty(context, console, "markTimeline", V8Console::markTimelineCallback);
+    createBoundFunctionProperty(context, console, "profile", V8Console::profileCallback);
+    createBoundFunctionProperty(context, console, "profileEnd", V8Console::profileEndCallback);
+    createBoundFunctionProperty(context, console, "timeline", V8Console::timelineCallback);
+    createBoundFunctionProperty(context, console, "timelineEnd", V8Console::timelineEndCallback);
+    createBoundFunctionProperty(context, console, "time", V8Console::timeCallback);
+    createBoundFunctionProperty(context, console, "timeEnd", V8Console::timeEndCallback);
+    createBoundFunctionProperty(context, console, "timeStamp", V8Console::timeStampCallback);
 
     if (hasMemoryAttribute)
         console->SetAccessorProperty(toV8StringInternalized(isolate, "memory"), v8::Function::New(isolate, V8Console::memoryGetterCallback, console), v8::Function::New(isolate, V8Console::memorySetterCallback), static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT);
@@ -688,27 +663,27 @@
 
     v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate);
 
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "dir", V8Console::dirCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "dirxml", V8Console::dirxmlCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "profile", V8Console::profileCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "profileEnd", V8Console::profileEndCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "clear", V8Console::clearCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "table", V8Console::tableCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "dir", V8Console::dirCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "dirxml", V8Console::dirxmlCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "profile", V8Console::profileCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "profileEnd", V8Console::profileEndCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "clear", V8Console::clearCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "table", V8Console::tableCallback);
 
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "keys", V8Console::keysCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "values", V8Console::valuesCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "debug", V8Console::debugFunctionCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "undebug", V8Console::undebugFunctionCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "monitor", V8Console::monitorFunctionCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "unmonitor", V8Console::unmonitorFunctionCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "inspect", V8Console::inspectCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "copy", V8Console::copyCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "$_", V8Console::lastEvaluationResultCallback);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "$0", V8Console::inspectedObject0);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "$1", V8Console::inspectedObject1);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "$2", V8Console::inspectedObject2);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "$3", V8Console::inspectedObject3);
-    createBoundFunctionProperty(context, commandLineAPI, commandLineAPI, "$4", V8Console::inspectedObject4);
+    createBoundFunctionProperty(context, commandLineAPI, "keys", V8Console::keysCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "values", V8Console::valuesCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "debug", V8Console::debugFunctionCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "undebug", V8Console::undebugFunctionCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "monitor", V8Console::monitorFunctionCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "unmonitor", V8Console::unmonitorFunctionCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "inspect", V8Console::inspectCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "copy", V8Console::copyCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "$_", V8Console::lastEvaluationResultCallback);
+    createBoundFunctionProperty(context, commandLineAPI, "$0", V8Console::inspectedObject0);
+    createBoundFunctionProperty(context, commandLineAPI, "$1", V8Console::inspectedObject1);
+    createBoundFunctionProperty(context, commandLineAPI, "$2", V8Console::inspectedObject2);
+    createBoundFunctionProperty(context, commandLineAPI, "$3", V8Console::inspectedObject3);
+    createBoundFunctionProperty(context, commandLineAPI, "$4", V8Console::inspectedObject4);
 
     commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, inspectedContext));
     return commandLineAPI;
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8Console.h b/third_party/WebKit/Source/platform/v8_inspector/V8Console.h
index 83489da..bd5fb8c 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8Console.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8Console.h
@@ -15,7 +15,7 @@
 // https://console.spec.whatwg.org/#console-interface
 class V8Console {
 public:
-    static v8::MaybeLocal<v8::Object> createConsole(InspectedContext*, bool hasMemoryAttribute);
+    static v8::Local<v8::Object> createConsole(InspectedContext*, bool hasMemoryAttribute);
     static v8::Local<v8::Object> createCommandLineAPI(InspectedContext*);
     static void clearInspectedContextIfNeeded(v8::Local<v8::Context>, v8::Local<v8::Object> console);
 
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
index 2b94263..ebedf0ed 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
@@ -295,14 +295,10 @@
         return;
 
     v8::HandleScope scope(m_isolate);
-    if (m_breakProgramCallbackTemplate.IsEmpty()) {
-        v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(m_isolate);
-        templ->SetCallHandler(&V8DebuggerImpl::breakProgramCallback, v8::External::New(m_isolate, this));
-        m_breakProgramCallbackTemplate.Reset(m_isolate, templ);
-    }
-
-    v8::Local<v8::Function> breakProgramFunction = v8::Local<v8::FunctionTemplate>::New(m_isolate, m_breakProgramCallbackTemplate)->GetFunction();
-    v8::Debug::Call(debuggerContext(), breakProgramFunction).ToLocalChecked();
+    v8::Local<v8::Function> breakFunction;
+    if (!v8::Function::New(m_isolate->GetCurrentContext(), &V8DebuggerImpl::breakProgramCallback, v8::External::New(m_isolate, this)).ToLocal(&breakFunction))
+        return;
+    v8::Debug::Call(debuggerContext(), breakFunction).ToLocalChecked();
 }
 
 void V8DebuggerImpl::continueProgram()
@@ -843,17 +839,4 @@
     return contextGroupId ? m_sessions.get(contextGroupId) : nullptr;
 }
 
-v8::MaybeLocal<v8::FunctionTemplate> V8DebuggerImpl::functionTemplate(const String16& name)
-{
-    if (!m_templates.contains(name))
-        return v8::MaybeLocal<v8::FunctionTemplate>();
-    return m_templates.get(name)->Get(m_isolate);
-}
-
-void V8DebuggerImpl::setFunctionTemplate(const String16& name, v8::Local<v8::FunctionTemplate> functionTemplate)
-{
-    OwnPtr<v8::Global<v8::FunctionTemplate>> global = adoptPtr(new v8::Global<v8::FunctionTemplate>(m_isolate, functionTemplate));
-    m_templates.set(name, global.release());
-}
-
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
index 654c120..ec39df7 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
@@ -121,9 +121,6 @@
     void disconnect(V8InspectorSessionImpl*);
     V8InspectorSessionImpl* sessionForContextGroup(int contextGroupId);
 
-    v8::MaybeLocal<v8::FunctionTemplate> functionTemplate(const String16& name);
-    void setFunctionTemplate(const String16& name, v8::Local<v8::FunctionTemplate>);
-
 private:
     void enable();
     void disable();
@@ -155,15 +152,12 @@
     SessionMap m_sessions;
     int m_enabledAgentsCount;
     bool m_breakpointsActivated;
-    v8::Global<v8::FunctionTemplate> m_breakProgramCallbackTemplate;
     v8::Global<v8::Object> m_debuggerScript;
     v8::Global<v8::Context> m_debuggerContext;
     v8::Local<v8::Object> m_executionState;
     v8::Local<v8::Context> m_pausedContext;
     bool m_runningNestedMessageLoop;
     v8::Global<v8::Context> m_regexContext;
-    using FunctionTemplateMap = protocol::HashMap<String16, OwnPtr<v8::Global<v8::FunctionTemplate>>>;
-    FunctionTemplateMap m_templates;
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8InjectedScriptHost.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8InjectedScriptHost.cpp
index b3244902..24d416d 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8InjectedScriptHost.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8InjectedScriptHost.cpp
@@ -172,7 +172,10 @@
 
     V8DebuggerClient* client = unwrapDebugger(info)->client();
     V8EventListenerInfoList listenerInfo;
+    // eventListeners call can produce message on ErrorEvent during lazy event listener compilation.
+    client->muteWarningsAndDeprecations();
     client->eventListeners(info[0], listenerInfo);
+    client->unmuteWarningsAndDeprecations();
 
     v8::Local<v8::Object> result = v8::Object::New(info.GetIsolate());
     protocol::HashSet<String16> types;
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index 60767e26..ab5b0e7 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -548,14 +548,8 @@
     // FIXME: Is this the right thing to do? Is there a way to avoid having
     // a local frame root that doesn't have a WebWidget? During initialization
     // there is no content to draw so this call serves no purpose.
-    if (WebLocalFrameImpl::fromFrame(frame) && WebLocalFrameImpl::fromFrame(frame)->frameWidget()) {
+    if (WebLocalFrameImpl::fromFrame(frame) && WebLocalFrameImpl::fromFrame(frame)->frameWidget())
         WebLocalFrameImpl::fromFrame(frame)->frameWidget()->scheduleAnimation();
-    } else {
-        // TODO(lfg): We need to keep this for now because we still have some
-        // WebViews who don't have a WebViewFrameWidget. This should be
-        // removed once the WebViewFrameWidget refactor is complete.
-        m_webView->scheduleAnimation();
-    }
 }
 
 IntRect ChromeClientImpl::viewportToScreen(const IntRect& rectInViewport, const Widget* widget) const
diff --git a/third_party/WebKit/Source/web/LinkHighlightImpl.cpp b/third_party/WebKit/Source/web/LinkHighlightImpl.cpp
index 416144b..f1f5464 100644
--- a/third_party/WebKit/Source/web/LinkHighlightImpl.cpp
+++ b/third_party/WebKit/Source/web/LinkHighlightImpl.cpp
@@ -332,6 +332,7 @@
 class LinkHighlightDisplayItemClientForTracking : public DisplayItemClient {
     String debugName() const final { return "LinkHighlight"; }
     LayoutRect visualRect() const final { return LayoutRect(); }
+    DISPLAY_ITEM_CACHE_STATUS_UNCACHEABLE_IMPLEMENTATION
 };
 
 void LinkHighlightImpl::updateGeometry()
diff --git a/third_party/WebKit/Source/web/PageOverlay.h b/third_party/WebKit/Source/web/PageOverlay.h
index e050a0a..c027ea2 100644
--- a/third_party/WebKit/Source/web/PageOverlay.h
+++ b/third_party/WebKit/Source/web/PageOverlay.h
@@ -79,6 +79,7 @@
 
 private:
     PageOverlay(WebViewImpl*, PageOverlay::Delegate*);
+    DISPLAY_ITEM_CACHE_STATUS_UNCACHEABLE_IMPLEMENTATION
 
     WebViewImpl* m_viewImpl;
     Persistent<PageOverlay::Delegate> m_delegate;
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index f49ee7a..cc5d4b7 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -311,9 +311,12 @@
 
 WebInputEventResult WebFrameWidgetImpl::handleInputEvent(const WebInputEvent& inputEvent)
 {
-
     TRACE_EVENT1("input", "WebFrameWidgetImpl::handleInputEvent", "type", inputTypeToName(inputEvent.type));
 
+    // Don't handle events once we've started shutting down.
+    if (!page())
+        return WebInputEventResult::NotHandled;
+
     // Report the event to be NOT processed by WebKit, so that the browser can handle it appropriately.
     if (m_ignoreInputEvents)
         return WebInputEventResult::NotHandled;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 107878a..dbce5b0 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -3064,6 +3064,31 @@
     return m_zoomLevel;
 }
 
+void WebViewImpl::propagateZoomToLocalFrameRoots(Frame* frame)
+{
+    if (frame->isLocalRoot()) {
+        LocalFrame* localFrame = toLocalFrame(frame);
+
+        if (!WebLocalFrameImpl::pluginContainerFromFrame(localFrame)) {
+            float zoomFactor = m_zoomFactorOverride ? m_zoomFactorOverride : static_cast<float>(zoomLevelToZoomFactor(m_zoomLevel));
+            if (m_zoomFactorForDeviceScaleFactor) {
+                if (m_compositorDeviceScaleFactorOverride) {
+                    // Adjust the page's DSF so that DevicePixelRatio becomes m_zoomFactorForDeviceScaleFactor.
+                    page()->setDeviceScaleFactor(m_zoomFactorForDeviceScaleFactor / m_compositorDeviceScaleFactorOverride);
+                    zoomFactor *= m_compositorDeviceScaleFactorOverride;
+                } else {
+                    page()->setDeviceScaleFactor(1.f);
+                    zoomFactor *= m_zoomFactorForDeviceScaleFactor;
+                }
+            }
+            localFrame->setPageZoomFactor(zoomFactor);
+        }
+    }
+
+    for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling())
+        propagateZoomToLocalFrameRoots(child);
+}
+
 double WebViewImpl::setZoomLevel(double zoomLevel)
 {
     if (zoomLevel < m_minimumZoomLevel)
@@ -3073,26 +3098,7 @@
     else
         m_zoomLevel = zoomLevel;
 
-    // TODO(nasko): Setting zoom level needs to be refactored to support
-    // out-of-process iframes. See https://crbug.com/528407.
-    if (mainFrame()->isWebRemoteFrame())
-        return m_zoomLevel;
-
-    LocalFrame* frame = mainFrameImpl()->frame();
-    if (!WebLocalFrameImpl::pluginContainerFromFrame(frame)) {
-        float zoomFactor = m_zoomFactorOverride ? m_zoomFactorOverride : static_cast<float>(zoomLevelToZoomFactor(m_zoomLevel));
-        if (m_zoomFactorForDeviceScaleFactor) {
-            if (m_compositorDeviceScaleFactorOverride) {
-                // Adjust the page's DSF so that DevicePixelRatio becomes m_zoomFactorForDeviceScaleFactor.
-                page()->setDeviceScaleFactor(m_zoomFactorForDeviceScaleFactor / m_compositorDeviceScaleFactorOverride);
-                zoomFactor *= m_compositorDeviceScaleFactorOverride;
-            } else {
-                page()->setDeviceScaleFactor(1.f);
-                zoomFactor *= m_zoomFactorForDeviceScaleFactor;
-            }
-        }
-        frame->setPageZoomFactor(zoomFactor);
-    }
+    propagateZoomToLocalFrameRoots(m_page->mainFrame());
 
     return m_zoomLevel;
 }
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index 27faa498..6c951ef 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -529,6 +529,7 @@
     InspectorOverlay* inspectorOverlay();
 
     void setPageScaleFactorAndLocation(float, const FloatPoint&);
+    void propagateZoomToLocalFrameRoots(Frame*);
 
     void scrollAndRescaleViewports(float scaleFactor, const IntPoint& mainFrameOrigin, const FloatPoint& visualViewportOrigin);
 
diff --git a/third_party/WebKit/public/blink_headers.gypi b/third_party/WebKit/public/blink_headers.gypi
index 5527ba0..812bc9c 100644
--- a/third_party/WebKit/public/blink_headers.gypi
+++ b/third_party/WebKit/public/blink_headers.gypi
@@ -102,7 +102,6 @@
       "platform/WebGestureCurve.h",
       "platform/WebGestureCurveTarget.h",
       "platform/WebGestureDevice.h",
-      "platform/WebGraphicsContext3D.h",
       "platform/WebGraphicsContext3DProvider.h",
       "platform/WebHTTPBody.h",
       "platform/WebHTTPHeaderVisitor.h",
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index 03e769e..c7a244b 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -44,7 +44,6 @@
 #include "WebGamepadListener.h"
 #include "WebGamepads.h"
 #include "WebGestureDevice.h"
-#include "WebGraphicsContext3D.h"
 #include "WebLocalizedString.h"
 #include "WebPlatformEventType.h"
 #include "WebSize.h"
diff --git a/third_party/WebKit/public/platform/WebCompositorSupport.h b/third_party/WebKit/public/platform/WebCompositorSupport.h
index cc91732..4b5146d1 100644
--- a/third_party/WebKit/public/platform/WebCompositorSupport.h
+++ b/third_party/WebKit/public/platform/WebCompositorSupport.h
@@ -42,7 +42,6 @@
 class WebContentLayerClient;
 class WebExternalTextureLayer;
 class WebExternalTextureLayerClient;
-class WebGraphicsContext3D;
 class WebImageLayer;
 class WebLayer;
 class WebScrollbarLayer;
diff --git a/third_party/WebKit/public/platform/WebGraphicsContext3D.h b/third_party/WebKit/public/platform/WebGraphicsContext3D.h
deleted file mode 100644
index 134146e..0000000
--- a/third_party/WebKit/public/platform/WebGraphicsContext3D.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. 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.
- */
-
-#ifndef WebGraphicsContext3D_h
-#define WebGraphicsContext3D_h
-
-#include "WebNonCopyable.h"
-
-namespace blink {
-class WebGraphicsContext3D : public WebNonCopyable {
-public:
-    virtual ~WebGraphicsContext3D() { }
-};
-
-} // namespace blink
-
-#endif
diff --git a/third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h b/third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h
index c2e18ad..d02e849 100644
--- a/third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h
+++ b/third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h
@@ -44,13 +44,11 @@
 }
 
 namespace blink {
-class WebGraphicsContext3D;
 
 class WebGraphicsContext3DProvider {
 public:
     virtual ~WebGraphicsContext3DProvider() { }
 
-    virtual WebGraphicsContext3D* context3d() = 0;
     virtual gpu::gles2::GLES2Interface* contextGL() = 0;
     virtual GrContext* grContext() = 0;
     virtual gpu::Capabilities getCapabilities() = 0;
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom b/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom
index 4def9bb3..469e6b3 100644
--- a/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom
+++ b/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom
@@ -75,6 +75,11 @@
   MULTIPLE
 };
 
+struct WebBluetoothRemoteGATTService {
+  string instance_id;
+  string uuid;
+};
+
 struct WebBluetoothRemoteGATTCharacteristic {
   string instance_id;
   string uuid;
@@ -92,6 +97,14 @@
   // a device disconnects.
   SetClient(associated WebBluetoothServiceClient client);
 
+  // Returns the first GATT Service with |service_uuid| of a Bluetooth Device
+  // with |device_id|.
+  RemoteServerGetPrimaryService(
+    string device_id,
+    string service_uuid) => (
+      WebBluetoothError error,
+      WebBluetoothRemoteGATTService? service);
+
   // Returns the Characteristics of a GATT Service with |service_instance_id|.
   // If |quantity| == WebBluetoothGATTQueryQuantity::SINGLE, only one
   // characteristic will be returned.
diff --git a/third_party/WebKit/public/web/WebViewClient.h b/third_party/WebKit/public/web/WebViewClient.h
index e98ed97a..a41ebe62 100644
--- a/third_party/WebKit/public/web/WebViewClient.h
+++ b/third_party/WebKit/public/web/WebViewClient.h
@@ -32,7 +32,6 @@
 #define WebViewClient_h
 
 #include "../platform/WebDragOperation.h"
-#include "../platform/WebGraphicsContext3D.h"
 #include "../platform/WebPageVisibilityState.h"
 #include "../platform/WebString.h"
 #include "WebAXEnums.h"
diff --git a/third_party/google_toolbox_for_mac/BUILD.gn b/third_party/google_toolbox_for_mac/BUILD.gn
index 49fa91c..b0461d0a 100644
--- a/third_party/google_toolbox_for_mac/BUILD.gn
+++ b/third_party/google_toolbox_for_mac/BUILD.gn
@@ -202,5 +202,6 @@
     # chrome binary doesn't end up with unnecessarily exported
     # symbols.
     configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+    configs += [ "//build/config/gcc:symbol_visibility_default" ]
   }
 }
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index 81d4f65a9d..d86da33c 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -190,6 +190,7 @@
         current_cpu != "arm" && current_cpu != "mipsel") {
       deps += [ "//build/linux:freetype2" ]
       configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+      configs += [ "//build/config/gcc:symbol_visibility_default" ]
       configs += [ "//build/config/linux:glib" ]
       sources += [
         "src/hb-ft.cc",
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index e82835a..8be0c223 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Wednesday April 20 2016
+Date: Wednesday April 27 2016
 Branch: master
-Commit: 83f17eeede83f138f34177121019798719d5be1c
+Commit: b2ccb9c189069d45d201c988184e9e0796b96270
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
index 9dea1ed..11a1809b 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
@@ -421,7 +421,8 @@
 RTCD_EXTERN void (*vpx_lpf_vertical_8_dual)(uint8_t *s, int pitch, const uint8_t *blimit0, const uint8_t *limit0, const uint8_t *thresh0, const uint8_t *blimit1, const uint8_t *limit1, const uint8_t *thresh1);
 
 void vpx_minmax_8x8_c(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
-#define vpx_minmax_8x8 vpx_minmax_8x8_c
+void vpx_minmax_8x8_neon(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
+RTCD_EXTERN void (*vpx_minmax_8x8)(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
 
 unsigned int vpx_mse16x16_c(const uint8_t *src_ptr, int  source_stride, const uint8_t *ref_ptr, int  recon_stride, unsigned int *sse);
 unsigned int vpx_mse16x16_media(const uint8_t *src_ptr, int  source_stride, const uint8_t *ref_ptr, int  recon_stride, unsigned int *sse);
@@ -971,6 +972,8 @@
     if (flags & HAS_NEON) vpx_lpf_vertical_8 = vpx_lpf_vertical_8_neon;
     vpx_lpf_vertical_8_dual = vpx_lpf_vertical_8_dual_c;
     if (flags & HAS_NEON) vpx_lpf_vertical_8_dual = vpx_lpf_vertical_8_dual_neon;
+    vpx_minmax_8x8 = vpx_minmax_8x8_c;
+    if (flags & HAS_NEON) vpx_minmax_8x8 = vpx_minmax_8x8_neon;
     vpx_mse16x16 = vpx_mse16x16_media;
     if (flags & HAS_NEON) vpx_mse16x16 = vpx_mse16x16_neon;
     vpx_sad16x16 = vpx_sad16x16_media;
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
index 372cca6..0562fc9 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
@@ -421,7 +421,8 @@
 #define vpx_lpf_vertical_8_dual vpx_lpf_vertical_8_dual_neon
 
 void vpx_minmax_8x8_c(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
-#define vpx_minmax_8x8 vpx_minmax_8x8_c
+void vpx_minmax_8x8_neon(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
+#define vpx_minmax_8x8 vpx_minmax_8x8_neon
 
 unsigned int vpx_mse16x16_c(const uint8_t *src_ptr, int  source_stride, const uint8_t *ref_ptr, int  recon_stride, unsigned int *sse);
 unsigned int vpx_mse16x16_media(const uint8_t *src_ptr, int  source_stride, const uint8_t *ref_ptr, int  recon_stride, unsigned int *sse);
diff --git a/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
index e9161fb1..faa55bf 100644
--- a/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
@@ -415,7 +415,8 @@
 #define vpx_lpf_vertical_8_dual vpx_lpf_vertical_8_dual_c
 
 void vpx_minmax_8x8_c(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
-#define vpx_minmax_8x8 vpx_minmax_8x8_c
+void vpx_minmax_8x8_neon(const uint8_t *s, int p, const uint8_t *d, int dp, int *min, int *max);
+#define vpx_minmax_8x8 vpx_minmax_8x8_neon
 
 unsigned int vpx_mse16x16_c(const uint8_t *src_ptr, int  source_stride, const uint8_t *ref_ptr, int  recon_stride, unsigned int *sse);
 unsigned int vpx_mse16x16_neon(const uint8_t *src_ptr, int  source_stride, const uint8_t *ref_ptr, int  recon_stride, unsigned int *sse);
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 8dc9c85..53ac4757 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -1,7 +1,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  5
 #define VERSION_PATCH  0
-#define VERSION_EXTRA  "738-g83f17ee"
+#define VERSION_EXTRA  "794-gb2ccb9c"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.5.0-738-g83f17ee"
-#define VERSION_STRING      " v1.5.0-738-g83f17ee"
+#define VERSION_STRING_NOSP "v1.5.0-794-gb2ccb9c"
+#define VERSION_STRING      " v1.5.0-794-gb2ccb9c"
diff --git a/third_party/mesa/BUILD.gn b/third_party/mesa/BUILD.gn
index e23f64b..30623a7 100644
--- a/third_party/mesa/BUILD.gn
+++ b/third_party/mesa/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/ui.gni")
+import("//build/config/compiler/compiler.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 
 config("mesa_headers_config") {
@@ -144,6 +145,11 @@
       "/wd4345",  # POD-type default initializers.
       "/wd4311",  # Pointer truncation TODO(brucedawson): http://crbug.com/554200
     ]
+    if (is_official_build && full_wpo_on_official) {
+      # TODO(sebmarchand): http://crbug.com/604838 This should be removed if
+      # Mesa is ever rolled and the warning is fixed.
+      cflags += [ "/wd4723" ]  # potential divide by 0.
+    }
   }
 }
 
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 518877c5..0ca92a7 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -18,7 +18,6 @@
     "iron-icon": "PolymerElements/iron-icon#^1.0.0",
     "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
     "iron-icons": "PolymerElements/iron-icons#^1.0.0",
-    "iron-image": "PolymerElements/iron-image#^1.0.0",
     "iron-input": "PolymerElements/iron-input#^1.0.0",
     "iron-list": "PolymerElements/iron-list#^1.0.0",
     "iron-location": "PolymerElements/iron-location#^0.8.1",
@@ -37,7 +36,6 @@
     "neon-animation": "PolymerElements/neon-animation#^1.1.0",
     "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
     "paper-button": "PolymerElements/paper-button#^1.0.0",
-    "paper-card": "PolymerElements/paper-card",
     "paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
     "paper-dialog-behavior": "PolymerElements/paper-dialog-behavior#^1.0.0",
     "paper-dialog": "PolymerElements/paper-dialog#^1.0.0",
@@ -62,7 +60,6 @@
     "paper-tabs": "PolymerElements/paper-tabs#^1.0.0",
     "paper-toggle-button": "PolymerElements/paper-toggle-button#^1.0.0",
     "paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
-    "paper-tooltip": "PolymerElements/paper-tooltip#^1.0.0",
     "polymer-externs": "PolymerLabs/polymer-externs#^1.0.0",
     "polymer": "Polymer/polymer#^v1.0.0",
     "web-animations-js": "web-animations/web-animations-js#^2.0.0"
diff --git a/third_party/polymer/v1_0/components-chromium/iron-image/bower.json b/third_party/polymer/v1_0/components-chromium/iron-image/bower.json
deleted file mode 100644
index 9ac7b5d6..0000000
--- a/third_party/polymer/v1_0/components-chromium/iron-image/bower.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "name": "iron-image",
-  "version": "1.2.3",
-  "license": "http://polymer.github.io/LICENSE.txt",
-  "description": "An image-displaying element with lots of convenient features",
-  "private": true,
-  "authors": [
-    "The Polymer Authors"
-  ],
-  "keywords": [
-    "web-components",
-    "polymer",
-    "media"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "git://github.com/PolymerElements/iron-image.git"
-  },
-  "dependencies": {
-    "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
-    "polymer": "Polymer/polymer#^1.1.0"
-  },
-  "devDependencies": {
-    "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
-    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
-    "paper-styles": "PolymerElements/paper-styles#^1.0.4",
-    "test-fixture": "PolymerElements/test-fixture#^1.0.0",
-    "web-component-tester": "^4.0.0",
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
-  },
-  "main": "iron-image.html",
-  "ignore": []
-}
diff --git a/third_party/polymer/v1_0/components-chromium/iron-image/compiled_resources2.gyp b/third_party/polymer/v1_0/components-chromium/iron-image/compiled_resources2.gyp
deleted file mode 100644
index 3d922f8..0000000
--- a/third_party/polymer/v1_0/components-chromium/iron-image/compiled_resources2.gyp
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2016 The Chromium 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: Created with generate_compiled_resources_gyp.py, please do not edit.
-{
-  'targets': [
-    {
-      'target_name': 'iron-image-extracted',
-      'includes': ['../../../../closure_compiler/compile_js2.gypi'],
-    },
-  ],
-}
diff --git a/third_party/polymer/v1_0/components-chromium/iron-image/iron-image-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-image/iron-image-extracted.js
deleted file mode 100644
index b8cf4c7..0000000
--- a/third_party/polymer/v1_0/components-chromium/iron-image/iron-image-extracted.js
+++ /dev/null
@@ -1,274 +0,0 @@
-Polymer({
-      is: 'iron-image',
-
-      properties: {
-        /**
-         * The URL of an image.
-         */
-        src: {
-          observer: '_srcChanged',
-          type: String,
-          value: ''
-        },
-
-        /**
-         * A short text alternative for the image.
-         */
-        alt: {
-          type: String,
-          value: null
-        },
-
-        /**
-         * When true, the image is prevented from loading and any placeholder is
-         * shown.  This may be useful when a binding to the src property is known to
-         * be invalid, to prevent 404 requests.
-         */
-        preventLoad: {
-          type: Boolean,
-          value: false,
-          observer: '_preventLoadChanged'
-        },
-
-        /**
-         * Sets a sizing option for the image.  Valid values are `contain` (full
-         * aspect ratio of the image is contained within the element and
-         * letterboxed) or `cover` (image is cropped in order to fully cover the
-         * bounds of the element), or `null` (default: image takes natural size).
-         */
-        sizing: {
-          type: String,
-          value: null,
-          reflectToAttribute: true
-        },
-
-        /**
-         * When a sizing option is used (`cover` or `contain`), this determines
-         * how the image is aligned within the element bounds.
-         */
-        position: {
-          type: String,
-          value: 'center'
-        },
-
-        /**
-         * When `true`, any change to the `src` property will cause the `placeholder`
-         * image to be shown until the new image has loaded.
-         */
-        preload: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * This image will be used as a background/placeholder until the src image has
-         * loaded.  Use of a data-URI for placeholder is encouraged for instant rendering.
-         */
-        placeholder: {
-          type: String,
-          value: null,
-          observer: '_placeholderChanged'
-        },
-
-        /**
-         * When `preload` is true, setting `fade` to true will cause the image to
-         * fade into place.
-         */
-        fade: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * Read-only value that is true when the image is loaded.
-         */
-        loaded: {
-          notify: true,
-          readOnly: true,
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * Read-only value that tracks the loading state of the image when the `preload`
-         * option is used.
-         */
-        loading: {
-          notify: true,
-          readOnly: true,
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * Read-only value that indicates that the last set `src` failed to load.
-         */
-        error: {
-          notify: true,
-          readOnly: true,
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * Can be used to set the width of image (e.g. via binding); size may also be
-         * set via CSS.
-         */
-        width: {
-          observer: '_widthChanged',
-          type: Number,
-          value: null
-        },
-
-        /**
-         * Can be used to set the height of image (e.g. via binding); size may also be
-         * set via CSS.
-         *
-         * @attribute height
-         * @type number
-         * @default null
-         */
-        height: {
-          observer: '_heightChanged',
-          type: Number,
-          value: null
-        },
-      },
-
-      observers: [
-        '_transformChanged(sizing, position)'
-      ],
-
-      ready: function() {
-        var img = this.$.img;
-
-        img.onload = function() {
-          if (this.$.img.src !== this._resolveSrc(this.src)) return;
-
-          this._setLoading(false);
-          this._setLoaded(true);
-          this._setError(false);
-        }.bind(this);
-
-        img.onerror = function() {
-          if (this.$.img.src !== this._resolveSrc(this.src)) return;
-
-          this._reset();
-
-          this._setLoading(false);
-          this._setLoaded(false);
-          this._setError(true);
-        }.bind(this);
-
-        this._resolvedSrc = '';
-      },
-
-      _load: function(src) {
-        if (src) {
-          this.$.img.src = src;
-        } else {
-          this.$.img.removeAttribute('src');
-        }
-        this.$.sizedImgDiv.style.backgroundImage = src ? 'url("' + src + '")' : '';
-
-        this._setLoading(!!src);
-        this._setLoaded(false);
-        this._setError(false);
-      },
-
-      _reset: function() {
-        this.$.img.removeAttribute('src');
-        this.$.sizedImgDiv.style.backgroundImage = '';
-
-        this._setLoading(false);
-        this._setLoaded(false);
-        this._setError(false);
-      },
-
-      _computePlaceholderHidden: function() {
-        return !this.preload || (!this.fade && !this.loading && this.loaded);
-      },
-
-      _computePlaceholderClassName: function() {
-        return (this.preload && this.fade && !this.loading && this.loaded) ? 'faded-out' : '';
-      },
-
-      _computeImgDivHidden: function() {
-        return !this.sizing;
-      },
-
-      _computeImgDivARIAHidden: function() {
-        return this.alt === '' ? 'true' : undefined;
-      },
-
-      _computeImgDivARIALabel: function() {
-        if (this.alt !== null) {
-          return this.alt;
-        }
-
-        // Polymer.ResolveUrl.resolveUrl will resolve '' relative to a URL x to
-        // that URL x, but '' is the default for src.
-        if (this.src === '') {
-          return '';
-        }
-
-        var pathComponents = (new URL(this._resolveSrc(this.src))).pathname.split("/");
-        return pathComponents[pathComponents.length - 1];
-      },
-
-      _computeImgHidden: function() {
-        return !!this.sizing;
-      },
-
-      _widthChanged: function() {
-        this.style.width = isNaN(this.width) ? this.width : this.width + 'px';
-      },
-
-      _heightChanged: function() {
-        this.style.height = isNaN(this.height) ? this.height : this.height + 'px';
-      },
-
-      _preventLoadChanged: function() {
-        if (this.preventLoad || this.loaded) return;
-
-        this._reset();
-        this._load(this.src);
-      },
-
-      _srcChanged: function(newSrc, oldSrc) {
-        var newResolvedSrc = this._resolveSrc(newSrc);
-        if (newResolvedSrc === this._resolvedSrc) return;
-        this._resolvedSrc = newResolvedSrc;
-
-        this._reset();
-        if (!this.preventLoad) {
-          this._load(newSrc);
-        }
-      },
-
-      _placeholderChanged: function() {
-        this.$.placeholder.style.backgroundImage =
-          this.placeholder ? 'url("' + this.placeholder + '")' : '';
-      },
-
-      _transformChanged: function() {
-        var sizedImgDivStyle = this.$.sizedImgDiv.style;
-        var placeholderStyle = this.$.placeholder.style;
-
-        sizedImgDivStyle.backgroundSize =
-        placeholderStyle.backgroundSize =
-          this.sizing;
-
-        sizedImgDivStyle.backgroundPosition =
-        placeholderStyle.backgroundPosition =
-          this.sizing ? this.position : '';
-
-        sizedImgDivStyle.backgroundRepeat =
-        placeholderStyle.backgroundRepeat =
-          this.sizing ? 'no-repeat' : '';
-      },
-
-      _resolveSrc: function(testSrc) {
-        return Polymer.ResolveUrl.resolveUrl(testSrc, this.ownerDocument.baseURI);
-      }
-    });
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/iron-image/iron-image.html b/third_party/polymer/v1_0/components-chromium/iron-image/iron-image.html
deleted file mode 100644
index 5c8e3a0f..0000000
--- a/third_party/polymer/v1_0/components-chromium/iron-image/iron-image.html
+++ /dev/null
@@ -1,120 +0,0 @@
-<!--
-@license
-Copyright (c) 2015 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="../iron-flex-layout/iron-flex-layout.html">
-
-<!--
-`iron-image` is an element for displaying an image that provides useful sizing and
-preloading options not found on the standard `<img>` tag.
-
-The `sizing` option allows the image to be either cropped (`cover`) or
-letterboxed (`contain`) to fill a fixed user-size placed on the element.
-
-The `preload` option prevents the browser from rendering the image until the
-image is fully loaded.  In the interim, either the element's CSS `background-color`
-can be be used as the placeholder, or the `placeholder` property can be
-set to a URL (preferably a data-URI, for instant rendering) for an
-placeholder image.
-
-The `fade` option (only valid when `preload` is set) will cause the placeholder
-image/color to be faded out once the image is rendered.
-
-Examples:
-
-  Basically identical to `<img src="...">` tag:
-
-    <iron-image src="http://lorempixel.com/400/400"></iron-image>
-
-  Will letterbox the image to fit:
-
-    <iron-image style="width:400px; height:400px;" sizing="contain"
-      src="http://lorempixel.com/600/400"></iron-image>
-
-  Will crop the image to fit:
-
-    <iron-image style="width:400px; height:400px;" sizing="cover"
-      src="http://lorempixel.com/600/400"></iron-image>
-
-  Will show light-gray background until the image loads:
-
-    <iron-image style="width:400px; height:400px; background-color: lightgray;"
-      sizing="cover" preload src="http://lorempixel.com/600/400"></iron-image>
-
-  Will show a base-64 encoded placeholder image until the image loads:
-
-    <iron-image style="width:400px; height:400px;" placeholder="data:image/gif;base64,..."
-      sizing="cover" preload src="http://lorempixel.com/600/400"></iron-image>
-
-  Will fade the light-gray background out once the image is loaded:
-
-    <iron-image style="width:400px; height:400px; background-color: lightgray;"
-      sizing="cover" preload fade src="http://lorempixel.com/600/400"></iron-image>
-
-Custom property | Description | Default
-----------------|-------------|----------
-`--iron-image-placeholder` | Mixin applied to #placeholder | `{}`
-`--iron-image-width` | Sets the width of the wrapped image | `auto`
-`--iron-image-height` | Sets the height of the wrapped image | `auto`
-
-@group Iron Elements
-@element iron-image
-@demo demo/index.html
--->
-
-</head><body><dom-module id="iron-image">
-  <template>
-    <style>
-      :host {
-        display: inline-block;
-        overflow: hidden;
-        position: relative;
-      }
-
-      #sizedImgDiv {
-        @apply(--layout-fit);
-
-        display: none;
-      }
-
-      #img {
-        display: block;
-        width: var(--iron-image-width, auto);
-        height: var(--iron-image-height, auto);
-      }
-
-      :host([sizing]) #sizedImgDiv {
-        display: block;
-      }
-
-      :host([sizing]) #img {
-        display: none;
-      }
-
-      #placeholder {
-        @apply(--layout-fit);
-
-        background-color: inherit;
-        opacity: 1;
-
-        @apply(--iron-image-placeholder);
-      }
-
-      #placeholder.faded-out {
-        transition: opacity 0.5s linear;
-        opacity: 0;
-      }
-    </style>
-
-    <div id="sizedImgDiv" role="img" hidden$="[[_computeImgDivHidden(sizing)]]" aria-hidden$="[[_computeImgDivARIAHidden(alt)]]" aria-label$="[[_computeImgDivARIALabel(alt, src)]]"></div>
-    <img id="img" alt$="[[alt]]" hidden$="[[_computeImgHidden(sizing)]]">
-    <div id="placeholder" hidden$="[[_computePlaceholderHidden(preload, fade, loading, loaded)]]" class$="[[_computePlaceholderClassName(preload, fade, loading, loaded)]]"></div>
-  </template>
-
-  </dom-module>
-<script src="iron-image-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-card/bower.json b/third_party/polymer/v1_0/components-chromium/paper-card/bower.json
deleted file mode 100644
index b17e3fa..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-card/bower.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "name": "paper-card",
-  "version": "1.1.1",
-  "description": "Material design piece of paper with unique related data",
-  "authors": [
-    "The Polymer Authors"
-  ],
-  "keywords": [
-    "web-components",
-    "polymer",
-    "card"
-  ],
-  "main": "paper-card.html",
-  "private": true,
-  "repository": {
-    "type": "git",
-    "url": "git://github.com/PolymerElements/paper-card.git"
-  },
-  "license": "http://polymer.github.io/LICENSE.txt",
-  "homepage": "https://github.com/PolymerElements/paper-card",
-  "ignore": [],
-  "dependencies": {
-    "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
-    "iron-image": "PolymerElements/iron-image#^1.2.0",
-    "paper-material": "PolymerElements/paper-material#^1.0.0",
-    "paper-styles": "PolymerElements/paper-styles#^1.1.0",
-    "polymer": "Polymer/polymer#^1.1.0"
-  },
-  "devDependencies": {
-    "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
-    "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
-    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
-    "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
-    "paper-button": "PolymerElements/paper-button#^1.0.0",
-    "paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
-    "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
-    "test-fixture": "PolymerElements/test-fixture#^1.0.0",
-    "web-component-tester": "^4.0.0",
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
-  }
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-card/compiled_resources2.gyp b/third_party/polymer/v1_0/components-chromium/paper-card/compiled_resources2.gyp
deleted file mode 100644
index 3dd29db..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-card/compiled_resources2.gyp
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2016 The Chromium 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: Created with generate_compiled_resources_gyp.py, please do not edit.
-{
-  'targets': [
-    {
-      'target_name': 'paper-card-extracted',
-      'dependencies': [
-        '../iron-image/compiled_resources2.gyp:iron-image-extracted',
-        '../paper-material/compiled_resources2.gyp:paper-material-extracted',
-      ],
-      'includes': ['../../../../closure_compiler/compile_js2.gypi'],
-    },
-  ],
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-card/paper-card-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-card/paper-card-extracted.js
deleted file mode 100644
index 55a5c9e..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-card/paper-card-extracted.js
+++ /dev/null
@@ -1,85 +0,0 @@
-Polymer({
-      is: 'paper-card',
-
-      properties: {
-        /**
-         * The title of the card.
-         */
-        heading: {
-          type: String,
-          value: '',
-          observer: '_headingChanged'
-        },
-
-        /**
-         * The url of the title image of the card.
-         */
-        image: {
-          type: String,
-          value: ''
-        },
-
-        /**
-         * When `true`, any change to the image url property will cause the
-         * `placeholder` image to be shown until the image is fully rendered.
-         */
-        preloadImage: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * When `preloadImage` is true, setting `fadeImage` to true will cause the
-         * image to fade into place.
-         */
-        fadeImage: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * The z-depth of the card, from 0-5.
-         */
-        elevation: {
-          type: Number,
-          value: 1,
-          reflectToAttribute: true
-        },
-
-        /**
-         * Set this to true to animate the card shadow when setting a new
-         * `z` value.
-         */
-        animatedShadow: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * Read-only property used to pass down the `animatedShadow` value to
-         * the underlying paper-material style (since they have different names).
-         */
-        animated: {
-          type: Boolean,
-          reflectToAttribute: true,
-          readOnly: true,
-          computed: '_computeAnimated(animatedShadow)'
-        }
-      },
-
-      _headingChanged: function(heading) {
-        var label = this.getAttribute('aria-label');
-        this.setAttribute('aria-label', heading);
-      },
-
-      _computeHeadingClass: function(image) {
-        var cls = 'title-text';
-        if (image)
-          cls += ' over-image';
-        return cls;
-      },
-
-      _computeAnimated: function(animatedShadow) {
-        return animatedShadow;
-      }
-    });
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-card/paper-card.html b/third_party/polymer/v1_0/components-chromium/paper-card/paper-card.html
deleted file mode 100644
index 932fb30d..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-card/paper-card.html
+++ /dev/null
@@ -1,137 +0,0 @@
-<!--
-@license
-Copyright (c) 2015 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="../iron-flex-layout/iron-flex-layout.html">
-<link rel="import" href="../iron-image/iron-image.html">
-<link rel="import" href="../paper-material/paper-material.html">
-<link rel="import" href="../paper-styles/default-theme.html">
-
-<!--
-Material design: [Cards](https://www.google.com/design/spec/components/cards.html)
-
-`paper-card` is a container with a drop shadow.
-
-Example:
-
-    <paper-card heading="Card Title">
-      <div class="card-content">Some content</div>
-      <div class="card-actions">
-        <paper-button>Some action</paper-button>
-      </div>
-    </paper-card>
-
-Example - top card image:
-
-    <paper-card heading="Card Title" image="/path/to/image.png">
-      ...
-    </paper-card>
-
-### Accessibility
-
-By default, the `aria-label` will be set to the value of the `heading` attribute.
-
-### Styling
-
-The following custom properties and mixins are available for styling:
-
-Custom property | Description | Default
-----------------|-------------|----------
-`--paper-card-background-color` | The background color of the card | `--primary-background-color`
-`--paper-card-header-color` | The color of the header text | `#000`
-`--paper-card-header` | Mixin applied to the card header section | `{}`
-`--paper-card-header-text` | Mixin applied to the title in the card header section | `{}`
-`--paper-card-header-image` | Mixin applied to the image in the card header section | `{}`
-`--paper-card-header-image-text` | Mixin applied to the text overlapping the image in the card header section | `{}`
-`--paper-card-content` | Mixin applied to the card content section| `{}`
-`--paper-card-actions` | Mixin applied to the card action section | `{}`
-`--paper-card` | Mixin applied to the card | `{}`
-
-@group Paper Elements
-@element paper-card
-@demo demo/index.html
--->
-
-</head><body><dom-module id="paper-card">
-  <template>
-    <style include="paper-material">
-      :host {
-        display: inline-block;
-        position: relative;
-        box-sizing: border-box;
-        background-color: var(--paper-card-background-color, --primary-background-color);
-        border-radius: 2px;
-
-        @apply(--paper-font-common-base);
-        @apply(--paper-card);
-      }
-
-      /* IE 10 support for HTML5 hidden attr */
-      [hidden] {
-        display: none !important;
-      }
-
-      .header {
-        position: relative;
-        border-top-left-radius: inherit;
-        border-top-right-radius: inherit;
-        overflow: hidden;
-
-        @apply(--paper-card-header);
-      }
-
-      .header iron-image {
-        width: 100%;
-        --iron-image-width: 100%;
-        pointer-events: none;
-
-        @apply(--paper-card-header-image);
-      }
-
-      .header .title-text {
-        padding: 16px;
-        font-size: 24px;
-        font-weight: 400;
-        color: var(--paper-card-header-color, #000);
-
-        @apply(--paper-card-header-text);
-      }
-
-      .header .title-text.over-image {
-        position: absolute;
-        bottom: 0px;
-
-        @apply(--paper-card-header-image-text);
-      }
-
-      :host ::content .card-content {
-        padding: 16px;
-        position:relative;
-
-        @apply(--paper-card-content);
-      }
-
-      :host ::content .card-actions {
-        border-top: 1px solid #e8e8e8;
-        padding: 5px 16px;
-        position:relative;
-
-        @apply(--paper-card-actions);
-      }
-    </style>
-
-    <div class="header">
-      <iron-image hidden$="[[!image]]" src="[[image]]" preload$="[[preloadImage]]" fade$="[[fadeImage]]"></iron-image>
-      <div hidden$="[[!heading]]" class$="[[_computeHeadingClass(image)]]">[[heading]]</div>
-    </div>
-
-    <content></content>
-  </template>
-
-  </dom-module>
-<script src="paper-card-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-tooltip/bower.json b/third_party/polymer/v1_0/components-chromium/paper-tooltip/bower.json
deleted file mode 100644
index 423c88f..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-tooltip/bower.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
-  "name": "paper-tooltip",
-  "version": "1.1.2",
-  "description": "Material design tooltip popup for content",
-  "authors": [
-    "The Polymer Authors"
-  ],
-  "keywords": [
-    "web-components",
-    "polymer",
-    "tooltip"
-  ],
-  "main": "paper-tooltip.html",
-  "private": true,
-  "repository": {
-    "type": "git",
-    "url": "git://github.com/PolymerElements/paper-tooltip.git"
-  },
-  "license": "http://polymer.github.io/LICENSE.txt",
-  "homepage": "https://github.com/PolymerElements/paper-tooltip",
-  "ignore": [],
-  "dependencies": {
-    "polymer": "Polymer/polymer#^1.1.0",
-    "paper-styles": "PolymerElements/paper-styles#^1.0.0",
-    "neon-animation": "PolymerElements/neon-animation#^1.0.0"
-  },
-  "devDependencies": {
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
-    "web-component-tester": "^4.0.0",
-    "test-fixture": "PolymerElements/test-fixture#^1.0.0",
-    "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
-    "iron-demo-helpers": "polymerelements/iron-demo-helpers#^1.0.0",
-    "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
-    "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0"
-  }
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-tooltip/compiled_resources2.gyp b/third_party/polymer/v1_0/components-chromium/paper-tooltip/compiled_resources2.gyp
deleted file mode 100644
index 7a9d239..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-tooltip/compiled_resources2.gyp
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2016 The Chromium 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: Created with generate_compiled_resources_gyp.py, please do not edit.
-{
-  'targets': [
-    {
-      'target_name': 'paper-tooltip-extracted',
-      'dependencies': [
-        '../neon-animation/animations/compiled_resources2.gyp:fade-in-animation-extracted',
-        '../neon-animation/animations/compiled_resources2.gyp:fade-out-animation-extracted',
-        '../neon-animation/compiled_resources2.gyp:neon-animation-runner-behavior-extracted',
-      ],
-      'includes': ['../../../../closure_compiler/compile_js2.gypi'],
-    },
-  ],
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip-extracted.js
deleted file mode 100644
index ed84ac2..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip-extracted.js
+++ /dev/null
@@ -1,269 +0,0 @@
-Polymer({
-      is: 'paper-tooltip',
-
-      hostAttributes: {
-        role: 'tooltip',
-        tabindex: -1
-      },
-
-      behaviors: [
-        Polymer.NeonAnimationRunnerBehavior
-      ],
-
-      properties: {
-        /**
-         * The id of the element that the tooltip is anchored to. This element
-         * must be a sibling of the tooltip.
-         */
-        for: {
-          type: String,
-          observer: '_forChanged'
-        },
-
-        /**
-         * Set this to true if you want to manually control when the tooltip
-         * is shown or hidden.
-         */
-        manualMode: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * Positions the tooltip to the top, right, bottom, left of its content.
-         */
-        position: {
-          type: String,
-          value: 'bottom'
-        },
-
-        /**
-         * If true, no parts of the tooltip will ever be shown offscreen.
-         */
-        fitToVisibleBounds: {
-          type: Boolean,
-          value: false
-        },
-
-        /**
-         * The spacing between the top of the tooltip and the element it is
-         * anchored to.
-         */
-        offset: {
-          type: Number,
-          value: 14
-        },
-
-        /**
-         * This property is deprecated, but left over so that it doesn't
-         * break exiting code. Please use `offset` instead. If both `offset` and
-         * `marginTop` are provided, `marginTop` will be ignored.
-         * @deprecated since version 1.0.3
-         */
-        marginTop: {
-          type: Number,
-          value: 14
-        },
-
-        /**
-         * The delay that will be applied before the `entry` animation is
-         * played when showing the tooltip.
-         */
-        animationDelay: {
-          type: Number,
-          value: 500
-        },
-
-        /**
-         * The entry and exit animations that will be played when showing and
-         * hiding the tooltip. If you want to override this, you must ensure
-         * that your animationConfig has the exact format below.
-         */
-        animationConfig: {
-          type: Object,
-          value: function() {
-            return {
-              'entry': [{
-                name: 'fade-in-animation',
-                node: this,
-                timing: {delay: 0}
-              }],
-              'exit': [{
-                name: 'fade-out-animation',
-                node: this
-              }]
-            }
-          }
-        },
-
-        _showing: {
-          type: Boolean,
-          value: false
-        }
-      },
-
-      listeners: {
-        'neon-animation-finish': '_onAnimationFinish',
-        'mouseenter': 'hide'
-      },
-
-      /**
-       * Returns the target element that this tooltip is anchored to. It is
-       * either the element given by the `for` attribute, or the immediate parent
-       * of the tooltip.
-       */
-      get target () {
-        var parentNode = Polymer.dom(this).parentNode;
-        // If the parentNode is a document fragment, then we need to use the host.
-        var ownerRoot = Polymer.dom(this).getOwnerRoot();
-
-        var target;
-        if (this.for) {
-          target = Polymer.dom(ownerRoot).querySelector('#' + this.for);
-        } else {
-          target = parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE ?
-              ownerRoot.host : parentNode;
-        }
-
-        return target;
-      },
-
-      attached: function() {
-        this._target = this.target;
-
-        if (this.manualMode)
-          return;
-
-        this.listen(this._target, 'mouseenter', 'show');
-        this.listen(this._target, 'focus', 'show');
-        this.listen(this._target, 'mouseleave', 'hide');
-        this.listen(this._target, 'blur', 'hide');
-        this.listen(this._target, 'tap', 'hide');
-      },
-
-      detached: function() {
-        if (this._target && !this.manualMode) {
-          this.unlisten(this._target, 'mouseenter', 'show');
-          this.unlisten(this._target, 'focus', 'show');
-          this.unlisten(this._target, 'mouseleave', 'hide');
-          this.unlisten(this._target, 'blur', 'hide');
-          this.unlisten(this._target, 'tap', 'hide');
-        }
-      },
-
-      show: function() {
-        // If the tooltip is already showing, there's nothing to do.
-        if (this._showing)
-          return;
-
-        if (Polymer.dom(this).textContent.trim() === '')
-          return;
-
-
-        this.cancelAnimation();
-        this._showing = true;
-        this.toggleClass('hidden', false, this.$.tooltip);
-        this.updatePosition();
-
-        this.animationConfig.entry[0].timing.delay = this.animationDelay;
-        this._animationPlaying = true;
-        this.playAnimation('entry');
-      },
-
-      hide: function() {
-        // If the tooltip is already hidden, there's nothing to do.
-        if (!this._showing) {
-          return;
-        }
-
-        // If the entry animation is still playing, don't try to play the exit
-        // animation since this will reset the opacity to 1. Just end the animation.
-        if (this._animationPlaying) {
-          this.cancelAnimation();
-          this._showing = false;
-          this._onAnimationFinish();
-          return;
-        }
-
-        this._showing = false;
-        this._animationPlaying = true;
-        this.playAnimation('exit');
-      },
-
-      _forChanged: function() {
-        this._target = this.target;
-      },
-
-      updatePosition: function() {
-        if (!this._target || !this.offsetParent)
-          return;
-
-        var offset = this.offset;
-        // If a marginTop has been provided by the user (pre 1.0.3), use it.
-        if (this.marginTop != 14 && this.offset == 14)
-          offset = this.marginTop;
-
-        var parentRect = this.offsetParent.getBoundingClientRect();
-        var targetRect = this._target.getBoundingClientRect();
-        var thisRect = this.getBoundingClientRect();
-
-        var horizontalCenterOffset = (targetRect.width - thisRect.width) / 2;
-        var verticalCenterOffset = (targetRect.height - thisRect.height) / 2;
-
-        var targetLeft = targetRect.left - parentRect.left;
-        var targetTop = targetRect.top - parentRect.top;
-
-        var tooltipLeft, tooltipTop;
-
-        switch (this.position) {
-          case 'top':
-            tooltipLeft = targetLeft + horizontalCenterOffset;
-            tooltipTop = targetTop - thisRect.height - offset;
-            break;
-          case 'bottom':
-            tooltipLeft = targetLeft + horizontalCenterOffset;
-            tooltipTop = targetTop + targetRect.height + offset;
-            break;
-          case 'left':
-            tooltipLeft = targetLeft - thisRect.width - offset;
-            tooltipTop = targetTop + verticalCenterOffset;
-            break;
-          case 'right':
-            tooltipLeft = targetLeft + targetRect.width + offset;
-            tooltipTop = targetTop + verticalCenterOffset;
-            break;
-        }
-
-        // TODO(noms): This should use IronFitBehavior if possible.
-        if (this.fitToVisibleBounds) {
-          // Clip the left/right side.
-          if (tooltipLeft + thisRect.width > window.innerWidth) {
-            this.style.right = '0px';
-            this.style.left = 'auto';
-          } else {
-            this.style.left = Math.max(0, tooltipLeft) + 'px';
-            this.style.right = 'auto';
-          }
-
-          // Clip the top/bottom side.
-          if (tooltipTop + thisRect.height > window.innerHeight) {
-            this.style.bottom = '0px';
-            this.style.top = 'auto';
-          } else {
-            this.style.top = Math.max(0, tooltipTop) + 'px';
-            this.style.bottom = 'auto';
-          }
-        } else {
-          this.style.left = tooltipLeft + 'px';
-          this.style.top = tooltipTop + 'px';
-        }
-
-      },
-
-      _onAnimationFinish: function() {
-        this._animationPlaying = false;
-        if (!this._showing) {
-          this.toggleClass('hidden', true, this.$.tooltip);
-        }
-      },
-    });
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip.html b/third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip.html
deleted file mode 100644
index 2e885fe..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip.html
+++ /dev/null
@@ -1,99 +0,0 @@
-<!--
-Copyright (c) 2015 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="../neon-animation/neon-animation-runner-behavior.html">
-<link rel="import" href="../neon-animation/animations/fade-in-animation.html">
-<link rel="import" href="../neon-animation/animations/fade-out-animation.html">
-
-<!--
-Material design: [Tooltips](https://www.google.com/design/spec/components/tooltips.html)
-
-`<paper-tooltip>` is a label that appears on hover and focus when the user
-hovers over an element with the cursor or with the keyboard. It will be centered
-to an anchor element specified in the `for` attribute, or, if that doesn't exist,
-centered to the parent node containing it.
-
-Example:
-
-    <div style="display:inline-block">
-      <button>Click me!</button>
-      <paper-tooltip>Tooltip text</paper-tooltip>
-    </div>
-
-    <div>
-      <button id="btn">Click me!</button>
-      <paper-tooltip for="btn">Tooltip text</paper-tooltip>
-    </div>
-
-The tooltip can be positioned on the top|bottom|left|right of the anchor using
-the `position` attribute. The default position is bottom.
-
-    <paper-tooltip for="btn" position="left">Tooltip text</paper-tooltip>
-    <paper-tooltip for="btn" position="top">Tooltip text</paper-tooltip>
-
-### Styling
-
-The following custom properties and mixins are available for styling:
-
-Custom property | Description | Default
-----------------|-------------|----------
-`--paper-tooltip-background` | The background color of the tooltip | `#616161`
-`--paper-tooltip-opacity` | The opacity of the tooltip | `0.9`
-`--paper-tooltip-text-color` | The text color of the tooltip | `white`
-`--paper-tooltip` | Mixin applied to the tooltip | `{}`
-
-@group Paper Elements
-@element paper-tooltip
-@demo demo/index.html
--->
-
-</head><body><dom-module id="paper-tooltip">
-  <template>
-    <style>
-      :host {
-        display: block;
-        position: absolute;
-        outline: none;
-        z-index: 1002;
-        -moz-user-select: none;
-        -ms-user-select: none;
-        -webkit-user-select: none;
-        user-select: none;
-        cursor: default;
-      }
-
-      #tooltip {
-        display: block;
-        outline: none;
-        @apply(--paper-font-common-base);
-        font-size: 10px;
-        line-height: 1;
-
-        background-color: var(--paper-tooltip-background, #616161);
-        opacity: var(--paper-tooltip-opacity, 0.9);
-        color: var(--paper-tooltip-text-color, white);
-
-        padding: 8px;
-        border-radius: 2px;
-
-        @apply(--paper-tooltip);
-      }
-
-      /* Thanks IE 10. */
-      .hidden {
-        display: none !important;
-      }
-    </style>
-
-    <div id="tooltip" class="hidden">
-      <content></content>
-    </div>
-  </template>
-
-  </dom-module>
-<script src="paper-tooltip-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index b3c7719e..8dfdb84 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -110,13 +110,6 @@
 Revision: ce9b2ea1f73d936cffdd05f3fe34b1f69d1d32db
 Tree link: https://github.com/PolymerElements/iron-iconset-svg/tree/v1.0.9
 
-Name: iron-image
-Version: 1.2.3
-Repository: git://github.com/PolymerElements/iron-image.git
-Tag: v1.2.3
-Revision: ab4f710c21e34eb1a383da63b127920111804919
-Tree link: https://github.com/PolymerElements/iron-image/tree/v1.2.3
-
 Name: iron-input
 Version: 1.0.10
 Repository: git://github.com/PolymerElements/iron-input.git
@@ -243,13 +236,6 @@
 Revision: 7d0f75300372d91835ae7298593d50987d4a610f
 Tree link: https://github.com/PolymerElements/paper-button/tree/v1.0.11
 
-Name: paper-card
-Version: 1.1.1
-Repository: git://github.com/PolymerElements/paper-card.git
-Tag: v1.1.1
-Revision: 8ddd91424414ad32147c4c0c6812aff348da6114
-Tree link: https://github.com/PolymerElements/paper-card/tree/v1.1.1
-
 Name: paper-checkbox
 Version: 1.1.3
 Repository: git://github.com/PolymerElements/paper-checkbox.git
@@ -418,13 +404,6 @@
 Revision: e9d582733fab4d0698c680047d7faaab1a35196c
 Tree link: https://github.com/PolymerElements/paper-toolbar/tree/v1.1.4
 
-Name: paper-tooltip
-Version: 1.1.2
-Repository: git://github.com/PolymerElements/paper-tooltip.git
-Tag: v1.1.2
-Revision: 6be894127678900f6e506b56fc9622ab768c03aa
-Tree link: https://github.com/PolymerElements/paper-tooltip/tree/v1.1.2
-
 Name: polymer
 Version: 1.4.0
 Repository: git://github.com/Polymer/polymer.git
diff --git a/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn
index b744cc4..fa4350c 100644
--- a/third_party/yasm/BUILD.gn
+++ b/third_party/yasm/BUILD.gn
@@ -27,6 +27,8 @@
 #   6) compile_gperf, compile_re2c, etc. -- Actions that invoke programs that
 #              turn intermediate files into .c files.
 
+import("//build/config/compiler/compiler.gni")
+
 if (current_toolchain == host_toolchain) {
   # Various files referenced by multiple targets.
   yasm_gen_include_dir = "$target_gen_dir/include"
@@ -297,6 +299,12 @@
       ":yasm_warnings",
     ]
 
+    # Disable WPO for yasm: crbug.com/604808
+    if (is_official_build && full_wpo_on_official) {
+      configs -= [ "//build/config/compiler:default_optimization" ]
+      configs += [ "//build/config/compiler:optimize_no_wpo" ]
+    }
+
     # Yasm generates a bunch of .c files which its source file #include.
     # Add the |target_gen_dir| into the include path so it can find them.
     # Ideally, these generated .c files would be placed into a separate
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index d8239078..1b00b235 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -428,7 +428,7 @@
 
   Checkout('LLVM', LLVM_REPO_URL + '/llvm/trunk', LLVM_DIR)
   Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR)
-  if sys.platform == 'win32':
+  if sys.platform == 'win32' or use_head_revision:
     Checkout('LLD', LLVM_REPO_URL + '/lld/trunk', LLD_DIR)
   Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk', COMPILER_RT_DIR)
   if sys.platform == 'darwin':
diff --git a/tools/ipc_fuzzer/BUILD.gn b/tools/ipc_fuzzer/BUILD.gn
index 2a72653..7f33cef 100644
--- a/tools/ipc_fuzzer/BUILD.gn
+++ b/tools/ipc_fuzzer/BUILD.gn
@@ -9,6 +9,9 @@
 }
 
 config("ipc_fuzzer_tool_config") {
+  if (is_win) {
+    cflags = [ "/wd4366" ]
+  }
   defines = [
     "ENABLE_IPC_FUZZER",
     "USE_CUPS",
diff --git a/tools/ipc_fuzzer/message_lib/BUILD.gn b/tools/ipc_fuzzer/message_lib/BUILD.gn
index a362de8..2c6503e 100644
--- a/tools/ipc_fuzzer/message_lib/BUILD.gn
+++ b/tools/ipc_fuzzer/message_lib/BUILD.gn
@@ -13,6 +13,7 @@
     "//components/network_hints/common",
     "//components/page_load_metrics/common",
     "//components/pdf/common",
+    "//components/tracing",
     "//content/child",
     "//content/public/child",
     "//ipc",
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index b145df34..99366b5 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -132,7 +132,7 @@
         'swarming_gyp_clang_tot_linux_dump_symbols_shared_release',
       'ClangToTLinux tester': 'none',
       'ClangToTLinux (dbg)': 'swarming_clang_tot_shared_debug',
-      'ClangToTLinuxASan': 'swarming_clang_tot_asan_lsan_static_release',
+      'ClangToTLinuxASan': 'swarming_gyp_clang_tot_asan_lsan_static_release',
       'ClangToTLinuxASan tester': 'none',
       'ClangToTLinuxUBSanVptr':
         'swarming_gyp_clang_tot_edge_ubsan_no_recover_hack_static_release',
@@ -220,16 +220,17 @@
     },
 
     'chromium.gpu': {
-      'Android Debug (Nexus 5)': 'android_gn_debug_static_bot',
-      'Android Debug (Nexus 6)': 'android_gn_debug_static_bot',
-      'GPU Mac Builder': 'swarming_gpu_tests_gyp_release_bot',
-      'GPU Mac Builder (dbg)': 'swarming_gpu_tests_gyp_debug_bot',
-      'GPU Linux Builder (dbg)': 'swarming_gpu_tests_gn_debug_bot',
-      'GPU Linux Builder': 'swarming_gpu_tests_gn_release_bot',
+      # These all use the 'trybot' mixins to ensure that dcheck is on.
+      'Android Debug (Nexus 5)': 'android_gn_debug_static_trybot',
+      'Android Debug (Nexus 6)': 'android_gn_debug_static_trybot',
+      'GPU Mac Builder': 'swarming_gpu_tests_gyp_release_trybot',
+      'GPU Mac Builder (dbg)': 'swarming_gpu_tests_gyp_debug_trybot',
+      'GPU Linux Builder (dbg)': 'swarming_gpu_tests_gn_debug_trybot',
+      'GPU Linux Builder': 'swarming_gpu_tests_gn_release_trybot',
       'GPU Win Builder':
-        'swarming_gpu_tests_gyp_release_bot_minimal_symbols_x86',
+        'swarming_gpu_tests_gyp_release_trybot_minimal_symbols_x86',
       'GPU Win Builder (dbg)':
-        'swarming_gpu_tests_gyp_debug_bot_minimal_symbols_x86',
+        'swarming_gpu_tests_gyp_debug_trybot_minimal_symbols_x86',
       'Linux Debug (NVIDIA)': 'none',
       'Linux Release (NVIDIA)': 'none',
       'Mac 10.10 Debug (Intel)': 'none',
@@ -242,23 +243,24 @@
     },
 
     'chromium.gpu.fyi': {
-      'Android Debug (Nexus 9)': 'android_gn_debug_static_bot_arm64',
-      'GPU Linux Builder (dbg)': 'swarming_gpu_fyi_tests_gn_debug_bot',
-      'GPU Linux Builder': 'swarming_gpu_fyi_tests_gn_release_bot',
-      'GPU Mac Builder': 'swarming_gpu_fyi_tests_gyp_release_bot',
-      'GPU Mac Builder (dbg)': 'swarming_gpu_fyi_tests_gyp_debug_bot',
+      # These all use the 'trybot' mixins to ensure that dcheck is on.
+      'Android Debug (Nexus 9)': 'android_gn_debug_static_trybot_arm64',
+      'GPU Linux Builder (dbg)': 'swarming_gpu_fyi_tests_gn_debug_trybot',
+      'GPU Linux Builder': 'swarming_gpu_fyi_tests_gn_release_trybot',
+      'GPU Mac Builder': 'swarming_gpu_fyi_tests_gyp_release_trybot',
+      'GPU Mac Builder (dbg)': 'swarming_gpu_fyi_tests_gyp_debug_trybot',
       'GPU Win Builder':
-        'swarming_gpu_tests_deqp_gles_gyp_release_bot_minimal_symbols_x86',
+        'swarming_gpu_tests_deqp_gles_gyp_release_trybot_minimal_symbols_x86',
       'GPU Win Builder (dbg)':
-        'swarming_gpu_tests_deqp_gles_gyp_debug_bot_minimal_symbols_x86',
+        'swarming_gpu_tests_deqp_gles_gyp_debug_trybot_minimal_symbols_x86',
       'GPU Win Clang Builder (dbg)':
-        'swarming_gpu_fyi_tests_gyp_clang_debug_bot_x86',
+        'swarming_gpu_fyi_tests_gyp_clang_debug_trybot_x86',
       'GPU Win x64 Builder':
-        'swarming_gpu_tests_deqp_gles_gyp_release_bot_minimal_symbols_x64',
+        'swarming_gpu_tests_deqp_gles_gyp_release_trybot_minimal_symbols_x64',
       'GPU Win x64 Builder (dbg)':
-        'swarming_gpu_tests_deqp_gles_gyp_debug_bot_minimal_symbols_x64',
+        'swarming_gpu_tests_deqp_gles_gyp_debug_trybot_minimal_symbols_x64',
       'Linux ChromiumOS Builder':
-        'swarming_gpu_fyi_tests_chromeos_gyp_release_bot',
+        'swarming_gpu_fyi_tests_chromeos_gyp_release_trybot',
       'Linux Debug (NVIDIA)': 'none',
       'Linux Debug (New Intel)': 'none',
       'Linux Release (ATI)': 'none',
@@ -352,6 +354,10 @@
       'Mac10.11 Tests': 'none',
       'Mac10.9 Tests (dbg)': 'none',
       'Mac10.9 Tests': 'none',
+      'ios-device': 'ios_gyp',
+      'ios-device-gn': 'ios_gn',
+      'ios-simulator': 'ios_gyp',
+      'ios-simulator-gn': 'ios_gn',
       'iOS_Device': 'ios_gyp',
       'iOS_Device_(ninja)': 'ios_gyp',
       'iOS_Device_GN': 'ios_gn',
@@ -677,11 +683,11 @@
       'mac_angle_dbg_ng': 'swarming_gpu_fyi_tests_gyp_debug_trybot',
       'mac_angle_rel_ng': 'swarming_gpu_fyi_tests_gyp_release_trybot',
       'win_angle_dbg_ng':
-        'swarming_gpu_tests_deqp_gles_gyp_debug_bot_minimal_symbols_x86',
+        'swarming_gpu_tests_deqp_gles_gyp_debug_trybot_minimal_symbols_x86',
       'win_angle_rel_ng':
         'swarming_gpu_tests_deqp_gles_gyp_release_trybot_minimal_symbols_x86',
       'win_angle_x64_dbg_ng':
-        'swarming_gpu_tests_deqp_gles_gyp_debug_bot_minimal_symbols_x64',
+        'swarming_gpu_tests_deqp_gles_gyp_debug_trybot_minimal_symbols_x64',
       'win_angle_x64_rel_ng':
         'swarming_gpu_tests_deqp_gles_gyp_release_trybot_minimal_symbols_x64',
     },
@@ -755,6 +761,10 @@
     },
 
     'tryserver.chromium.mac': {
+      'ios-device': 'ios_gyp',
+      'ios-device-gn': 'ios_gn',
+      'ios-simulator': 'ios_gyp',
+      'ios-simulator-gn': 'ios_gn',
       'ios_dbg_simulator': 'ios_gyp',
       'ios_dbg_simulator_gn': 'ios_gn',
       'ios_dbg_simulator_ninja': 'ios_gyp',
@@ -984,6 +994,14 @@
       'android', 'gn', 'debug', 'static', 'minimal_symbols', 'mipsel',
     ],
 
+    'android_gn_debug_static_trybot': [
+      'android', 'gn', 'debug_static_trybot',
+    ],
+
+    'android_gn_debug_static_trybot_arm64': [
+      'android', 'gn', 'debug_static_trybot', 'arm64',
+    ],
+
     'android_gn_debug_trybot': [
       'android', 'gn', 'debug_trybot',
     ],
@@ -1149,7 +1167,7 @@
     ],
 
     'gn_ubsan_vptr_release_bot': [
-      'gn', 'ubsan_vptr', 'release_bot',
+      'gn', 'ubsan_vptr', 'ubsan_no_recover_hack', 'release_bot',
     ],
 
     'gyp_asan_lsan_edge_fuzzer_debug_bot': [
@@ -1473,7 +1491,8 @@
     ],
 
     'swarming_gyp_asan_clang_tot_full_symbols_static_release': [
-      'swarming', 'asan', 'clang_tot', 'full_symbols', 'static', 'release',
+      'swarming', 'gyp', 'asan', 'clang_tot', 'full_symbols', 'static',
+      'release',
     ],
 
     'swarming_gyp_clang_tot_minimal_symbols_shared_debug_use_lld_x64': [
@@ -1584,8 +1603,8 @@
       'static', 'release',
     ],
 
-    'swarming_clang_tot_asan_lsan_static_release': [
-      'swarming', 'gn', 'clang_tot', 'asan', 'lsan', 'static', 'release',
+    'swarming_gyp_clang_tot_asan_lsan_static_release': [
+      'swarming', 'gyp', 'clang_tot', 'asan', 'lsan', 'static', 'release',
     ],
 
     'swarming_clang_tot_shared_debug': [
@@ -1619,14 +1638,9 @@
       'swarming', 'gn', 'msan', 'release_bot', 'x64',
     ],
 
-    'swarming_gpu_fyi_tests_chromeos_gyp_release_bot': [
+    'swarming_gpu_fyi_tests_chromeos_gyp_release_trybot': [
       'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gyp',
-      'release_bot', 'angle_deqp_tests', 'chromeos',
-    ],
-
-    'swarming_gpu_fyi_tests_gn_debug_bot': [
-      'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gn',
-      'debug_bot', 'angle_deqp_tests',
+      'release_trybot', 'angle_deqp_tests', 'chromeos',
     ],
 
     'swarming_gpu_fyi_tests_gn_debug_trybot': [
@@ -1634,50 +1648,35 @@
       'debug_trybot', 'angle_deqp_tests',
     ],
 
-    'swarming_gpu_fyi_tests_gn_release_bot': [
-      'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gn',
-      'release_bot', 'angle_deqp_tests',
-    ],
-
     'swarming_gpu_fyi_tests_gn_release_trybot': [
       'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gn',
       'release_bot', 'angle_deqp_tests',
     ],
 
-    'swarming_gpu_fyi_tests_gyp_debug_bot': [
-      'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gyp',
-      'debug_bot',
-    ],
-
     'swarming_gpu_fyi_tests_gyp_debug_trybot': [
       'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gyp',
       'debug_trybot',
     ],
 
-    'swarming_gpu_fyi_tests_gyp_release_bot': [
-      'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gyp',
-      'release_bot',
-    ],
-
     'swarming_gpu_fyi_tests_gyp_release_trybot': [
       'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gyp',
       'release_trybot',
     ],
 
-    'swarming_gpu_fyi_tests_gyp_clang_debug_bot_x86': [
+    'swarming_gpu_fyi_tests_gyp_clang_debug_trybot_x86': [
       'swarming', 'gpu_tests', 'internal_gles2_conform_tests', 'gyp',
-      'clang', 'debug_bot', 'x86',
+      'clang', 'debug_trybot', 'x86',
     ],
 
-    'swarming_gpu_tests_deqp_gles_gyp_debug_bot_minimal_symbols_x64': [
+    'swarming_gpu_tests_deqp_gles_gyp_debug_trybot_minimal_symbols_x64': [
       'swarming', 'gpu_tests', 'angle_deqp_tests',
-      'internal_gles2_conform_tests', 'gyp', 'debug_bot',
+      'internal_gles2_conform_tests', 'gyp', 'debug_trybot',
       'minimal_symbols', 'x64',
     ],
 
-    'swarming_gpu_tests_deqp_gles_gyp_debug_bot_minimal_symbols_x86': [
+    'swarming_gpu_tests_deqp_gles_gyp_debug_trybot_minimal_symbols_x86': [
       'swarming', 'gpu_tests', 'angle_deqp_tests',
-      'internal_gles2_conform_tests', 'gyp', 'debug_bot',
+      'internal_gles2_conform_tests', 'gyp', 'debug_trybot',
       'minimal_symbols', 'x86',
     ],
 
@@ -1693,40 +1692,36 @@
       'minimal_symbols', 'x86',
     ],
 
-    'swarming_gpu_tests_deqp_gles_gyp_release_bot_minimal_symbols_x64': [
+    'swarming_gpu_tests_deqp_gles_gyp_release_trybot_minimal_symbols_x64': [
       'swarming', 'gpu_tests', 'angle_deqp_tests',
-      'internal_gles2_conform_tests', 'gyp', 'release_bot',
+      'internal_gles2_conform_tests', 'gyp', 'release_trybot',
       'minimal_symbols', 'x64',
     ],
 
-    'swarming_gpu_tests_deqp_gles_gyp_release_bot_minimal_symbols_x86': [
+    'swarming_gpu_tests_deqp_gles_gyp_release_trybot_minimal_symbols_x86': [
       'swarming', 'gpu_tests', 'angle_deqp_tests',
-      'internal_gles2_conform_tests', 'gyp', 'release_bot',
+      'internal_gles2_conform_tests', 'gyp', 'release_trybot',
       'minimal_symbols', 'x86',
     ],
 
-    'swarming_gpu_tests_gn_debug_bot': [
-      'swarming', 'gpu_tests', 'gn', 'debug_bot',
-    ],
-
-    'swarming_gpu_tests_gn_release_bot': [
-      'swarming', 'gpu_tests', 'gn', 'release_bot',
+    'swarming_gpu_tests_gn_debug_trybot': [
+      'swarming', 'gpu_tests', 'gn', 'debug_trybot',
     ],
 
     'swarming_gpu_tests_gn_release_trybot': [
       'swarming', 'gpu_tests', 'gn', 'release_trybot',
     ],
 
-    'swarming_gpu_tests_gyp_debug_bot_minimal_symbols_x86': [
-      'swarming', 'gpu_tests', 'gyp', 'debug_bot', 'minimal_symbols', 'x86',
+    'swarming_gpu_tests_gyp_debug_trybot_minimal_symbols_x86': [
+      'swarming', 'gpu_tests', 'gyp', 'debug_trybot', 'minimal_symbols', 'x86',
     ],
 
     'swarming_gpu_tests_gyp_release_bot': [
       'swarming', 'gpu_tests', 'gyp', 'release_bot',
     ],
 
-    'swarming_gpu_tests_gyp_debug_bot': [
-      'swarming', 'gpu_tests', 'gyp', 'debug_bot',
+    'swarming_gpu_tests_gyp_debug_trybot': [
+      'swarming', 'gpu_tests', 'gyp', 'debug_trybot',
     ],
 
     'swarming_gpu_tests_gyp_release_bot_minimal_symbols_x86': [
@@ -2055,6 +2050,11 @@
       'mixins': ['debug', 'static', 'minimal_symbols', 'goma'],
     },
 
+    'debug_static_trybot': {
+      'mixins': ['debug', 'static', 'dcheck_always_on', 'minimal_symbols',
+                 'goma'],
+    },
+
     'debug_trybot': {
       'mixins': ['debug_bot_minimal_symbols'],
     },
@@ -2298,7 +2298,7 @@
       # TODO(krasin): Remove when https://llvm.org/bugs/show_bug.cgi?id=25569
       # is fixed and just use ubsan_vptr instead.
       'mixins': ['ubsan_vptr'],
-      'gn_args': 'error', # TODO(GYP): need a GN equivalent for this
+      'gn_args': 'is_ubsan_no_recover=true',
       'gyp_defines': 'release_extra_cflags=-fno-sanitize-recover=undefined',
     },
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index bfa3a81..6ff7f086 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4473,6 +4473,9 @@
 </histogram>
 
 <histogram name="Cellular.ActivationFailure">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The count of cellular device activation failures (Chrome OS).
@@ -4485,6 +4488,9 @@
 </histogram>
 
 <histogram name="Cellular.ConnectionFailed">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The count of cellular reconnect failures during activation (Chrome OS).
@@ -4492,6 +4498,9 @@
 </histogram>
 
 <histogram name="Cellular.ConnectionRetry">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The count of cellular device reconnect tries during activation (Chrome OS).
@@ -4499,6 +4508,9 @@
 </histogram>
 
 <histogram name="Cellular.MobileSetupFailed">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The count of successful cellular plan established (Chrome OS).
@@ -4513,16 +4525,25 @@
 </histogram>
 
 <histogram name="Cellular.MobileSetupSucceeded">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The count of failed cellular plan setup tries (Chrome OS).</summary>
 </histogram>
 
 <histogram name="Cellular.PaymentFailed">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The count of failed cellular plan purchases (Chrome OS).</summary>
 </histogram>
 
 <histogram name="Cellular.PaymentReceived">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The count of successfully completed cellular plan purchases (Chrome OS).
@@ -4547,6 +4568,9 @@
 </histogram>
 
 <histogram name="CertificateType2">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Information about the certificate algorithms and sizes in use on the web, to
@@ -4726,6 +4750,9 @@
 </histogram>
 
 <histogram name="Chrome.Android.Activity.CrashCounts" enum="AndroidActivityId">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Indicates how many times each particular type of Activity was in the
@@ -4735,6 +4762,9 @@
 </histogram>
 
 <histogram name="Chrome.Android.Activity.LaunchCounts" enum="AndroidActivityId">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Indicates how many times each particular type of Activity was brought to the
@@ -4863,6 +4893,9 @@
 
 <histogram name="ChromeNotifierService.Actions"
     enum="ChromeNotifierServiceActionType">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The actions to enable or disable services sending synced notifications.
@@ -5219,6 +5252,9 @@
 </histogram>
 
 <histogram name="clickjacking.report_and_discard_download" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time between &quot;Report and Discard&quot; button being shown and it being
@@ -5555,6 +5591,9 @@
 </histogram>
 
 <histogram name="Compositing.CopyFromSurfaceTimeSynchronous" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time taken for the sync readback of pixels is measured here.
@@ -7927,6 +7966,9 @@
 </histogram>
 
 <histogram name="Diagnostics.Recovery.ConflictingDlls" enum="DiagnosticsResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     TBD - Not run automatically yet, so this is just a placeholder for future
@@ -7986,6 +8028,9 @@
 </histogram>
 
 <histogram name="Diagnostics.Recovery.OperatingSystem" enum="DiagnosticsResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     TBD - Not run automatically yet, so this is just a placeholder for future
@@ -8039,6 +8084,9 @@
 
 <histogram name="Diagnostics.Recovery.SQLiteIntegrityAppCache"
     enum="DiagnosticsResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Shows the success and failure rates of the SQLiteIntegrityAppCache recovery
@@ -8158,6 +8206,9 @@
 </histogram>
 
 <histogram name="Diagnostics.Test.ConflictingDlls" enum="DiagnosticsResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     TBD - Not run automatically yet, so this is just a placeholder for future
@@ -8213,6 +8264,9 @@
 </histogram>
 
 <histogram name="Diagnostics.Test.OperatingSystem" enum="DiagnosticsResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     TBD - Not run automatically yet, so this is just a placeholder for future
@@ -8262,6 +8316,9 @@
 
 <histogram name="Diagnostics.Test.SQLiteIntegrityAppCache"
     enum="DiagnosticsResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Shows the success and failure rates of the SQLiteIntegrityAppCache test that
@@ -8476,11 +8533,17 @@
 </histogram>
 
 <histogram name="DisabledExtension.ExtensionWipedStatus" enum="BooleanWiped">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Whether an extension has been wiped out.</summary>
 </histogram>
 
 <histogram name="DisabledExtension.SideloadWipeoutCount">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     How many external extensions get wiped out as a result of the Sideload
@@ -8489,6 +8552,9 @@
 </histogram>
 
 <histogram name="DisabledExtension.SideloadWipeoutNeeded" enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Whether any extension got wiped out as a result of the Sideload Wipeout
@@ -8497,6 +8563,9 @@
 </histogram>
 
 <histogram name="DisabledExtension.UserSelection" enum="SideloadWipeoutBubble">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The user selection in the Sideload Wipeout bubble, grouped by the
@@ -12907,6 +12976,9 @@
 </histogram>
 
 <histogram name="Extensions.APIUse_RelativeURL" enum="UrlResolutionResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Captures the results of URL resolution when relative urls are used in the
@@ -13176,6 +13248,9 @@
 </histogram>
 
 <histogram name="Extensions.DepricatedExternalJsonCount">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Number of extensions referenced in the depricated external extensions source
@@ -13684,6 +13759,9 @@
 </histogram>
 
 <histogram name="Extensions.GetUserDataTempDir" enum="GetUserDataTempDirResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     What happens when the extensions system tries to get a temp dir to unpack
@@ -14979,6 +15057,9 @@
 </histogram>
 
 <histogram name="Extensions.ToolstripLoadTime" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Time taken to load a toolstrip.</summary>
 </histogram>
@@ -18164,6 +18245,9 @@
 </histogram>
 
 <histogram name="Instant.InstantControllerEvent" enum="InstantControllerEvent">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Records various events of interest in the InstantController. E.g. When URLs
@@ -18173,6 +18257,9 @@
 
 <histogram name="Instant.SessionsStorageNamespace"
     enum="InstantSessionStorageNamespace">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     How often an Instant preview is committed onto a different tab than it was
@@ -18181,6 +18268,9 @@
 </histogram>
 
 <histogram name="Instant.TimeToFirstShow" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time between the first Omnibox interaction and when the Instant preview
@@ -20716,6 +20806,9 @@
 </histogram>
 
 <histogram name="Media.Fling.DelayedAndDroppedFramesPer5Sec" units="frames/5s">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average number of delayed and dropped frames for the Fling application.
@@ -20735,6 +20828,9 @@
 </histogram>
 
 <histogram name="Media.Fling.TimeToBufferAv" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time needed to pre-buffer A/V data before the actual playback for the Fling
@@ -20743,6 +20839,9 @@
 </histogram>
 
 <histogram name="Media.Fling.TimeToBufferAvAfterAbort" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time needed to buffer A/V data after an abort for the Fling application.
@@ -20750,6 +20849,9 @@
 </histogram>
 
 <histogram name="Media.Fling.TimeToBufferAvAfterUnderrun" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time needed to buffer A/V data after an underrun for the Fling application.
@@ -21014,6 +21116,9 @@
 </histogram>
 
 <histogram name="Media.Netflix.AudioBitrate" units="kbps">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The audio bit rate as reported by the Netflix application.  May be reported
@@ -21022,6 +21127,9 @@
 </histogram>
 
 <histogram name="Media.Netflix.AudioNumChannels" units="channels">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of audio channels as reported by the Netflix application. May be
@@ -21031,6 +21139,9 @@
 
 <histogram name="Media.Netflix.DelayedAndDroppedFramesPer5Sec"
     units="frames/5s">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average number of delayed and dropped frames for the Netflix
@@ -21050,6 +21161,9 @@
 </histogram>
 
 <histogram name="Media.Netflix.VideoBitrate" units="kbps">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Video bit rate as reported by the Netflix application.  May be reported
@@ -21058,6 +21172,9 @@
 </histogram>
 
 <histogram name="Media.Netflix.VideoHeight" units="pixels">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Video height as reported by the Netflix application.  May be reported
@@ -21112,6 +21229,9 @@
 
 <histogram name="Media.PlayMovies.DelayedAndDroppedFramesPer5Sec"
     units="frames/5s">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average number of delayed and dropped frames for the PlayMovies
@@ -21573,6 +21693,9 @@
 
 <histogram name="Media.YouTube.DelayedAndDroppedFramesPer5Sec"
     units="frames/5s">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average number of delayed and dropped frames for the YouTube
@@ -21600,6 +21723,9 @@
 </histogram>
 
 <histogram name="Media.YouTube.TimeToBufferAvAfterAbort" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time needed to buffer A/V data after an abort for the YouTube application.
@@ -23003,6 +23129,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.MouseDownDuration_Click" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Measures the time elapsed between when the user mousedown-ed a link and when
@@ -23012,6 +23141,9 @@
 
 <histogram name="MouseEventPrefetch.MouseDownFollowedByClick"
     enum="MouseEventFollowedByClick">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     For each click handled by an HTML anchor tag link, whether Blink saw a
@@ -23021,6 +23153,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.MouseDowns">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of mousedown events detected at HTML anchor-tag links' default
@@ -23029,6 +23164,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.MouseOverDuration_Click" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Measures the time elapsed between when the user mouseover-ed a link and when
@@ -23037,6 +23175,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.MouseOverDuration_NoClick" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Measures the time elapsed between when the user mouseover-ed a link and when
@@ -23045,6 +23186,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.MouseOvers">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of mouseover events detected at HTML anchor-tag links' default
@@ -23054,6 +23198,9 @@
 
 <histogram name="MouseEventPrefetch.PreTapEventsFollowedByClick"
     enum="PreTapEvents">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The tap gesture events detected before click at HTML anchor-tag links'
@@ -23062,6 +23209,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.TapDownDuration_Click" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Measures the time elapsed between when the user tapdown-ed a link and when
@@ -23070,6 +23220,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.TapDowns">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of gesturetapdown events detected at HTML anchor-tag links'
@@ -23078,6 +23231,9 @@
 </histogram>
 
 <histogram name="MouseEventPrefetch.TapUnconfirmeds">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of gesturetapunconfirmed events detected at HTML anchor-tag
@@ -23149,11 +23305,17 @@
 </histogram>
 
 <histogram name="MPArch.RWH_OnMsgPaintRect" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The time spent inside RenderWidgetHost::OnMsgPaintRect.</summary>
 </histogram>
 
 <histogram name="MPArch.RWH_OnMsgScrollRect" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The time spent inside RenderWidgetHost::OnMsgScrollRect.</summary>
 </histogram>
@@ -23177,6 +23339,9 @@
 </histogram>
 
 <histogram name="MPArch.RWHH_WhiteoutDuration" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time that the user sees a blank white page after switching to a
@@ -24896,6 +25061,9 @@
 </histogram>
 
 <histogram name="Net.CoalescePotential" enum="CoalescePotentialPackets">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of times we sent N packets, but could have sent N-1 packets.
@@ -25188,6 +25356,9 @@
 </histogram>
 
 <histogram name="net.CookieDomainPerEtldp1Count">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     For every top level domain, number of subdomains in that top level domain
@@ -25805,6 +25976,9 @@
 
 <histogram name="Net.DoubleGetExperiment_InitialResponseMethod"
     enum="DoubleGetExperimentMethods">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The number of HTTP request responses with MS Office Docs MIME types. The
@@ -25818,6 +25992,9 @@
 </histogram>
 
 <histogram name="Net.DoubleGetExperiment_ResponseCode">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The response codes encountered for GET request repeated in Double GET
@@ -26036,6 +26213,9 @@
 </histogram>
 
 <histogram name="Net.FileError_Flush">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file Flush failed with.  The code is OS dependent,
@@ -26044,6 +26224,9 @@
 </histogram>
 
 <histogram name="Net.FileError_GetSize">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file GetSize failed with.  The code is OS
@@ -26052,6 +26235,9 @@
 </histogram>
 
 <histogram name="Net.FileError_Open">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file Open failed with.  The code is OS dependent,
@@ -26060,6 +26246,9 @@
 </histogram>
 
 <histogram name="Net.FileError_Read">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file Read failed with.  The code is OS dependent,
@@ -26068,6 +26257,9 @@
 </histogram>
 
 <histogram name="Net.FileError_Seek">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file Seek failed with.  The code is OS dependent,
@@ -26076,6 +26268,9 @@
 </histogram>
 
 <histogram name="Net.FileError_SetEof">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file SetEof failed with.  The code is OS dependent,
@@ -26084,6 +26279,9 @@
 </histogram>
 
 <histogram name="Net.FileError_Write">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code that a file Write failed with.  The code is OS dependent,
@@ -26092,6 +26290,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_Flush">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file Flush failed with.  Any value other than
@@ -26102,6 +26303,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_GetSize">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file GetSize failed with.  Any value other
@@ -26112,6 +26316,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_Open">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file Open failed with.  Any value other than
@@ -26122,6 +26329,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_Read">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file Read failed with.  Any value other than
@@ -26132,6 +26342,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_Seek">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file Seek failed with.  Any value other than
@@ -26142,6 +26355,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_SetEof">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file SetEof failed with.  Any value other
@@ -26152,6 +26368,9 @@
 </histogram>
 
 <histogram name="Net.FileErrorRange_Write">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     System error code range that a file Write failed with.  Any value other than
@@ -26673,6 +26892,9 @@
 </histogram>
 
 <histogram name="Net.IOError_SocketReuseType" enum="HttpSocketType">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The count of handleable socket errors (connection abort/close/reset) per
@@ -26747,11 +26969,17 @@
 </histogram>
 
 <histogram name="Net.IPv6Status" enum="IPV6ProbeResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The probe results when a test for IPv6 support is done.</summary>
 </histogram>
 
 <histogram name="Net.IPv6Status_retest" enum="IPV6ProbeResult">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The probe results when a test for IPv6 support is done, after a network
@@ -26805,6 +27033,9 @@
 </histogram>
 
 <histogram name="Net.NetworkErrorsRecovered.MainFrame" enum="NetErrorCodes">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     How often automatically retrying to download the main frame of a page in
@@ -26813,6 +27044,9 @@
 </histogram>
 
 <histogram name="Net.NetworkErrorsRecovered.Subresource" enum="NetErrorCodes">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     How often automatically retrying to download a subresource in response to
@@ -26821,6 +27055,9 @@
 </histogram>
 
 <histogram name="Net.NetworkErrorsUnrecovered.MainFrame" enum="NetErrorCodes">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     How often automatically retrying to download the main frame of a page in
@@ -26830,6 +27067,9 @@
 </histogram>
 
 <histogram name="Net.NetworkErrorsUnrecovered.Subresource" enum="NetErrorCodes">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     How often automatically retrying to download a subresource in response to
@@ -26934,6 +27174,9 @@
 </histogram>
 
 <histogram name="Net.PacResultForStrippedUrl" enum="PacResultForStrippedUrl">
+  <obsolete>
+    Deprecated 4/27/2016. No longer tracked.
+  </obsolete>
   <owner>eroman@chromium.org</owner>
   <summary>
     Proxy Auto Config (PAC) allows specifying an arbitrary javascript program to
@@ -27099,8 +27342,7 @@
 <histogram name="Net.Prefetch.HitBytes" units="bytes">
   <owner>jkarlin@chromium.org</owner>
   <summary>
-    Bytes read for requests that were served from a cache entry filled by a
-    prefetch request. An entry is considered filled by a prefetch if its
+    Bytes read for requests that were served from a cache entry whose
     unused_since_prefetch bit is true.
   </summary>
 </histogram>
@@ -27139,8 +27381,7 @@
 <histogram name="Net.Prefetch.TimeSpentOnPrefetchHit" units="ms">
   <owner>jkarlin@chromium.org</owner>
   <summary>
-    Time spent on requests that were served from a cache entry filled by a
-    prefetch request. An entry is considered filled by a prefetch if its
+    Time spent on requests that were served from a cache entry whose
     unused_since_prefetch bit is true.
   </summary>
 </histogram>
@@ -28713,6 +28954,9 @@
 </histogram>
 
 <histogram name="Net.SocketIdleTimeOnIOError2_ReusedSocket">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time a previously used socket sat idle before encountering a recoverable
@@ -28721,6 +28965,9 @@
 </histogram>
 
 <histogram name="Net.SocketIdleTimeOnIOError2_UnusedSocket">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time an unused socket sat idle before encountering a recoverable socket
@@ -28913,6 +29160,9 @@
 </histogram>
 
 <histogram name="Net.SocksSocketRequestTime">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Time it takes to request a new (unused) SOCKS proxy socket.</summary>
 </histogram>
@@ -29642,6 +29892,9 @@
 </histogram>
 
 <histogram name="Net.TCP_Connection_Idle_Sockets">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Number of idle sockets when the Connect() succeeded.</summary>
 </histogram>
@@ -30368,6 +30621,9 @@
 
 <histogram name="NetConnectivity.TCP.Status"
     enum="NetConnectivityProtocolStatus">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Status for TCP protocol for echoing</summary>
 </histogram>
@@ -30389,6 +30645,9 @@
 </histogram>
 
 <histogram name="NetConnectivity.TCP.Success" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The RTT for TCP protocol for echoing</summary>
 </histogram>
@@ -30457,6 +30716,9 @@
 
 <histogram name="NetConnectivity.UDP.Status"
     enum="NetConnectivityProtocolStatus">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Status for UDP protocol for echoing</summary>
 </histogram>
@@ -30478,6 +30740,9 @@
 </histogram>
 
 <histogram name="NetConnectivity.UDP.Success" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The RTT for UDP protocol for echoing</summary>
 </histogram>
@@ -30499,6 +30764,9 @@
 </histogram>
 
 <histogram name="NetConnectivity2.Send6.PacketsSent">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     This histogram records how many packets (out of 6 attempted) were sent via
@@ -30507,6 +30775,9 @@
 </histogram>
 
 <histogram name="NetConnectivity2.Send6.SeriesAcked">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome sends 6 UDP packets in a row to test to see if there is a
@@ -30519,6 +30790,9 @@
 </histogram>
 
 <histogram name="NetConnectivity2.Sent21">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     In this experiment, 21 packets were sent to Google via UDP as rapidly as
@@ -30531,6 +30805,9 @@
 </histogram>
 
 <histogram name="NetConnectivity2.Sent21.AckReceivedForNthPacket">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     In this experiment, 21 packets were sent to Google via UDP as rapidly as
@@ -30541,6 +30818,9 @@
 </histogram>
 
 <histogram name="NetConnectivity2.Sent21.GotAnAck" enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     In this experiment, 21 packets were sent to Google via UDP as rapidly as
@@ -30551,6 +30831,9 @@
 </histogram>
 
 <histogram name="NetConnectivity2.Sent21.PacketsSent">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     This histogram records how many packets (out of 21 attempted) were sent via
@@ -30559,6 +30842,9 @@
 </histogram>
 
 <histogram name="NetConnectivity3">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     In this experiment, 21 packets were sent to Google via UDP on port 443 or
@@ -30568,94 +30854,142 @@
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.443.100B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.443.1200B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.443.500B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.6121.100B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.6121.1200B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.6121.500B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.GotAnAck"
     enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.NonPacedPacket.Sent21.Success.RTT" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.443.100B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.443.1200B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.443.500B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.6121.100B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.6121.1200B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.6121.500B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.GotAnAck"
     enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.PacedPacket.Sent21.Success.RTT" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
@@ -30673,52 +31007,79 @@
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.443.100B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.443.1200B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.443.500B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.6121.100B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.6121.1200B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.6121.500B.PacketDelay"
     units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.GotAnAck"
     enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity3.StartPacket.Sent21.Success.RTT" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary/>
 </histogram>
 
 <histogram name="NetConnectivity4">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     In this experiment, a few packets were sent from Google to clients via UDP
@@ -30727,6 +31088,9 @@
 </histogram>
 
 <histogram name="NetConnectivity5">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     In this experiment, a few packets were sent from Google to clients via UDP
@@ -30736,6 +31100,9 @@
 
 <histogram name="NetConnectivity5.TestFailed.WritePending"
     enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Next NetConnectivity5 experiment weren't started because there is an
@@ -30785,6 +31152,9 @@
 </histogram>
 
 <histogram name="Network.Cellular.TimeOnline" units="seconds">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network metric sampling the time spent using Cellular to transport
@@ -30794,6 +31164,9 @@
 </histogram>
 
 <histogram name="Network.Cellular.TimeToConfig" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join a 3G/Cellular
@@ -30802,6 +31175,9 @@
 </histogram>
 
 <histogram name="Network.Cellular.TimeToOnline" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that a
@@ -30810,6 +31186,9 @@
 </histogram>
 
 <histogram name="Network.Cellular.TimeToPortal" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that a
@@ -30819,11 +31198,17 @@
 
 <histogram name="Network.Cellular.UsageRequestStatus"
     enum="NetworkCellularUsageRequestStatus">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Chrome OS cellular usage API request status codes.</summary>
 </histogram>
 
 <histogram name="Network.Ethernet.TimeOnline" units="seconds">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network metric sampling the time spent using Ethernet to transport
@@ -30833,6 +31218,9 @@
 </histogram>
 
 <histogram name="Network.Ethernet.TimeToConfig" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join a wired
@@ -30842,6 +31230,9 @@
 </histogram>
 
 <histogram name="Network.Ethernet.TimeToOnline" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that an
@@ -30850,6 +31241,9 @@
 </histogram>
 
 <histogram name="Network.Ethernet.TimeToPortal" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that an
@@ -30873,6 +31267,9 @@
 </histogram>
 
 <histogram name="Network.ServiceErrors" enum="NetworkServiceError">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Chrome OS connection manager service errors seen.</summary>
 </histogram>
@@ -32156,6 +32553,9 @@
 </histogram>
 
 <histogram name="Network.TimeToConfig.Cellular" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join a 3G/Cellular
@@ -32165,6 +32565,9 @@
 </histogram>
 
 <histogram name="Network.TimeToConfig.Ethernet" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join a wired
@@ -32174,6 +32577,9 @@
 </histogram>
 
 <histogram name="Network.TimeToConfig.Wifi" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to configure Layer 3
@@ -32183,6 +32589,9 @@
 </histogram>
 
 <histogram name="Network.TimeToDrop" units="seconds">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network stability metric sampling the time in seconds between the
@@ -32193,6 +32602,9 @@
 </histogram>
 
 <histogram name="Network.TimeToJoin.Wifi" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join (associate
@@ -32202,6 +32614,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.AuthMode" enum="NetworkAuthModeType">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to configure Layer 3
@@ -32210,6 +32625,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.BitRate" units="bps">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Network metric reporting the download speed test results run at setup time.
@@ -32250,6 +32668,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.TimeOnline" units="seconds">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network metric sampling the time spent using WiFi to transport
@@ -32259,6 +32680,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.TimeResumeToReady" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time from the resume event
@@ -32268,6 +32692,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.TimeToConfig" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to configure Layer 3
@@ -32276,6 +32703,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.TimeToJoin" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to join (associate
@@ -32284,6 +32714,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.TimeToOnline" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that an
@@ -32292,6 +32725,9 @@
 </histogram>
 
 <histogram name="Network.Wifi.TimeToPortal" units="ms">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Chrome OS network performance metric sampling the time to determine that an
@@ -33289,6 +33725,9 @@
 </histogram>
 
 <histogram name="ntp.searchurls.total">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>TBD</summary>
 </histogram>
@@ -33771,6 +34210,9 @@
 </histogram>
 
 <histogram name="OfflinePolicy.SuccessfulResourceLoadPercentage" units="%">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     When a page is loaded in offline mode, the percentage of resources on that
@@ -36150,6 +36592,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.Cache_Avg" units="KB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Average size of user's Cache directory. Logged once a day, if disk usage is
@@ -36158,6 +36603,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.Cache_Max" units="KB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Maximum size of user's Cache directory. Logged once a day, if disk usage is
@@ -36166,6 +36614,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.Downloads_Avg" units="KB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Average size of user's Cache directory. Logged once a day, if disk usage is
@@ -36174,6 +36625,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.Downloads_Max" units="KB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Maximum size of user's Cache directory. Logged once a day, if disk usage is
@@ -36182,6 +36636,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.GCache_Avg" units="KB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Average size of user's GCache directory. Logged once a day, if disk usage is
@@ -36190,6 +36647,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.GCache_Max" units="KB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Maximum size of user's GCache directory. Logged once a day, if disk usage is
@@ -36198,6 +36658,9 @@
 </histogram>
 
 <histogram name="Platform.DiskUsage.LeastUsedAccountDays" units="days">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Days since the least frequently used account signed in. Logged once a day,
@@ -36954,6 +37417,9 @@
 
 <histogram name="Platform.SwapJank.Scroll.Faults.Swap2.Time3"
     units="page faults/second">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Page faults/second for the specified swap group and time interval after a
@@ -37594,6 +38060,9 @@
 </histogram>
 
 <histogram name="Platform.Tps65090Retries">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Retries needed to enable a FET on tps65090 (AKA tpschrome).  Tps65090 is a
@@ -39384,6 +39853,9 @@
 </histogram>
 
 <histogram name="Power.MilliConsumptionPerHourIosOnActive">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average power consumption, measured in milli-units per hour, when sync
@@ -39396,6 +39868,9 @@
 </histogram>
 
 <histogram name="Power.MilliConsumptionPerHourOthers">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average power consumption, measured in milli-units per hour, for other
@@ -39408,6 +39883,9 @@
 </histogram>
 
 <histogram name="Power.MilliConsumptionPerHourP2P">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average power consumption, measured in milli-units per hour, when sync
@@ -39420,6 +39898,9 @@
 </histogram>
 
 <histogram name="Power.MilliConsumptionPerHourServer">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The average power consumption, measured in milli-units per hour, when sync
@@ -41337,6 +41818,9 @@
 </histogram>
 
 <histogram name="Profile.ThumbnailsSize" units="MB">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Size of the thumbnails database.</summary>
 </histogram>
@@ -42063,6 +42547,9 @@
 </histogram>
 
 <histogram name="Renderer.PixelIncreaseFromTransitions">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     A lower-bound on the percentage increase in memory that would result from
@@ -42091,6 +42578,9 @@
 </histogram>
 
 <histogram name="Renderer2.FinishDocToFinish">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a document finished loading to when all it's resources
@@ -42110,6 +42600,9 @@
 </histogram>
 
 <histogram name="Renderer2.RequestToFinish_L">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a page was requested by a user to when it is fully
@@ -42118,6 +42611,9 @@
 </histogram>
 
 <histogram name="Renderer2.RequestToFirstLayout">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a page was requested by a user to its first layout.
@@ -42125,6 +42621,9 @@
 </histogram>
 
 <histogram name="Renderer2.RequestToStart">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a page was requested by a user to when it starts loading.
@@ -42132,6 +42631,9 @@
 </histogram>
 
 <histogram name="Renderer2.StartToFinish">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a page started loading to when it is fully loaded.
@@ -42139,6 +42641,9 @@
 </histogram>
 
 <histogram name="Renderer2.StartToFinishDoc">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a page starts loading to when the main document is
@@ -42147,6 +42652,9 @@
 </histogram>
 
 <histogram name="Renderer2.StartToFirstLayout">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time from when a page starts loading to its first layout.
@@ -57680,6 +58188,9 @@
 </histogram>
 
 <histogram name="VirtualKeyboard.KeystrokesBetweenBackspace">
+  <obsolete>
+    Deprecated 04/2016 as doesn't have data nor owner.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Counts the number of keys typed by the virtual keyboard between each
@@ -57865,6 +58376,16 @@
   </summary>
 </histogram>
 
+<histogram name="WebAudio.IIRFilterNode.Order">
+  <owner>rtoy@chromium.org</owner>
+  <owner>hongchan@chromium.org</owner>
+  <summary>
+    The order of the WebAudio IIRFilterNode.  The order is one less than the
+    number of feedback coefficients used in the denominator of the IIRFilter
+    transfer function.  Recorded each time an IIRFilter is constructed.
+  </summary>
+</histogram>
+
 <histogram name="WebController.CertVerificationErrorsCacheHit"
     enum="BooleanCacheHit">
   <owner>eugenebut@chromium.org</owner>
@@ -71426,6 +71947,8 @@
   <int value="1320" label="FileAPINativeLineEndings"/>
   <int value="1321" label="PointerEventAttributeCount"/>
   <int value="1322" label="CompositedReplication"/>
+  <int value="1323" label="EncryptedMediaAllSelectedContentTypesHaveCodecs"/>
+  <int value="1324" label="EncryptedMediaAllSelectedContentTypesMissingCodecs"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -79511,6 +80034,9 @@
 </enum>
 
 <enum name="PacResultForStrippedUrl" type="int">
+  <obsolete>
+    Deprecated 4/27/2016. No longer tracked.
+  </obsolete>
   <summary>
     Result for PAC script experiment as defined in
     net/proxy/proxy_resolver_v8_tracing.h
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index ad4d53b3..a24f14d 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -36,6 +36,17 @@
       zero-coin-prob="0.25"/>
 </noise-level>
 
+<noise-level name="SPARSE_NOISE">
+  <summary>
+    Reduced noise, suitable for metrics which receive limited reports (less
+    than 500,000 per day) or expect a highly skewed/sparse distribution over
+    a wide set of domains. Approval from the Chrome Privacy and Rappor teams
+    is required to implement a metric using this noise level.
+  </summary>
+  <noise-values fake-prob="0.25" fake-one-prob="0.5" one-coin-prob="0.75"
+      zero-coin-prob="0.25"/>
+</noise-level>
+
 </noise-levels>
 
 <rappor-parameter-types>
@@ -64,9 +75,47 @@
       reporting-level="FINE" noise-level="NORMAL_NOISE"/>
 </rappor-parameters>
 
+<rappor-parameters name="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <summary>
+    Parameters for collecting the domain and registry of a URL from UMA opt-in
+    users, using SPARSE_NOISE. Intended for sparse/skewed metrics, or low
+    frequency metrics (less than 500,000 reports per day). Explicit approval
+    from the Chrome Privacy and Rappor teams is required to use this type. New
+    metrics should also consider LOW_FREQUENCY_UMA_RAPPOR_TYPE instead.
+  </summary>
+  <parameters num-cohorts="128" bytes="16" hash-functions="2" fake-prob="0.25"
+      fake-one-prob="0.5" one-coin-prob="0.75" zero-coin-prob="0.25"
+      reporting-level="FINE" noise-level="SPARSE_NOISE"/>
+</rappor-parameters>
+
+<rappor-parameters name="LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE">
+  <summary>
+    Parameters for metrics related to Safe Browsing, collected from all users
+    with safe browsing enabled, using SPARSE_NOISE. Intended for sparse/skewed
+    metrics, or low frequency metrics (less than 500,000 reports per day).
+    Explicit approval from the Chrome Privacy and Rappor teams is required to
+    use this type.
+  </summary>
+  <parameters num-cohorts="128" bytes="1" hash-functions="2" fake-prob="0.25"
+      fake-one-prob="0.5" one-coin-prob="0.75" zero-coin-prob="0.25"
+      reporting-level="COARSE" noise-level="SPARSE_NOISE"/>
+</rappor-parameters>
+
+<rappor-parameters name="LOW_FREQUENCY_UMA_RAPPOR_TYPE">
+  <summary>
+    Parameters suitable for metrics from UMA opt-in users, using SPARSE_NOISE.
+    Intended for sparse/skewed metrics, or low frequency metrics (less than
+    500,000 reports per day). Explicit approval from the Chrome Privacy and
+    Rappor teams is required to use this type.
+  </summary>
+  <parameters num-cohorts="128" bytes="4" hash-functions="2" fake-prob="0.25"
+      fake-one-prob="0.5" one-coin-prob="0.75" zero-coin-prob="0.25"
+      reporting-level="FINE" noise-level="SPARSE_NOISE"/>
+</rappor-parameters>
+
 <rappor-parameters name="SAFEBROWSING_RAPPOR_TYPE">
   <summary>
-    Parameters for metrics related to Safe Browsing, collected from all user
+    Parameters for metrics related to Safe Browsing, collected from all users
     with safe browsing enabled.
   </summary>
   <parameters num-cohorts="128" bytes="1" hash-functions="2" fake-prob="0.5"
@@ -211,8 +260,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a AudioCapture permission prompt was Denied. Also
-    recorded as a multi-dimensional metric in Permissions.Action.AudioCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_AudioCapture.Denied.Url2 as of M52. The
+    domain for which a AudioCapture permission prompt was Denied.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_AudioCapture.Denied.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which an AudioCapture permission prompt was Denied.
   </summary>
 </rappor-metric>
 
@@ -223,8 +285,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a AudioCapture permission prompt was Dismissed. Also
-    recorded as a multi-dimensional metric in Permissions.Action.AudioCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_AudioCapture.Dismissed.Url2 as of M52.
+    The domain for which an AudioCapture permission prompt was Dismissed.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_AudioCapture.Dismissed.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which an AudioCapture permission prompt was Dismissed.
   </summary>
 </rappor-metric>
 
@@ -235,8 +310,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a AudioCapture permission prompt was accepted. Also
-    recorded as a multi-dimensional metric in Permissions.Action.AudioCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_AudioCapture.Granted.Url2 as of M52. The
+    domain for which an AudioCapture permission prompt was accepted.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_AudioCapture.Granted.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which an AudioCapture permission prompt was accepted.
   </summary>
 </rappor-metric>
 
@@ -247,8 +335,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a AudioCapture permission prompt was Ignored. Also
-    recorded as a multi-dimensional metric in Permissions.Action.AudioCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_AudioCapture.Ignored.Url2 as of M52. The
+    domain for which an AudioCapture permission prompt was Ignored.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_AudioCapture.Ignored.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which an AudioCapture permission prompt was Ignored.
   </summary>
 </rappor-metric>
 
@@ -257,9 +358,20 @@
     type="ETLD_PLUS_ONE">
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which an AudioCapture permission was revoked. Also recorded
-    as a multi-dimensional metric in Permissions.Action.AudioCapture. Note: This
-    metric is recorded for more revocation actions as of 2016-04-01 (M51).
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_AudioCapture.Revoked.Url2 as of M52. The
+    domain for which an AudioCapture permission was revoked. Note: This metric
+    is recorded for more revocation actions as of 2016-04-01 (M51).
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_AudioCapture.Revoked.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which an AudioCapture permission was revoked.
   </summary>
 </rappor-metric>
 
@@ -277,6 +389,17 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Geolocation.Denied.Url2 as of M52. The
+    domain for which a Geolocation permission prompt was Denied.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionActions_Geolocation.Denied.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain for which a Geolocation permission prompt was Denied.
   </summary>
 </rappor-metric>
@@ -286,6 +409,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Geolocation.Dismissed.Url2 as of M52.
+    The domain for which a Geolocation permission prompt was Dismissed.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Geolocation.Dismissed.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain for which a Geolocation permission prompt was Dismissed.
   </summary>
 </rappor-metric>
@@ -294,6 +429,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Geolocation.Granted.Url2 as of M52. The
+    domain for which a Geolocation permission prompt was accepted.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Geolocation.Granted.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain for which a Geolocation permission prompt was accepted.
   </summary>
 </rappor-metric>
@@ -302,6 +449,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Geolocation.Ignored.Url2 as of M52. The
+    domain for which a Geolocation permission prompt was Ignored.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Geolocation.Ignored.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain for which a Geolocation permission prompt was Ignored.
   </summary>
 </rappor-metric>
@@ -310,8 +469,20 @@
     type="ETLD_PLUS_ONE">
   <owner>jialiul@chromium.org</owner>
   <summary>
-    The domain for which a Geolocation permission was revoked. Note: This
-    metric is recorded for more revocation actions as of 2016-04-01 (M51).
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Geolocation.Revoked.Url2 as of M52. The
+    domain for which a Geolocation permission was revoked. Note: This metric is
+    recorded for more revocation actions as of 2016-04-01 (M51).
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Geolocation.Revoked.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>jialiul@chromium.org</owner>
+  <summary>
+    The domain for which a Geolocation permission was revoked.
   </summary>
 </rappor-metric>
 
@@ -330,6 +501,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Notifications.Denied.Url2 as of M52. The
+    domain for which a Notification permission prompt was Denied.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Denied.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>jialiul@chromium.org</owner>
+  <summary>
     The domain for which a Notification permission prompt was Denied.
   </summary>
 </rappor-metric>
@@ -339,6 +522,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Notifications.Dismissed.Url2 as of M52.
+    The domain for which a Notification permission prompt was Dismissed.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Dismissed.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>jialiul@chromium.org</owner>
+  <summary>
     The domain for which a Notification permission prompt was Dismissed.
   </summary>
 </rappor-metric>
@@ -348,6 +543,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Notifications.Granted.Url2 as of M52. The
+    domain for which a Notification permission prompt was accepted.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Granted.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain for which a Notification permission prompt was accepted.
   </summary>
 </rappor-metric>
@@ -357,6 +564,18 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Notifications.Ignored.Url2 as of M52. The
+    domain for which a Notification permission prompt was Ignored.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Ignored.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain for which a Notification permission prompt was Ignored.
   </summary>
 </rappor-metric>
@@ -366,8 +585,20 @@
     type="ETLD_PLUS_ONE">
   <owner>jialiul@chromium.org</owner>
   <summary>
-    The domain for which a Notification permission was revoked. Note: This
-    metric is recorded for more revocation actions as of 2016-04-01 (M51).
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_Notifications.Revoked.Url2 as of M52. The
+    domain for which a Notification permission was revoked. Note: This metric
+    is recorded for more revocation actions as of 2016-04-01 (M51).
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Revoked.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>jialiul@chromium.org</owner>
+  <summary>
+    The domain for which a Notification permission was revoked.
   </summary>
 </rappor-metric>
 
@@ -377,8 +608,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a VideoCapture permission prompt was Denied. Also
-    recorded as a multi-dimensional metric in Permissions.Action.VideoCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_VideoCapture.Denied.Url2 as of M52. The
+    domain for which a VideoCapture permission prompt was Denied.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_VideoCapture.Denied.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which a VideoCapture permission prompt was Denied.
   </summary>
 </rappor-metric>
 
@@ -389,8 +633,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a VideoCapture permission prompt was Dismissed. Also
-    recorded as a multi-dimensional metric in Permissions.Action.VideoCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_VideoCapture.Dismissed.Url2 as of M52. The
+    domain for which a VideoCapture permission prompt was Dismissed.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_VideoCapture.Dismissed.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which a VideoCapture permission prompt was Dismissed.
   </summary>
 </rappor-metric>
 
@@ -401,8 +658,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a VideoCapture permission prompt was accepted. Also
-    recorded as a multi-dimensional metric in Permissions.Action.VideoCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_VideoCapture.Granted.Url2 as of M52. The
+    domain for which a VideoCapture permission prompt was accepted.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_VideoCapture.Granted.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which a VideoCapture permission prompt was accepted.
   </summary>
 </rappor-metric>
 
@@ -413,8 +683,21 @@
   <owner>miguelg@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which a VideoCapture permission prompt was Ignored. Also
-    recorded as a multi-dimensional metric in Permissions.Action.VideoCapture.
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_VideoCapture.Ignored.Url2 as of M52. The
+    domain for which a VideoCapture permission prompt was Ignored.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_VideoCapture.Ignored.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>kcarattini@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which a VideoCapture permission prompt was Ignored.
   </summary>
 </rappor-metric>
 
@@ -423,9 +706,20 @@
     type="ETLD_PLUS_ONE">
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain for which an VideoCapture permission was revoked. Also recorded
-    as a multi-dimensional metric in Permissions.Action.VideoCapture. Note: This
-    metric is recorded for more revocation actions as of 2016-04-01 (M51).
+    **DEPRECATED. Replaced by
+    ContentSettings.PermissionActions_VideoCapture.Revoked.Url2 as of M52. The
+    domain for which an VideoCapture permission was revoked. Note: This metric
+    is recorded for more revocation actions as of 2016-04-01 (M51).
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_VideoCapture.Revoked.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>tsergeant@chromium.org</owner>
+  <summary>
+    The domain for which an VideoCapture permission was revoked.
   </summary>
 </rappor-metric>
 
@@ -433,6 +727,17 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED: replaced by
+    ContentSettings.PermissionRequested.Geolocation.Url2 as of M52. The domain
+    that issues a Geolocation permission prompt.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionRequested.Geolocation.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain that issues a Geolocation permission prompt.
   </summary>
 </rappor-metric>
@@ -441,6 +746,17 @@
     type="ETLD_PLUS_ONE">
   <owner>miguelg@chromium.org</owner>
   <summary>
+    **DEPRECATED: replaced by
+    ContentSettings.PermissionRequested.Notifications.Url2 as of M52. The
+    domain that issues a Notification permission prompt.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionRequested.Notifications.Url2"
+    type="LOW_FREQUENCY_ETLD_PLUS_ONE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>miguelg@chromium.org</owner>
+  <summary>
     The domain that issues a Notification permission prompt.
   </summary>
 </rappor-metric>
@@ -724,7 +1040,8 @@
   <owner>kcarattini@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the MediaStream Microphone API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the MediaStream Microphone API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -764,7 +1081,8 @@
     type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>kcarattini@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the Durable Storage API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the Durable Storage API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -804,7 +1122,8 @@
     type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>kcarattini@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the Geolocation API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the Geolocation API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -844,7 +1163,8 @@
     type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>kcarattini@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the MidiSysEx API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that     requested the MidiSysEx API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -884,7 +1204,8 @@
     type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>kcarattini@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the Notifications API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the Notifications API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -924,8 +1245,8 @@
     type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>kcarattini@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the ProtectedMediaIdentifier
-    API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the ProtectedMediaIdentifier API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -965,7 +1286,8 @@
     type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>kcarattini@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the PushMessaging API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the PushMessaging API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -1006,7 +1328,8 @@
   <owner>kcarattini@chromium.org</owner>
   <owner>tsergeant@chromium.org</owner>
   <summary>
-    The domain+registry of a URL that requested the MediaStream Camera API.
+    **DEPRECATED. No longer reported as of M52. The domain+registry of a URL
+    that requested the MediaStream Camera API.
   </summary>
   <string-field name="Scheme">
     <summary>
@@ -1370,6 +1693,30 @@
   </summary>
   <string-field name="domain">
     <summary>
+      **DEPRECATED: replaced by interstitial.harmful2 as of M52. The
+      domain+registry of a URL that triggered a safe-browsing UWS
+      interstitial.
+    </summary>
+  </string-field>
+  <flags-field name="flags">
+    <flag>Bit 0: DID_PROCEED</flag>
+    <flag>Bit 1: IS_REPEAT_VISIT</flag>
+    <summary>
+      Bitfield of the state from a safe-browsing UWS warning interstitial.
+    </summary>
+  </flags-field>
+</rappor-metric>
+
+<rappor-metric name="interstitial.harmful2"
+    type="LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>
+    The domain+registry of a URL that triggered a safe-browsing UWS
+    interstitial.
+  </summary>
+  <string-field name="domain">
+    <summary>
       The domain+registry of a URL that triggered a safe-browsing UWS
       interstitial.
     </summary>
@@ -1386,6 +1733,30 @@
 <rappor-metric name="interstitial.malware" type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>nparker@chromium.org</owner>
   <summary>
+    **DEPRECATED: replaced by interstitial.malware2 as of M52. The
+    domain+registry of a URL that triggered a safe-browsing malware
+    interstitial.
+  </summary>
+  <string-field name="domain">
+    <summary>
+      The domain+registry of a URL that triggered a safe-browsing malware
+      interstitial.
+    </summary>
+  </string-field>
+  <flags-field name="flags">
+    <flag>Bit 0: DID_PROCEED</flag>
+    <flag>Bit 1: IS_REPEAT_VISIT</flag>
+    <summary>
+      Bitfield of the state from a safe-browsing malware warning interstitial.
+    </summary>
+  </flags-field>
+</rappor-metric>
+
+<rappor-metric name="interstitial.malware2"
+    type="LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>
     The domain+registry of a URL that triggered a safe-browsing malware
     interstitial.
   </summary>
@@ -1407,6 +1778,30 @@
 <rappor-metric name="interstitial.phishing" type="SAFEBROWSING_RAPPOR_TYPE">
   <owner>nparker@chromium.org</owner>
   <summary>
+    **DEPRECATED: replaced by interstitial.phishing2 as of M52. The
+    domain+registry of a URL that triggered a safe-browsing phishing
+    interstitial.
+  </summary>
+  <string-field name="domain">
+    <summary>
+      The domain+registry of a URL that triggered a safe-browsing phishing
+      interstitial.
+    </summary>
+  </string-field>
+  <flags-field name="flags">
+    <flag>Bit 0: DID_PROCEED</flag>
+    <flag>Bit 1: IS_REPEAT_VISIT</flag>
+    <summary>
+      Bitfield of the state from a safe-browsing phishing warning interstitial.
+    </summary>
+  </flags-field>
+</rappor-metric>
+
+<rappor-metric name="interstitial.phishing2"
+    type="LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>
     The domain+registry of a URL that triggered a safe-browsing phishing
     interstitial.
   </summary>
@@ -1437,6 +1832,27 @@
 <rappor-metric name="interstitial.ssl2" type="UMA_RAPPOR_TYPE">
   <owner>nparker@chromium.org</owner>
   <summary>
+    **DEPRECATED: replaced by interstitial.ssl2 as of M52. The domain+registry
+    of a URL that triggered an SSL interstitial.
+  </summary>
+  <string-field name="domain">
+    <summary>
+      The domain+registry of a URL that triggered an SSL interstitial.
+    </summary>
+  </string-field>
+  <flags-field name="flags">
+    <flag>Bit 0: DID_PROCEED</flag>
+    <flag>Bit 1: IS_REPEAT_VISIT</flag>
+    <summary>
+      Bitfield of the state from an SSL warning interstitial.
+    </summary>
+  </flags-field>
+</rappor-metric>
+
+<rappor-metric name="interstitial.ssl3" type="LOW_FREQUENCY_UMA_RAPPOR_TYPE">
+  <owner>dominickn@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>
     The domain+registry of a URL that triggered an SSL interstitial.
   </summary>
   <string-field name="domain">
diff --git a/tools/perf/benchmarks/memory_infra.py b/tools/perf/benchmarks/memory_infra.py
index ff90333..1c89590 100644
--- a/tools/perf/benchmarks/memory_infra.py
+++ b/tools/perf/benchmarks/memory_infra.py
@@ -153,19 +153,6 @@
 
 
 # Disabled on reference builds because they don't support the new
-# Tracing.requestMemoryDump DevTools API. See http://crbug.com/540022.
-@benchmark.Disabled('reference')
-class MemoryBenchmarkTop10Mobile(_MemoryInfra):
-  """Timeline based benchmark for measuring memory on top 10 mobile sites."""
-
-  page_set = page_sets.MemoryInfraTop10MobilePageSet
-
-  @classmethod
-  def Name(cls):
-    return 'memory.top_10_mobile'
-
-
-# Disabled on reference builds because they don't support the new
 # Tracing.requestMemoryDump DevTools API.
 # For 'reference' see http://crbug.com/540022.
 # For 'android' see http://crbug.com/579546.
diff --git a/tools/perf/page_sets/memory_infra_top_10_mobile_story.py b/tools/perf/page_sets/memory_infra_top_10_mobile_story.py
deleted file mode 100644
index 52bcf90d..0000000
--- a/tools/perf/page_sets/memory_infra_top_10_mobile_story.py
+++ /dev/null
@@ -1,37 +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.
-
-import logging
-
-from page_sets import top_10_mobile
-from telemetry import story
-
-
-DUMP_WAIT_TIME = 3
-
-
-class MemoryInfraTop10MobilePage(top_10_mobile.Top10MobilePage):
-
-  def RunPageInteractions(self, action_runner):
-    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
-    action_runner.Wait(1)  # See crbug.com/540022#c17.
-    with action_runner.CreateInteraction('foreground'):
-      action_runner.Wait(DUMP_WAIT_TIME)
-      action_runner.ForceGarbageCollection()
-      action_runner.Wait(DUMP_WAIT_TIME)
-      if not action_runner.tab.browser.DumpMemory():
-        logging.error('Unable to get a memory dump for %s.', self.name)
-
-
-class MemoryInfraTop10MobilePageSet(story.StorySet):
-
-  """ Top 10 mobile sites for memory measurements """
-
-  def __init__(self):
-    super(MemoryInfraTop10MobilePageSet, self).__init__(
-      archive_data_file='data/top_10_mobile.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET)
-
-    for url in top_10_mobile.URL_LIST:
-      self.AddStory(MemoryInfraTop10MobilePage(url, self, False))
diff --git a/tools/valgrind/drmemory/suppressions.txt b/tools/valgrind/drmemory/suppressions.txt
index ee81fb0..cdb8218 100644
--- a/tools/valgrind/drmemory/suppressions.txt
+++ b/tools/valgrind/drmemory/suppressions.txt
@@ -816,3 +816,11 @@
 content.dll!content::BluetoothBlacklist::PopulateWithServerProvidedValues
 content.dll!content::BluetoothBlacklist::BluetoothBlacklist
 content.dll!content::BluetoothBlacklist::Get
+
+
+UNINITIALIZED READ
+name=bug_608111
+DWrite.dll!DWriteCreateFactory
+skia.dll!SkDWriteFontFileStreamWrapper::GetFileSize
+...
+blink_platform.dll!blink::SimpleFontData::platformWidthForGlyph
diff --git a/tools/valgrind/gtest_exclude/components_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/components_unittests.gtest-drmemory_win32.txt
index 82265dc..890ec8948 100644
--- a/tools/valgrind/gtest_exclude/components_unittests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/components_unittests.gtest-drmemory_win32.txt
@@ -1,2 +1,5 @@
 # http://crbug.com/514421
 WatcherMetricsProviderWinTest.DoesNotReportOwnProcessId
+
+# http://crbug.com/608053
+SafeBrowsingV4GetHashProtocolManagerTest.TestGetHash*
diff --git a/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt b/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
index 0bd392d..660d085 100644
--- a/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
+++ b/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
@@ -67,3 +67,11 @@
 
 # https://crbug.com/603337
 SingleProcessMemoryTracingTest.ManyInterleavedDumps
+
+# https://crbug.com/608081
+SitePerProcessBrowserTest.NavigateRemoteFrameToBlankAndDataURLs
+SitePerProcessBrowserTest.RFPHDestruction
+
+# https://crbug.com/608117
+WebRtcDataChannelTest.DataChannelGC
+
diff --git a/tools/valgrind/gtest_exclude/unit_tests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/unit_tests.gtest-drmemory_win32.txt
index 06fd527..031b0df 100644
--- a/tools/valgrind/gtest_exclude/unit_tests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/unit_tests.gtest-drmemory_win32.txt
@@ -84,3 +84,10 @@
 
 # https://crbug.com/577410
 PluginInfoMessageFilterTest.FindEnabledPlugin
+
+# http://crbug.com/608064
+BackgroundApplicationListModelTest.*
+BackgroundModeManagerWithExtensionsTest.*
+ExtensionContextMenuModelTest.*
+SavedFilesServiceUnitTest.*
+KeywordExtensionsDelegateImplTest.*
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.h b/ui/accelerated_widget_mac/accelerated_widget_mac.h
index fea9a51..3f7cebe7 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac.h
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac.h
@@ -67,12 +67,16 @@
       base::TimeTicks* timebase, base::TimeDelta* interval) const;
 
   void GotFrame(CAContextID ca_context_id,
+                bool fullscreen_low_power_ca_context_valid,
+                CAContextID fullscreen_low_power_ca_context_id,
                 base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
                 const gfx::Size& pixel_size,
                 float scale_factor);
 
  private:
   void GotCAContextFrame(CAContextID ca_context_id,
+                         bool fullscreen_low_power_ca_context_valid,
+                         CAContextID fullscreen_low_power_ca_context_id,
                          const gfx::Size& pixel_size,
                          float scale_factor);
 
@@ -104,8 +108,9 @@
   // behavior.
   base::scoped_nsobject<CALayer> flipped_layer_;
 
-  // The accelerated CoreAnimation layer hosted by the GPU process.
+  // The accelerated CoreAnimation layers hosted by the GPU process.
   base::scoped_nsobject<CALayerHost> ca_context_layer_;
+  base::scoped_nsobject<CALayerHost> fullscreen_low_power_layer_;
 
   // The locally drawn layer, which has its contents set to an IOSurface.
   base::scoped_nsobject<CALayer> local_layer_;
@@ -122,6 +127,8 @@
 void AcceleratedWidgetMacGotFrame(
     gfx::AcceleratedWidget widget,
     CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
     base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
     const gfx::Size& pixel_size,
     float scale_factor,
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.mm b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
index ee0a5e4..9853d56 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac.mm
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
@@ -118,6 +118,8 @@
 
 void AcceleratedWidgetMac::GotFrame(
     CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
     base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
     const gfx::Size& pixel_size,
     float scale_factor) {
@@ -134,17 +136,23 @@
 
   last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
 
-  if (ca_context_id)
-    GotCAContextFrame(ca_context_id, pixel_size, scale_factor);
-  else
+  if (ca_context_id) {
+    GotCAContextFrame(ca_context_id, fullscreen_low_power_ca_context_valid,
+                      fullscreen_low_power_ca_context_id, pixel_size,
+                      scale_factor);
+  } else {
     GotIOSurfaceFrame(io_surface, pixel_size, scale_factor);
+  }
 
   view_->AcceleratedWidgetSwapCompleted();
 }
 
-void AcceleratedWidgetMac::GotCAContextFrame(CAContextID ca_context_id,
-                                             const gfx::Size& pixel_size,
-                                             float scale_factor) {
+void AcceleratedWidgetMac::GotCAContextFrame(
+    CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
+    const gfx::Size& pixel_size,
+    float scale_factor) {
   TRACE_EVENT0("ui", "AcceleratedWidgetMac::GotCAContextFrame");
 
   // In the layer is replaced, keep the old one around until after the new one
@@ -162,6 +170,13 @@
         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
     [flipped_layer_ addSublayer:ca_context_layer_];
   }
+  if ([fullscreen_low_power_layer_ contextId] !=
+        fullscreen_low_power_ca_context_id) {
+    TRACE_EVENT0("ui", "Creating a new CALayerHost");
+    fullscreen_low_power_layer_.reset([[CALayerHost alloc] init]);
+    [fullscreen_low_power_layer_
+        setContextId:fullscreen_low_power_ca_context_id];
+  }
 
   // If this replacing a same-type layer, remove it now that the new layer is
   // in the hierarchy.
@@ -229,6 +244,8 @@
 void AcceleratedWidgetMacGotFrame(
     gfx::AcceleratedWidget widget,
     CAContextID ca_context_id,
+    bool fullscreen_low_power_ca_context_valid,
+    CAContextID fullscreen_low_power_ca_context_id,
     base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
     const gfx::Size& pixel_size,
     float scale_factor,
@@ -243,8 +260,10 @@
       GetHelperFromAcceleratedWidget(widget);
 
   if (accelerated_widget_mac) {
-    accelerated_widget_mac->GotFrame(ca_context_id, io_surface, pixel_size,
-                                     scale_factor);
+    accelerated_widget_mac->GotFrame(ca_context_id,
+                                     fullscreen_low_power_ca_context_valid,
+                                     fullscreen_low_power_ca_context_id,
+                                     io_surface, pixel_size, scale_factor);
     if (vsync_timebase && vsync_interval) {
       accelerated_widget_mac->GetVSyncParameters(vsync_timebase,
                                                  vsync_interval);
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index 2161449..9fe46c38 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -67,12 +67,12 @@
 
   ui::InitializeInputMethodForTesting();
 
-  gfx::Screen* screen = gfx::Screen::GetScreen();
+  display::Screen* screen = display::Screen::GetScreen();
   gfx::Size host_size(screen ? screen->GetPrimaryDisplay().GetSizeInPixel()
                              : gfx::Size(800, 600));
   test_screen_.reset(TestScreen::Create(host_size));
   if (!screen)
-    gfx::Screen::SetScreenInstance(test_screen_.get());
+    display::Screen::SetScreenInstance(test_screen_.get());
   host_.reset(test_screen_->CreateHostForPrimaryDisplay());
 
   focus_client_.reset(new TestFocusClient);
diff --git a/ui/base/cursor/cursor_loader.h b/ui/base/cursor/cursor_loader.h
index cf90846..0d53ac9 100644
--- a/ui/base/cursor/cursor_loader.h
+++ b/ui/base/cursor/cursor_loader.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "ui/base/ui_base_export.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -17,14 +17,12 @@
 
 class UI_BASE_EXPORT CursorLoader {
  public:
-  CursorLoader() : scale_(1.f), rotation_(gfx::Display::ROTATE_0) {}
+  CursorLoader() : scale_(1.f), rotation_(display::Display::ROTATE_0) {}
   virtual ~CursorLoader() {}
 
-  gfx::Display::Rotation rotation() const {
-    return rotation_;
-  }
+  display::Display::Rotation rotation() const { return rotation_; }
 
-  void set_rotation(gfx::Display::Rotation rotation) {
+  void set_rotation(display::Display::Rotation rotation) {
     rotation_ = rotation;
   }
 
@@ -67,7 +65,7 @@
   float scale_;
 
   // The current rotation of the mouse cursor icon.
-  gfx::Display::Rotation rotation_;
+  display::Display::Rotation rotation_;
 
   DISALLOW_COPY_AND_ASSIGN(CursorLoader);
 };
diff --git a/ui/base/cursor/cursor_loader_x11.cc b/ui/base/cursor/cursor_loader_x11.cc
index aff40d2..6bf237d 100644
--- a/ui/base/cursor/cursor_loader_x11.cc
+++ b/ui/base/cursor/cursor_loader_x11.cc
@@ -14,6 +14,7 @@
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_util.h"
 #include "ui/base/x/x11_util.h"
+#include "ui/display/display.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/image/image.h"
@@ -209,7 +210,7 @@
     xcursor =  invisible_cursor_.get();
   else if (*cursor == kCursorCustom)
     xcursor = cursor->platform();
-  else if (scale() == 1.0f && rotation() == gfx::Display::ROTATE_0) {
+  else if (scale() == 1.0f && rotation() == display::Display::ROTATE_0) {
     xcursor = GetXCursor(CursorShapeFromNative(*cursor));
   } else {
     xcursor = ImageCursorFromNative(kCursorPointer);
diff --git a/ui/base/cursor/cursor_loader_x11.h b/ui/base/cursor/cursor_loader_x11.h
index 7523db3..582a615 100644
--- a/ui/base/cursor/cursor_loader_x11.h
+++ b/ui/base/cursor/cursor_loader_x11.h
@@ -14,7 +14,6 @@
 #include "ui/base/cursor/cursor_loader.h"
 #include "ui/base/ui_base_export.h"
 #include "ui/base/x/x11_util.h"
-#include "ui/gfx/display.h"
 
 namespace ui {
 
diff --git a/ui/base/cursor/cursor_loader_x11_unittest.cc b/ui/base/cursor/cursor_loader_x11_unittest.cc
index 598746f..1574e48 100644
--- a/ui/base/cursor/cursor_loader_x11_unittest.cc
+++ b/ui/base/cursor/cursor_loader_x11_unittest.cc
@@ -11,6 +11,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/cursor/cursor_util.h"
+#include "ui/display/display.h"
 
 namespace ui {
 
@@ -21,35 +22,27 @@
 
   gfx::Point hotpoint(3,4);
 
-  ScaleAndRotateCursorBitmapAndHotpoint(1.0f,
-                                        gfx::Display::ROTATE_0,
-                                        &bitmap,
-                                        &hotpoint);
+  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_0,
+                                        &bitmap, &hotpoint);
   EXPECT_EQ(10, bitmap.width());
   EXPECT_EQ(5, bitmap.height());
   EXPECT_EQ("3,4", hotpoint.ToString());
 
-  ScaleAndRotateCursorBitmapAndHotpoint(1.0f,
-                                        gfx::Display::ROTATE_90,
-                                        &bitmap,
-                                        &hotpoint);
+  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_90,
+                                        &bitmap, &hotpoint);
 
   EXPECT_EQ(5, bitmap.width());
   EXPECT_EQ(10, bitmap.height());
   EXPECT_EQ("1,3", hotpoint.ToString());
 
-  ScaleAndRotateCursorBitmapAndHotpoint(2.0f,
-                                        gfx::Display::ROTATE_180,
-                                        &bitmap,
-                                        &hotpoint);
+  ScaleAndRotateCursorBitmapAndHotpoint(2.0f, display::Display::ROTATE_180,
+                                        &bitmap, &hotpoint);
   EXPECT_EQ(10, bitmap.width());
   EXPECT_EQ(20, bitmap.height());
   EXPECT_EQ("8,14", hotpoint.ToString());
 
-  ScaleAndRotateCursorBitmapAndHotpoint(1.0f,
-                                        gfx::Display::ROTATE_270,
-                                        &bitmap,
-                                        &hotpoint);
+  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_270,
+                                        &bitmap, &hotpoint);
   EXPECT_EQ(20, bitmap.width());
   EXPECT_EQ(10, bitmap.height());
   EXPECT_EQ("14,2", hotpoint.ToString());
diff --git a/ui/base/cursor/cursor_util.cc b/ui/base/cursor/cursor_util.cc
index 0e1d36e5..6aee6b56 100644
--- a/ui/base/cursor/cursor_util.cc
+++ b/ui/base/cursor/cursor_util.cc
@@ -40,33 +40,33 @@
 } // namespace
 
 void ScaleAndRotateCursorBitmapAndHotpoint(float scale,
-                                           gfx::Display::Rotation rotation,
+                                           display::Display::Rotation rotation,
                                            SkBitmap* bitmap,
                                            gfx::Point* hotpoint) {
   // SkBitmapOperations::Rotate() needs the bitmap to have premultiplied alpha,
   // so convert bitmap alpha type if we are going to rotate.
   bool was_converted = false;
-  if (rotation != gfx::Display::ROTATE_0 &&
+  if (rotation != display::Display::ROTATE_0 &&
       bitmap->info().alphaType() == kUnpremul_SkAlphaType) {
     ConvertSkBitmapAlphaType(bitmap, kPremul_SkAlphaType);
     was_converted = true;
   }
 
   switch (rotation) {
-    case gfx::Display::ROTATE_0:
+    case display::Display::ROTATE_0:
       break;
-    case gfx::Display::ROTATE_90:
+    case display::Display::ROTATE_90:
       hotpoint->SetPoint(bitmap->height() - hotpoint->y(), hotpoint->x());
       *bitmap = SkBitmapOperations::Rotate(
           *bitmap, SkBitmapOperations::ROTATION_90_CW);
       break;
-    case gfx::Display::ROTATE_180:
+    case display::Display::ROTATE_180:
       hotpoint->SetPoint(
           bitmap->width() - hotpoint->x(), bitmap->height() - hotpoint->y());
       *bitmap = SkBitmapOperations::Rotate(
           *bitmap, SkBitmapOperations::ROTATION_180_CW);
       break;
-    case gfx::Display::ROTATE_270:
+    case display::Display::ROTATE_270:
       hotpoint->SetPoint(hotpoint->y(), bitmap->width() - hotpoint->x());
       *bitmap = SkBitmapOperations::Rotate(
           *bitmap, SkBitmapOperations::ROTATION_270_CW);
@@ -98,7 +98,7 @@
 
 void GetImageCursorBitmap(int resource_id,
                           float scale,
-                          gfx::Display::Rotation rotation,
+                          display::Display::Rotation rotation,
                           gfx::Point* hotspot,
                           SkBitmap* bitmap) {
   const gfx::ImageSkia* image =
@@ -114,7 +114,7 @@
 
 void GetAnimatedCursorBitmaps(int resource_id,
                               float scale,
-                              gfx::Display::Rotation rotation,
+                              display::Display::Rotation rotation,
                               gfx::Point* hotspot,
                               std::vector<SkBitmap>* bitmaps) {
   // TODO(oshima|tdanderson): Support rotation and fractional scale factor.
diff --git a/ui/base/cursor/cursor_util.h b/ui/base/cursor/cursor_util.h
index 8649328..fc93f215 100644
--- a/ui/base/cursor/cursor_util.h
+++ b/ui/base/cursor/cursor_util.h
@@ -10,7 +10,7 @@
 #include "base/compiler_specific.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/ui_base_export.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
 #include "ui/gfx/geometry/point.h"
 
 namespace ui {
@@ -20,19 +20,19 @@
 // both input and output.
 UI_BASE_EXPORT void ScaleAndRotateCursorBitmapAndHotpoint(
     float scale,
-    gfx::Display::Rotation rotation,
+    display::Display::Rotation rotation,
     SkBitmap* bitmap_in_out,
     gfx::Point* hotpoint_in_out);
 
 // Helpers for CursorLoader.
 void GetImageCursorBitmap(int resource_id,
                           float scale,
-                          gfx::Display::Rotation rotation,
+                          display::Display::Rotation rotation,
                           gfx::Point* hotspot,
                           SkBitmap* bitmap);
 void GetAnimatedCursorBitmaps(int resource_id,
                               float scale,
-                              gfx::Display::Rotation rotation,
+                              display::Display::Rotation rotation,
                               gfx::Point* hotspot,
                               std::vector<SkBitmap>* bitmaps);
 
diff --git a/ui/base/cursor/image_cursors.cc b/ui/base/cursor/image_cursors.cc
index 54b851e..32ff6613 100644
--- a/ui/base/cursor/image_cursors.cc
+++ b/ui/base/cursor/image_cursors.cc
@@ -13,7 +13,7 @@
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_loader.h"
 #include "ui/base/cursor/cursors_aura.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
 #include "ui/gfx/geometry/point.h"
 
 namespace ui {
@@ -78,17 +78,17 @@
   return cursor_loader_->scale();
 }
 
-gfx::Display::Rotation ImageCursors::GetRotation() const {
+display::Display::Rotation ImageCursors::GetRotation() const {
   if (!cursor_loader_) {
     NOTREACHED();
     // Returning default on release build as it's not serious enough to crash
     // even if this ever happens.
-    return gfx::Display::ROTATE_0;
+    return display::Display::ROTATE_0;
   }
   return cursor_loader_->rotation();
 }
 
-bool ImageCursors::SetDisplay(const gfx::Display& display,
+bool ImageCursors::SetDisplay(const display::Display& display,
                               float scale_factor) {
   if (!cursor_loader_) {
     cursor_loader_.reset(CursorLoader::Create());
diff --git a/ui/base/cursor/image_cursors.h b/ui/base/cursor/image_cursors.h
index 01f3b08c..36e54656 100644
--- a/ui/base/cursor/image_cursors.h
+++ b/ui/base/cursor/image_cursors.h
@@ -11,7 +11,7 @@
 #include "base/strings/string16.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/ui_base_export.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace ui {
@@ -27,11 +27,11 @@
 
   // Returns the scale and rotation of the currently loaded cursor.
   float GetScale() const;
-  gfx::Display::Rotation GetRotation() const;
+  display::Display::Rotation GetRotation() const;
 
   // Sets the display the cursors are loaded for. |scale_factor| determines the
   // size of the image to load. Returns true if the cursor image is reloaded.
-  bool SetDisplay(const gfx::Display& display, float scale_factor);
+  bool SetDisplay(const display::Display& display, float scale_factor);
 
   // Sets the type of the mouse cursor icon.
   void SetCursorSet(CursorSetType cursor_set);
diff --git a/ui/base/l10n/l10n_util_win.cc b/ui/base/l10n/l10n_util_win.cc
index 1e1da3b..cd5b519 100644
--- a/ui/base/l10n/l10n_util_win.cc
+++ b/ui/base/l10n/l10n_util_win.cc
@@ -17,8 +17,8 @@
 #include "base/win/i18n.h"
 #include "base/win/windows_version.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/display/display.h"
 #include "ui/display/win/dpi.h"
-#include "ui/gfx/display.h"
 #include "ui/strings/grit/app_locale_settings.h"
 
 namespace {
@@ -156,7 +156,7 @@
 
 void AdjustUIFont(LOGFONT* logfont) {
   float dpi_scale = display::win::GetDPIScale();
-  if (gfx::Display::HasForceDeviceScaleFactor()) {
+  if (display::Display::HasForceDeviceScaleFactor()) {
     // If the scale is forced, we don't need to adjust it here.
     dpi_scale = 1.0f;
   }
diff --git a/ui/base/layout.cc b/ui/base/layout.cc
index 4299f09..06595c34 100644
--- a/ui/base/layout.cc
+++ b/ui/base/layout.cc
@@ -16,9 +16,9 @@
 #include "build/build_config.h"
 #include "ui/base/touch/touch_device.h"
 #include "ui/base/ui_base_switches.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/screen.h"
 
 namespace ui {
 
@@ -106,7 +106,7 @@
 
 #if !defined(OS_MACOSX)
 float GetScaleFactorForNativeView(gfx::NativeView view) {
-  return gfx::Screen::GetScreen()
+  return display::Screen::GetScreen()
       ->GetDisplayNearestWindow(view)
       .device_scale_factor();
 }
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 87ec2fc3..0f95c9e 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -26,6 +26,7 @@
 #include "build/build_config.h"
 #include "skia/ext/image_operations.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/layout.h"
 #include "ui/base/material_design/material_design_controller.h"
@@ -33,13 +34,14 @@
 #include "ui/base/ui_base_paths.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_features.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_source.h"
-#include "ui/gfx/screen.h"
 #include "ui/strings/grit/app_locale_settings.h"
 
 #if defined(OS_ANDROID)
@@ -103,6 +105,13 @@
 #endif  // OS_WIN
 }
 
+SkBitmap CreateEmptyBitmap() {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(32, 32);
+  bitmap.eraseARGB(255, 255, 255, 0);
+  return bitmap;
+}
+
 }  // namespace
 
 // An ImageSkiaSource that loads bitmaps for the requested scale factor from
@@ -125,8 +134,16 @@
     ScaleFactor scale_factor = GetSupportedScaleFactor(scale);
     bool found = rb_->LoadBitmap(resource_id_, &scale_factor,
                                  &image, &fell_back_to_1x);
-    if (!found)
+    if (!found) {
+#if defined(OS_ANDROID)
+      // TODO(oshima): Android unit_tests runs at DSF=3 with 100P assets.
       return gfx::ImageSkiaRep();
+#else
+      NOTREACHED() << "Unable to load image with id " << resource_id_
+                   << ", scale=" << scale;
+      return gfx::ImageSkiaRep(CreateEmptyBitmap(), scale);
+#endif
+    }
 
     // If the resource is in the package with SCALE_FACTOR_NONE, it
     // can be used in any scale factor. The image is maked as "unscaled"
@@ -319,6 +336,7 @@
 
 void ResourceBundle::LoadTestResources(const base::FilePath& path,
                                        const base::FilePath& locale_path) {
+  is_test_resources_ = true;
   DCHECK(!ui::GetSupportedScaleFactors().empty());
   const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]);
   // Use the given resource pak for both common and localized resources.
@@ -647,8 +665,8 @@
 #endif
 #if defined(OS_ANDROID)
   float display_density;
-  if (gfx::Display::HasForceDeviceScaleFactor()) {
-    display_density = gfx::Display::GetForcedDeviceScaleFactor();
+  if (display::Display::HasForceDeviceScaleFactor()) {
+    display_density = display::Display::GetForcedDeviceScaleFactor();
   } else {
     gfx::DeviceDisplayInfo device_info;
     display_density = device_info.GetDIPScale();
@@ -657,7 +675,7 @@
   if (closest != SCALE_FACTOR_100P)
     supported_scale_factors.push_back(closest);
 #elif defined(OS_IOS)
-  gfx::Display display = gfx::Screen::GetScreen()->GetPrimaryDisplay();
+  display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
   if (display.device_scale_factor() > 2.0) {
     DCHECK_EQ(3.0, display.device_scale_factor());
     supported_scale_factors.push_back(SCALE_FACTOR_300P);
@@ -799,6 +817,7 @@
                                 SkBitmap* bitmap,
                                 bool* fell_back_to_1x) const {
   DCHECK(fell_back_to_1x);
+  ResourceHandle* data_handle_100_percent = nullptr;
   for (size_t i = 0; i < data_packs_.size(); ++i) {
     if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
@@ -806,11 +825,25 @@
       *scale_factor = ui::SCALE_FACTOR_NONE;
       return true;
     }
+    if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P)
+      data_handle_100_percent = data_packs_[i];
+
     if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
       return true;
     }
   }
+
+  // Unit tests may only have 1x data pack. Allow them to fallback to 1x
+  // resources.
+  if (*scale_factor != ui::SCALE_FACTOR_100P && is_test_resources_ &&
+      data_handle_100_percent &&
+      LoadBitmap(*data_handle_100_percent, resource_id, bitmap,
+                 fell_back_to_1x)) {
+    *fell_back_to_1x = true;
+    return true;
+  }
+
   return false;
 }
 
@@ -819,9 +852,7 @@
 
   if (empty_image_.IsEmpty()) {
     // The placeholder bitmap is bright red so people notice the problem.
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(32, 32);
-    bitmap.eraseARGB(255, 255, 0, 0);
+    SkBitmap bitmap = CreateEmptyBitmap();
     empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
   }
   return empty_image_;
diff --git a/ui/base/resource/resource_bundle.h b/ui/base/resource/resource_bundle.h
index 76a5c6f..df812b1 100644
--- a/ui/base/resource/resource_bundle.h
+++ b/ui/base/resource/resource_bundle.h
@@ -439,6 +439,8 @@
 
   IdToStringMap overridden_locale_strings_;
 
+  bool is_test_resources_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(ResourceBundle);
 };
 
diff --git a/ui/base/resource/resource_bundle_auralinux.cc b/ui/base/resource/resource_bundle_auralinux.cc
index 8989a9e4..9b528b6 100644
--- a/ui/base/resource/resource_bundle_auralinux.cc
+++ b/ui/base/resource/resource_bundle_auralinux.cc
@@ -4,16 +4,6 @@
 
 #include "ui/base/resource/resource_bundle.h"
 
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "ui/base/layout.h"
-#include "ui/base/resource/resource_handle.h"
-#include "ui/base/ui_base_paths.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/gfx/display.h"
-#include "ui/gfx/image/image.h"
-
 namespace ui {
 
 void ResourceBundle::LoadCommonResources() {
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc
index 0fa6791..db9af3c 100644
--- a/ui/base/x/x11_util.cc
+++ b/ui/base/x/x11_util.cc
@@ -1251,7 +1251,7 @@
   if (!ui::GetOuterWindowBounds(window, &window_rect))
     return false;
 
-  // We can't use gfx::Screen here because we don't have an aura::Window. So
+  // We can't use display::Screen here because we don't have an aura::Window. So
   // instead just look at the size of the default display.
   //
   // TODO(erg): Actually doing this correctly would require pulling out xrandr,
diff --git a/ui/gfx/image/image_skia.cc b/ui/gfx/image/image_skia.cc
index d91b782f..fc5cef476 100644
--- a/ui/gfx/image/image_skia.cc
+++ b/ui/gfx/image/image_skia.cc
@@ -234,7 +234,7 @@
     if (scale != resource_scale) {
       std::vector<ImageSkiaRep>::iterator iter =
           FindRepresentation(resource_scale, fetch_new_image);
-      DCHECK(iter != image_reps_.end());
+      CHECK(iter != image_reps_.end());
       image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale);
     } else {
       image = source_->GetImageForScale(scale);
diff --git a/ui/gl/gl_implementation.cc b/ui/gl/gl_implementation.cc
index dfb1548b..9c9289f1 100644
--- a/ui/gl/gl_implementation.cc
+++ b/ui/gl/gl_implementation.cc
@@ -205,9 +205,10 @@
   const char* version_str =
       reinterpret_cast<const char*>(glGetString(GL_VERSION));
   unsigned major_version, minor_version;
-  bool is_es, is_es3;
+  bool is_es, is_es2, is_es3;
   gfx::GLVersionInfo::ParseVersionString(
-      version_str, &major_version, &minor_version, &is_es, &is_es3);
+      version_str, &major_version, &minor_version,
+      &is_es, &is_es2, &is_es3);
   return is_es || major_version < 3;
 }
 
diff --git a/ui/gl/gl_version_info.cc b/ui/gl/gl_version_info.cc
index 42616509..3a10437 100644
--- a/ui/gl/gl_version_info.cc
+++ b/ui/gl/gl_version_info.cc
@@ -43,11 +43,12 @@
       is_angle(false),
       major_version(0),
       minor_version(0),
+      is_es2(false),
       is_es3(false),
       is_desktop_core_profile(false) {
   if (version_str) {
     ParseVersionString(version_str, &major_version, &minor_version,
-                       &is_es, &is_es3);
+                       &is_es, &is_es2, &is_es3);
   }
   if (renderer_str) {
     is_angle = base::StartsWith(renderer_str, "ANGLE",
@@ -59,11 +60,13 @@
                                        unsigned* major_version,
                                        unsigned* minor_version,
                                        bool* is_es,
+                                       bool* is_es2,
                                        bool* is_es3) {
   // Make sure the outputs are always initialized.
   *major_version = 0;
   *minor_version = 0;
   *is_es = false;
+  *is_es2 = false;
   *is_es3 = false;
   if (!version_str)
     return;
@@ -81,6 +84,8 @@
       *minor_version = minor;
     }
   }
+  if (*is_es && *major_version == 2)
+    *is_es2 = true;
   if (*is_es && *major_version == 3)
     *is_es3 = true;
   DCHECK(major_version != 0);
diff --git a/ui/gl/gl_version_info.h b/ui/gl/gl_version_info.h
index c56825e8..64e4e31 100644
--- a/ui/gl/gl_version_info.h
+++ b/ui/gl/gl_version_info.h
@@ -55,12 +55,14 @@
                                  unsigned* major_version,
                                  unsigned* minor_version,
                                  bool* is_es,
+                                 bool* is_es2,
                                  bool* is_es3);
 
   bool is_es;
   bool is_angle;
   unsigned major_version;
   unsigned minor_version;
+  bool is_es2;
   bool is_es3;
   bool is_desktop_core_profile;
 
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 7b5c6ee..b32b921 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -435,14 +435,4 @@
   canvas->drawRect(gfx::RectToSkRect(rect), paint);
 }
 
-// static
-std::unique_ptr<gfx::Canvas> CommonThemeCreateCanvas(SkCanvas* sk_canvas) {
-  // TODO(pkotwicz): Do something better and don't infer device
-  // scale factor from canvas scale.
-  SkMatrix m = sk_canvas->getTotalMatrix();
-  float device_scale = static_cast<float>(SkScalarAbs(m.getScaleX()));
-  return base::WrapUnique(
-      new gfx::Canvas(skia::SharePtr(sk_canvas), device_scale));
-}
-
 }  // namespace ui
diff --git a/ui/native_theme/common_theme.h b/ui/native_theme/common_theme.h
index 9a10f8a..14e4a90 100644
--- a/ui/native_theme/common_theme.h
+++ b/ui/native_theme/common_theme.h
@@ -32,10 +32,6 @@
     const gfx::Rect& rect,
     const NativeTheme::MenuItemExtraParams& menu_item);
 
-// Creates a gfx::Canvas wrapping an SkCanvas.
-std::unique_ptr<gfx::Canvas> NATIVE_THEME_EXPORT
-CommonThemeCreateCanvas(SkCanvas* sk_canvas);
-
 }  // namespace ui
 
 #endif  // UI_NATIVE_THEME_COMMON_THEME_H_
diff --git a/ui/native_theme/native_theme_base.cc b/ui/native_theme/native_theme_base.cc
index cc94eaf..0d0d57a 100644
--- a/ui/native_theme/native_theme_base.cc
+++ b/ui/native_theme/native_theme_base.cc
@@ -918,24 +918,6 @@
   rect->iset(rect->x(), rect->y(), rect->right() - 1, rect->bottom() - 1);
 }
 
-void NativeThemeBase::DrawImageInt(
-    SkCanvas* sk_canvas, const gfx::ImageSkia& image,
-    int src_x, int src_y, int src_w, int src_h,
-    int dest_x, int dest_y, int dest_w, int dest_h) const {
-  std::unique_ptr<gfx::Canvas> canvas(CommonThemeCreateCanvas(sk_canvas));
-  canvas->DrawImageInt(image, src_x, src_y, src_w, src_h,
-      dest_x, dest_y, dest_w, dest_h, true);
-}
-
-void NativeThemeBase::DrawTiledImage(SkCanvas* sk_canvas,
-    const gfx::ImageSkia& image,
-    int src_x, int src_y, float tile_scale_x, float tile_scale_y,
-    int dest_x, int dest_y, int w, int h) const {
-  std::unique_ptr<gfx::Canvas> canvas(CommonThemeCreateCanvas(sk_canvas));
-  canvas->TileImageInt(image, src_x, src_y, tile_scale_x,
-      tile_scale_y, dest_x, dest_y, w, h);
-}
-
 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
                                              SkScalar saturate_amount,
                                              SkScalar brighten_amount) const {
diff --git a/ui/native_theme/native_theme_base.h b/ui/native_theme/native_theme_base.h
index 0f6e81f..675900f6 100644
--- a/ui/native_theme/native_theme_base.h
+++ b/ui/native_theme/native_theme_base.h
@@ -155,16 +155,6 @@
   }
   int scrollbar_button_length() const { return scrollbar_button_length_; }
 
-  void DrawImageInt(SkCanvas* canvas, const gfx::ImageSkia& image,
-                    int src_x, int src_y, int src_w, int src_h,
-                    int dest_x, int dest_y, int dest_w, int dest_h) const;
-
-  void DrawTiledImage(SkCanvas* canvas,
-                      const gfx::ImageSkia& image,
-                      int src_x, int src_y,
-                      float tile_scale_x, float tile_scale_y,
-                      int dest_x, int dest_y, int w, int h) const;
-
   SkColor SaturateAndBrighten(SkScalar* hsv,
                               SkScalar saturate_amount,
                               SkScalar brighten_amount) const;
diff --git a/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc b/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
index 64df5fe..d611c64 100644
--- a/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
+++ b/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
@@ -102,12 +102,20 @@
                     ui::MaterialDesignController::MATERIAL_HYBRID));
 
 TEST_P(InkDropAnimationControllerFactoryTest,
-       VerifyAllInkDropLayersRemovedAfterDestruction) {
+       VerifyInkDropLayersRemovedAfterDestructionWhenRippleIsActive) {
   ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING);
   ink_drop_animation_controller_.reset();
   EXPECT_EQ(0, test_ink_drop_host_.num_ink_drop_layers());
 }
 
+TEST_P(InkDropAnimationControllerFactoryTest,
+       VerifyInkDropLayersRemovedAfterDestructionWhenHoverIsActive) {
+  test_ink_drop_host_.set_should_show_hover(true);
+  ink_drop_animation_controller_->SetHovered(true);
+  ink_drop_animation_controller_.reset();
+  EXPECT_EQ(0, test_ink_drop_host_.num_ink_drop_layers());
+}
+
 TEST_P(InkDropAnimationControllerFactoryTest, StateIsHiddenInitially) {
   EXPECT_EQ(InkDropState::HIDDEN,
             ink_drop_animation_controller_->GetTargetInkDropState());
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.cc b/ui/views/animation/ink_drop_animation_controller_impl.cc
index 0cca4cd..62e953b 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.cc
+++ b/ui/views/animation/ink_drop_animation_controller_impl.cc
@@ -54,17 +54,17 @@
     InkDropHost* ink_drop_host)
     : ink_drop_host_(ink_drop_host),
       root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
+      root_layer_added_to_host_(false),
       is_hovered_(false),
       hover_after_animation_timer_(nullptr) {
   root_layer_->set_name("InkDropAnimationControllerImpl:RootLayer");
-  ink_drop_host_->AddInkDropLayer(root_layer_.get());
 }
 
 InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() {
   // Explicitly destroy the InkDropAnimation so that this still exists if
   // views::InkDropAnimationObserver methods are called on this.
   DestroyInkDropAnimation();
-  ink_drop_host_->RemoveInkDropLayer(root_layer_.get());
+  DestroyInkDropHover();
 }
 
 InkDropState InkDropAnimationControllerImpl::GetTargetInkDropState() const {
@@ -125,6 +125,7 @@
   ink_drop_animation_ = ink_drop_host_->CreateInkDropAnimation();
   ink_drop_animation_->set_observer(this);
   root_layer_->Add(ink_drop_animation_->GetRootLayer());
+  AddRootLayerToHostIfNeeded();
 }
 
 void InkDropAnimationControllerImpl::DestroyInkDropAnimation() {
@@ -132,6 +133,7 @@
     return;
   root_layer_->Remove(ink_drop_animation_->GetRootLayer());
   ink_drop_animation_.reset();
+  RemoveRootLayerFromHostIfNeeded();
 }
 
 void InkDropAnimationControllerImpl::CreateInkDropHover() {
@@ -140,20 +142,42 @@
   hover_ = ink_drop_host_->CreateInkDropHover();
   if (!hover_)
     return;
+  hover_->set_observer(this);
   root_layer_->Add(hover_->layer());
+  AddRootLayerToHostIfNeeded();
 }
 
 void InkDropAnimationControllerImpl::DestroyInkDropHover() {
   if (!hover_)
     return;
   root_layer_->Remove(hover_->layer());
+  hover_->set_observer(nullptr);
   hover_.reset();
+  RemoveRootLayerFromHostIfNeeded();
+}
+
+void InkDropAnimationControllerImpl::AddRootLayerToHostIfNeeded() {
+  DCHECK(hover_ || ink_drop_animation_);
+  if (!root_layer_added_to_host_) {
+    root_layer_added_to_host_ = true;
+    ink_drop_host_->AddInkDropLayer(root_layer_.get());
+  }
+}
+
+void InkDropAnimationControllerImpl::RemoveRootLayerFromHostIfNeeded() {
+  if (root_layer_added_to_host_ && !hover_ && !ink_drop_animation_) {
+    root_layer_added_to_host_ = false;
+    ink_drop_host_->RemoveInkDropLayer(root_layer_.get());
+  }
 }
 
 bool InkDropAnimationControllerImpl::IsHoverFadingInOrVisible() const {
   return hover_ && hover_->IsFadingInOrVisible();
 }
 
+// -----------------------------------------------------------------------------
+// views::InkDropAnimationObserver:
+
 void InkDropAnimationControllerImpl::AnimationStarted(
     InkDropState ink_drop_state) {}
 
@@ -174,6 +198,21 @@
   }
 }
 
+// -----------------------------------------------------------------------------
+// views::InkDropHoverObserver:
+
+void InkDropAnimationControllerImpl::AnimationStarted(
+    InkDropHover::AnimationType animation_type) {}
+
+void InkDropAnimationControllerImpl::AnimationEnded(
+    InkDropHover::AnimationType animation_type,
+    InkDropAnimationEndedReason reason) {
+  if (animation_type == InkDropHover::FADE_OUT &&
+      reason == InkDropAnimationEndedReason::SUCCESS) {
+    DestroyInkDropHover();
+  }
+}
+
 void InkDropAnimationControllerImpl::SetHoveredInternal(
     bool is_hovered,
     base::TimeDelta animation_duration,
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.h b/ui/views/animation/ink_drop_animation_controller_impl.h
index e075975..c47a0a2f 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.h
+++ b/ui/views/animation/ink_drop_animation_controller_impl.h
@@ -12,6 +12,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/animation/ink_drop_animation_controller.h"
 #include "ui/views/animation/ink_drop_animation_observer.h"
+#include "ui/views/animation/ink_drop_hover_observer.h"
 #include "ui/views/views_export.h"
 
 namespace base {
@@ -31,7 +32,8 @@
 // A functional implementation of an InkDropAnimationController.
 class VIEWS_EXPORT InkDropAnimationControllerImpl
     : public InkDropAnimationController,
-      public InkDropAnimationObserver {
+      public InkDropAnimationObserver,
+      public InkDropHoverObserver {
  public:
   // Constructs an ink drop controller that will attach the ink drop to the
   // given |ink_drop_host|.
@@ -66,6 +68,14 @@
   // Destroys the current |hover_|.
   void DestroyInkDropHover();
 
+  // Adds the |root_layer_| to the |ink_drop_host_| if it hasn't already been
+  // added.
+  void AddRootLayerToHostIfNeeded();
+
+  // Removes the |root_layer_| from the |ink_drop_host_| if no ink drop ripple
+  // or hover is active.
+  void RemoveRootLayerFromHostIfNeeded();
+
   // Returns true if the hover animation is in the process of fading in or
   // is visible.
   bool IsHoverFadingInOrVisible() const;
@@ -75,6 +85,11 @@
   void AnimationEnded(InkDropState ink_drop_state,
                       InkDropAnimationEndedReason reason) override;
 
+  // views::InkDropHoverObserver:
+  void AnimationStarted(InkDropHover::AnimationType animation_type) override;
+  void AnimationEnded(InkDropHover::AnimationType animation_type,
+                      InkDropAnimationEndedReason reason) override;
+
   // Enables or disables the hover state based on |is_hovered| and if an
   // animation is triggered it will be scheduled to have the given
   // |animation_duration|. If |explode| is true the hover will expand as it
@@ -102,6 +117,9 @@
   // from the InkDropHost.
   std::unique_ptr<ui::Layer> root_layer_;
 
+  // True when the |root_layer_| has been added to the |ink_drop_host_|.
+  bool root_layer_added_to_host_;
+
   // The current InkDropHover. Lazily created using CreateInkDropHover();
   std::unique_ptr<InkDropHover> hover_;
 
diff --git a/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc b/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc
index 8ceab47..fdee2bd6 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc
+++ b/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc
@@ -138,4 +138,104 @@
   EXPECT_FALSE(test_api_.IsHoverFadingInOrVisible());
 }
 
+TEST_F(InkDropAnimationControllerImplTest, LayersRemovedFromHostAfterHover) {
+  ink_drop_host_.set_should_show_hover(true);
+
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.SetHovered(true);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  test_api_.CompleteAnimations();
+
+  ink_drop_animation_controller_.SetHovered(false);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  test_api_.CompleteAnimations();
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+}
+
+TEST_F(InkDropAnimationControllerImplTest, LayersRemovedFromHostAfterInkDrop) {
+  ink_drop_host_.set_should_show_hover(true);
+
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_PENDING);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.AnimateToState(InkDropState::HIDDEN);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  test_api_.CompleteAnimations();
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+}
+
+TEST_F(InkDropAnimationControllerImplTest,
+       LayersAddedToHostWhenHoverOrInkDropVisible) {
+  ink_drop_host_.set_should_show_hover(true);
+
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.SetHovered(true);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_PENDING);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.AnimateToState(InkDropState::HIDDEN);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  test_api_.CompleteAnimations();
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  EXPECT_TRUE(task_runner_->HasPendingTask());
+  task_runner_->RunPendingTasks();
+
+  // Hover should be fading back in.
+  EXPECT_TRUE(test_api_.HasActiveAnimations());
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+}
+
+TEST_F(InkDropAnimationControllerImplTest,
+       LayersNotAddedToHostWhenHoverTimeFires) {
+  ink_drop_host_.set_should_show_hover(true);
+
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.SetHovered(true);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_PENDING);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.AnimateToState(InkDropState::HIDDEN);
+  test_api_.CompleteAnimations();
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_host_.set_should_show_hover(false);
+
+  EXPECT_TRUE(task_runner_->HasPendingTask());
+  task_runner_->RunPendingTasks();
+
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+}
+
+TEST_F(InkDropAnimationControllerImplTest,
+       LayersArentRemovedWhenPreemptingFadeOut) {
+  ink_drop_host_.set_should_show_hover(true);
+
+  EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.SetHovered(true);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  test_api_.CompleteAnimations();
+
+  ink_drop_animation_controller_.SetHovered(false);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+
+  ink_drop_animation_controller_.SetHovered(true);
+  EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers());
+}
+
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_animation_observer.h b/ui/views/animation/ink_drop_animation_observer.h
index bb306c9..831a6be 100644
--- a/ui/views/animation/ink_drop_animation_observer.h
+++ b/ui/views/animation/ink_drop_animation_observer.h
@@ -29,8 +29,8 @@
                               InkDropAnimationEndedReason reason) = 0;
 
  protected:
-  InkDropAnimationObserver() {}
-  virtual ~InkDropAnimationObserver() {}
+  InkDropAnimationObserver() = default;
+  virtual ~InkDropAnimationObserver() = default;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InkDropAnimationObserver);
diff --git a/ui/views/animation/ink_drop_hover.cc b/ui/views/animation/ink_drop_hover.cc
index f53a95a0..af98e61 100644
--- a/ui/views/animation/ink_drop_hover.cc
+++ b/ui/views/animation/ink_drop_hover.cc
@@ -9,6 +9,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/views/animation/ink_drop_hover_observer.h"
 #include "ui/views/animation/ink_drop_painted_layer_delegates.h"
 
 namespace views {
@@ -33,7 +34,8 @@
       last_animation_initiated_was_fade_in_(false),
       layer_delegate_(
           new RoundedRectangleLayerDelegate(color, size, corner_radius)),
-      layer_(new ui::Layer()) {
+      layer_(new ui::Layer()),
+      observer_(nullptr) {
   layer_->SetBounds(gfx::Rect(size));
   layer_->SetFillsBoundsOpaquely(false);
   layer_->set_delegate(layer_delegate_.get());
@@ -63,7 +65,7 @@
   return nullptr;
 }
 
-void InkDropHover::AnimateFade(HoverAnimationType animation_type,
+void InkDropHover::AnimateFade(AnimationType animation_type,
                                const base::TimeDelta& duration,
                                const gfx::Size& initial_size,
                                const gfx::Size& target_size) {
@@ -75,6 +77,8 @@
   // AnimationStartedCallback() returns true.
   ui::CallbackLayerAnimationObserver* animation_observer =
       new ui::CallbackLayerAnimationObserver(
+          base::Bind(&InkDropHover::AnimationStartedCallback,
+                     base::Unretained(this), animation_type),
           base::Bind(&InkDropHover::AnimationEndedCallback,
                      base::Unretained(this), animation_type));
 
@@ -115,13 +119,27 @@
   return transform;
 }
 
+void InkDropHover::AnimationStartedCallback(
+    AnimationType animation_type,
+    const ui::CallbackLayerAnimationObserver& observer) {
+  if (observer_)
+    observer_->AnimationStarted(animation_type);
+}
+
 bool InkDropHover::AnimationEndedCallback(
-    HoverAnimationType animation_type,
+    AnimationType animation_type,
     const ui::CallbackLayerAnimationObserver& observer) {
   // AnimationEndedCallback() may be invoked when this is being destroyed and
   // |layer_| may be null.
   if (animation_type == FADE_OUT && layer_)
     layer_->SetVisible(false);
+
+  if (observer_) {
+    observer_->AnimationEnded(animation_type,
+                              observer.aborted_count()
+                                  ? InkDropAnimationEndedReason::PRE_EMPTED
+                                  : InkDropAnimationEndedReason::SUCCESS);
+  }
   return true;
 }
 
diff --git a/ui/views/animation/ink_drop_hover.h b/ui/views/animation/ink_drop_hover.h
index cda2424..b2dc69c 100644
--- a/ui/views/animation/ink_drop_hover.h
+++ b/ui/views/animation/ink_drop_hover.h
@@ -27,17 +27,22 @@
 }  // namespace test
 
 class RoundedRectangleLayerDelegate;
+class InkDropHoverObserver;
 
 // Manages fade in/out animations for a painted Layer that is used to provide
 // visual feedback on ui::Views for mouse hover states.
 class VIEWS_EXPORT InkDropHover {
  public:
+  enum AnimationType { FADE_IN, FADE_OUT };
+
   InkDropHover(const gfx::Size& size,
                int corner_radius,
                const gfx::Point& center_point,
                SkColor color);
   virtual ~InkDropHover();
 
+  void set_observer(InkDropHoverObserver* observer) { observer_ = observer; }
+
   void set_explode_size(const gfx::Size& size) { explode_size_ = size; }
 
   // Returns true if the hover animation is either in the process of fading
@@ -62,12 +67,10 @@
  private:
   friend class test::InkDropHoverTestApi;
 
-  enum HoverAnimationType { FADE_IN, FADE_OUT };
-
   // Animates a fade in/out as specified by |animation_type| combined with a
   // transformation from the |initial_size| to the |target_size| over the given
   // |duration|.
-  void AnimateFade(HoverAnimationType animation_type,
+  void AnimateFade(AnimationType animation_type,
                    const base::TimeDelta& duration,
                    const gfx::Size& initial_size,
                    const gfx::Size& target_size);
@@ -75,9 +78,14 @@
   // Calculates the Transform to apply to |layer_| for the given |size|.
   gfx::Transform CalculateTransform(const gfx::Size& size) const;
 
+  // The callback that will be invoked when a fade in/out animation is started.
+  void AnimationStartedCallback(
+      AnimationType animation_type,
+      const ui::CallbackLayerAnimationObserver& observer);
+
   // The callback that will be invoked when a fade in/out animation is complete.
   bool AnimationEndedCallback(
-      HoverAnimationType animation_type,
+      AnimationType animation_type,
       const ui::CallbackLayerAnimationObserver& observer);
 
   // The size of the hover shape when fully faded in.
@@ -100,6 +108,8 @@
   // The visual hover layer that is painted by |layer_delegate_|.
   std::unique_ptr<ui::Layer> layer_;
 
+  InkDropHoverObserver* observer_;
+
   DISALLOW_COPY_AND_ASSIGN(InkDropHover);
 };
 
diff --git a/ui/views/animation/ink_drop_hover_observer.h b/ui/views/animation/ink_drop_hover_observer.h
new file mode 100644
index 0000000..4e31bc7
--- /dev/null
+++ b/ui/views/animation/ink_drop_hover_observer.h
@@ -0,0 +1,41 @@
+// 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_VIEWS_ANIMATION_INK_DROP_HOVER_OBSERVER_H_
+#define UI_VIEWS_ANIMATION_INK_DROP_HOVER_OBSERVER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "ui/views/animation/ink_drop_animation_ended_reason.h"
+#include "ui/views/animation/ink_drop_hover.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+
+// Observer to attach to an InkDropHover animation.
+class VIEWS_EXPORT InkDropHoverObserver {
+ public:
+  // An animation for the given |animation_type| has started.
+  virtual void AnimationStarted(InkDropHover::AnimationType animation_type) = 0;
+
+  // Notifies the observer that an animation for the given |animation_type| has
+  // finished and the reason for completion is given by |reason|. If |reason| is
+  // SUCCESS then the animation has progressed to its final frame however if
+  // |reason| is |PRE_EMPTED| then the animation was stopped before its final
+  // frame.
+  virtual void AnimationEnded(InkDropHover::AnimationType animation_type,
+                              InkDropAnimationEndedReason reason) = 0;
+
+ protected:
+  InkDropHoverObserver() = default;
+  virtual ~InkDropHoverObserver() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InkDropHoverObserver);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_ANIMATION_INK_DROP_HOVER_OBSERVER_H_
diff --git a/ui/views/animation/ink_drop_hover_unittest.cc b/ui/views/animation/ink_drop_hover_unittest.cc
index a72def9..eefe5a63 100644
--- a/ui/views/animation/ink_drop_hover_unittest.cc
+++ b/ui/views/animation/ink_drop_hover_unittest.cc
@@ -8,9 +8,12 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/views/animation/test/ink_drop_hover_test_api.h"
+#include "ui/views/animation/test/test_ink_drop_hover_observer.h"
 
 namespace views {
 namespace test {
@@ -21,41 +24,122 @@
   ~InkDropHoverTest() override;
 
  protected:
-  std::unique_ptr<InkDropHover> CreateInkDropHover() const;
+  // The test target.
+  std::unique_ptr<InkDropHover> ink_drop_hover_;
+
+  // Allows privileged access to the the |ink_drop_hover_|.
+  InkDropHoverTestApi test_api_;
+
+  // Observer of the test target.
+  TestInkDropHoverObserver observer_;
 
  private:
-  // Enables zero duration animations during the tests.
-  std::unique_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
-
   DISALLOW_COPY_AND_ASSIGN(InkDropHoverTest);
 };
 
-InkDropHoverTest::InkDropHoverTest() {
-  zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode(
-      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION));
+InkDropHoverTest::InkDropHoverTest()
+    : ink_drop_hover_(
+          new InkDropHover(gfx::Size(10, 10), 3, gfx::Point(), SK_ColorBLACK)),
+      test_api_(ink_drop_hover_.get()) {
+  ink_drop_hover_->set_observer(&observer_);
+
+  test_api_.SetDisableAnimationTimers(true);
 }
 
 InkDropHoverTest::~InkDropHoverTest() {}
 
-std::unique_ptr<InkDropHover> InkDropHoverTest::CreateInkDropHover() const {
-  return base::WrapUnique(
-      new InkDropHover(gfx::Size(10, 10), 3, gfx::Point(), SK_ColorBLACK));
-}
-
 TEST_F(InkDropHoverTest, InitialStateAfterConstruction) {
-  std::unique_ptr<InkDropHover> ink_drop_hover = CreateInkDropHover();
-  EXPECT_FALSE(ink_drop_hover->IsFadingInOrVisible());
+  EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible());
 }
 
 TEST_F(InkDropHoverTest, IsHoveredStateTransitions) {
-  std::unique_ptr<InkDropHover> ink_drop_hover = CreateInkDropHover();
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(ink_drop_hover_->IsFadingInOrVisible());
 
-  ink_drop_hover->FadeIn(base::TimeDelta::FromMilliseconds(0));
-  EXPECT_TRUE(ink_drop_hover->IsFadingInOrVisible());
+  test_api_.CompleteAnimations();
+  EXPECT_TRUE(ink_drop_hover_->IsFadingInOrVisible());
 
-  ink_drop_hover->FadeOut(base::TimeDelta::FromMilliseconds(0),
-                          false /* explode */);
-  EXPECT_FALSE(ink_drop_hover->IsFadingInOrVisible());
+  ink_drop_hover_->FadeOut(base::TimeDelta::FromSeconds(1),
+                           false /* explode */);
+  EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible());
+
+  test_api_.CompleteAnimations();
+  EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible());
+}
+
+TEST_F(InkDropHoverTest, VerifyObserversAreNotified) {
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+
+  EXPECT_EQ(1, observer_.last_animation_started_ordinal());
+  EXPECT_FALSE(observer_.AnimationHasEnded());
+
+  test_api_.CompleteAnimations();
+
+  EXPECT_TRUE(observer_.AnimationHasEnded());
+  EXPECT_EQ(2, observer_.last_animation_ended_ordinal());
+}
+
+TEST_F(InkDropHoverTest, VerifyObserversAreNotifiedWithCorrectAnimationType) {
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+
+  EXPECT_TRUE(observer_.AnimationHasStarted());
+  EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_started_context());
+
+  test_api_.CompleteAnimations();
+  EXPECT_TRUE(observer_.AnimationHasEnded());
+  EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_started_context());
+
+  ink_drop_hover_->FadeOut(base::TimeDelta::FromSeconds(1),
+                           false /* explode */);
+  EXPECT_EQ(InkDropHover::FADE_OUT, observer_.last_animation_started_context());
+
+  test_api_.CompleteAnimations();
+  EXPECT_EQ(InkDropHover::FADE_OUT, observer_.last_animation_started_context());
+}
+
+TEST_F(InkDropHoverTest, VerifyObserversAreNotifiedOfSuccessfulAnimations) {
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+  test_api_.CompleteAnimations();
+
+  EXPECT_EQ(2, observer_.last_animation_ended_ordinal());
+  EXPECT_EQ(InkDropAnimationEndedReason::SUCCESS,
+            observer_.last_animation_ended_reason());
+}
+
+TEST_F(InkDropHoverTest, VerifyObserversAreNotifiedOfPreemptedAnimations) {
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+  ink_drop_hover_->FadeOut(base::TimeDelta::FromSeconds(1),
+                           false /* explode */);
+
+  EXPECT_EQ(2, observer_.last_animation_ended_ordinal());
+  EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_ended_context());
+  EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED,
+            observer_.last_animation_ended_reason());
+}
+
+// Confirms there is no crash.
+TEST_F(InkDropHoverTest, NullObserverIsSafe) {
+  ink_drop_hover_->set_observer(nullptr);
+
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+  test_api_.CompleteAnimations();
+
+  ink_drop_hover_->FadeOut(base::TimeDelta::FromMilliseconds(0),
+                           false /* explode */);
+  test_api_.CompleteAnimations();
+  EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible());
+}
+
+// Verify animations are aborted during deletion and the InkDropHoverObservers
+// are notified.
+TEST_F(InkDropHoverTest, AnimationsAbortedDuringDeletion) {
+  ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1));
+  ink_drop_hover_.reset();
+  EXPECT_EQ(1, observer_.last_animation_started_ordinal());
+  EXPECT_EQ(2, observer_.last_animation_ended_ordinal());
+  EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_ended_context());
+  EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED,
+            observer_.last_animation_ended_reason());
 }
 
 }  // namespace test
diff --git a/ui/views/animation/test/ink_drop_animation_test_api.cc b/ui/views/animation/test/ink_drop_animation_test_api.cc
index 86d35a6a7..0fbfb55 100644
--- a/ui/views/animation/test/ink_drop_animation_test_api.cc
+++ b/ui/views/animation/test/ink_drop_animation_test_api.cc
@@ -4,12 +4,6 @@
 
 #include "ui/views/animation/test/ink_drop_animation_test_api.h"
 
-#include "base/time/time.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animator.h"
-#include "ui/compositor/test/layer_animator_test_controller.h"
-#include "ui/views/animation/ink_drop_animation.h"
-
 namespace views {
 namespace test {
 
diff --git a/ui/views/animation/test/ink_drop_animation_test_api.h b/ui/views/animation/test/ink_drop_animation_test_api.h
index 611f3d7..d10c73d 100644
--- a/ui/views/animation/test/ink_drop_animation_test_api.h
+++ b/ui/views/animation/test/ink_drop_animation_test_api.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/time/time.h"
 #include "ui/compositor/test/multi_layer_animator_test_controller.h"
 #include "ui/compositor/test/multi_layer_animator_test_controller_delegate.h"
 
diff --git a/ui/views/animation/test/test_ink_drop_animation_observer.h b/ui/views/animation/test/test_ink_drop_animation_observer.h
index 8c0bce09..12c1d19 100644
--- a/ui/views/animation/test/test_ink_drop_animation_observer.h
+++ b/ui/views/animation/test/test_ink_drop_animation_observer.h
@@ -43,7 +43,7 @@
                       InkDropAnimationEndedReason reason) override;
 
  private:
-  // The type as this inherits from. Reduces verbosity in .cc file.
+  // The type this inherits from. Reduces verbosity in .cc file.
   using ObserverHelper = TestInkDropAnimationObserverHelper<InkDropState>;
 
   // The value of InkDropAnimation::GetTargetInkDropState() the last time an
diff --git a/ui/views/animation/test/test_ink_drop_hover_observer.cc b/ui/views/animation/test/test_ink_drop_hover_observer.cc
new file mode 100644
index 0000000..a3e4f772
--- /dev/null
+++ b/ui/views/animation/test/test_ink_drop_hover_observer.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium 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/views/animation/test/test_ink_drop_hover_observer.h"
+
+#include "ui/views/animation/ink_drop_hover.h"
+
+namespace views {
+namespace test {
+
+TestInkDropHoverObserver::TestInkDropHoverObserver() {}
+
+void TestInkDropHoverObserver::AnimationStarted(
+    InkDropHover::AnimationType animation_type) {
+  ObserverHelper::OnAnimationStarted(animation_type);
+}
+
+void TestInkDropHoverObserver::AnimationEnded(
+    InkDropHover::AnimationType animation_type,
+    InkDropAnimationEndedReason reason) {
+  ObserverHelper::OnAnimationEnded(animation_type, reason);
+}
+
+}  // namespace test
+}  // namespace views
diff --git a/ui/views/animation/test/test_ink_drop_hover_observer.h b/ui/views/animation/test/test_ink_drop_hover_observer.h
new file mode 100644
index 0000000..c60d84cd
--- /dev/null
+++ b/ui/views/animation/test/test_ink_drop_hover_observer.h
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium 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_VIEWS_ANIMATION_TEST_TEST_INK_DROP_HOVER_OBSERVER_H_
+#define UI_VIEWS_ANIMATION_TEST_TEST_INK_DROP_HOVER_OBSERVER_H_
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/animation/ink_drop_hover.h"
+#include "ui/views/animation/ink_drop_hover_observer.h"
+#include "ui/views/animation/ink_drop_state.h"
+#include "ui/views/animation/test/test_ink_drop_animation_observer_helper.h"
+
+namespace views {
+namespace test {
+
+// Simple InkDropHoverObserver test double that tracks if InkDropHoverObserver
+// methods are invoked and the parameters used for the last invocation.
+class TestInkDropHoverObserver
+    : public InkDropHoverObserver,
+      public TestInkDropAnimationObserverHelper<InkDropHover::AnimationType> {
+ public:
+  TestInkDropHoverObserver();
+  ~TestInkDropHoverObserver() override = default;
+
+  void set_ink_drop_hover(InkDropHover* ink_drop_hover) {
+    ink_drop_hover_ = ink_drop_hover;
+  }
+
+  // InkDropHoverObserver:
+  void AnimationStarted(InkDropHover::AnimationType animation_type) override;
+  void AnimationEnded(InkDropHover::AnimationType animation_type,
+                      InkDropAnimationEndedReason reason) override;
+
+ private:
+  // The type this inherits from. Reduces verbosity in .cc file.
+  using ObserverHelper =
+      TestInkDropAnimationObserverHelper<InkDropHover::AnimationType>;
+
+  // An InkDropHover to spy info from when notifications are handled.
+  InkDropHover* ink_drop_hover_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(TestInkDropHoverObserver);
+};
+
+}  // namespace test
+}  // namespace views
+
+#endif  // UI_VIEWS_ANIMATION_TEST_TEST_INK_DROP_HOVER_OBSERVER_H_
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 0ccff9d1..6fd25948 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -18,11 +18,11 @@
 #include "ui/base/hit_test.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/input_method_factory.h"
-#include "ui/gfx/display.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/geometry/dip_util.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 #import "ui/gfx/mac/nswindow_frame_controls.h"
-#include "ui/gfx/screen.h"
 #import "ui/views/cocoa/bridged_content_view.h"
 #import "ui/views/cocoa/cocoa_mouse_capture.h"
 #include "ui/views/cocoa/tooltip_manager_mac.h"
@@ -107,8 +107,8 @@
 int kWindowPropertiesKey;
 
 float GetDeviceScaleFactorFromView(NSView* view) {
-  gfx::Display display =
-      gfx::Screen::GetScreen()->GetDisplayNearestWindow(view);
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(view);
   DCHECK(display.is_valid());
   return display.device_scale_factor();
 }
diff --git a/ui/views/event_monitor_mac.mm b/ui/views/event_monitor_mac.mm
index 37a23bd..8fb0e0e 100644
--- a/ui/views/event_monitor_mac.mm
+++ b/ui/views/event_monitor_mac.mm
@@ -8,10 +8,10 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_handler.h"
 #include "ui/events/event_utils.h"
-#include "ui/gfx/screen.h"
 
 namespace views {
 
@@ -30,7 +30,7 @@
 
 // static
 gfx::Point EventMonitor::GetLastMouseLocation() {
-  return gfx::Screen::GetScreen()->GetCursorScreenPoint();
+  return display::Screen::GetScreen()->GetCursorScreenPoint();
 }
 
 EventMonitorMac::EventMonitorMac(ui::EventHandler* event_handler,
diff --git a/ui/views/examples/examples_main.cc b/ui/views/examples/examples_main.cc
index 206bcda..8151ed72 100644
--- a/ui/views/examples/examples_main.cc
+++ b/ui/views/examples/examples_main.cc
@@ -97,7 +97,8 @@
     wm::WMState wm_state;
 #endif
 #if !defined(OS_CHROMEOS) && defined(USE_AURA)
-    std::unique_ptr<gfx::Screen> desktop_screen(views::CreateDesktopScreen());
+    std::unique_ptr<display::Screen> desktop_screen(
+        views::CreateDesktopScreen());
     display::Screen::SetScreenInstance(desktop_screen.get());
 #endif
 
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index a33d28e..0791a6c 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -128,14 +128,6 @@
   }
 }
 
-group("tests") {
-  testonly = true
-
-  deps = [
-    ":views_mus_unittests",
-  ]
-}
-
 test("views_mus_unittests") {
   testonly = true
 
diff --git a/ui/views/mus/screen_mus.cc b/ui/views/mus/screen_mus.cc
index 44a6df7..eb853388 100644
--- a/ui/views/mus/screen_mus.cc
+++ b/ui/views/mus/screen_mus.cc
@@ -106,7 +106,8 @@
     DCHECK(display_manager_.encountered_error() || !success);
     // In this case we install a default display and assume the process is
     // going to exit shortly so that the real value doesn't matter.
-    displays_.push_back(gfx::Display(0xFFFFFFFF, gfx::Rect(0, 0, 801, 802)));
+    displays_.push_back(
+        display::Display(0xFFFFFFFF, gfx::Rect(0, 0, 801, 802)));
   }
 }
 
diff --git a/ui/views/mus/screen_mus_unittest.cc b/ui/views/mus/screen_mus_unittest.cc
index fe80a72b..6c93020c 100644
--- a/ui/views/mus/screen_mus_unittest.cc
+++ b/ui/views/mus/screen_mus_unittest.cc
@@ -5,7 +5,7 @@
 #include "ui/views/mus/screen_mus.h"
 
 #include "base/command_line.h"
-#include "ui/gfx/screen.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/switches.h"
 #include "ui/views/mus/window_manager_connection.h"
 #include "ui/views/test/scoped_views_test_helper.h"
@@ -19,10 +19,10 @@
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kForceDeviceScaleFactor, "2");
   ScopedViewsTestHelper test_helper;
-  gfx::Screen* screen = gfx::Screen::GetScreen();
-  std::vector<gfx::Display> displays = screen->GetAllDisplays();
+  display::Screen* screen = display::Screen::GetScreen();
+  std::vector<display::Display> displays = screen->GetAllDisplays();
   ASSERT_FALSE(displays.empty());
-  for (const gfx::Display& display : displays) {
+  for (const display::Display& display : displays) {
     EXPECT_EQ(2.f, display.device_scale_factor());
     EXPECT_EQ(display.work_area(), display.bounds());
   }
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index eca6e61..1a58df7 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -32,6 +32,7 @@
       'animation/ink_drop_host_view.h',
       'animation/ink_drop_hover.cc',
       'animation/ink_drop_hover.h',
+      'animation/ink_drop_hover_observer.h',
       'animation/ink_drop_painted_layer_delegates.cc',
       'animation/ink_drop_painted_layer_delegates.h',
       'animation/ink_drop_state.cc',
@@ -505,6 +506,8 @@
       'animation/test/test_ink_drop_animation_observer_helper.h',
       'animation/test/test_ink_drop_host.cc',
       'animation/test/test_ink_drop_host.h',
+      'animation/test/test_ink_drop_hover_observer.cc',
+      'animation/test/test_ink_drop_hover_observer.h',
       'animation/test/test_ink_drop_delegate.cc',
       'animation/test/test_ink_drop_delegate.h',
       'controls/textfield/textfield_test_api.cc',
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 d827e3b..39aad49 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
@@ -166,6 +166,7 @@
       window_mapped_(false),
       is_fullscreen_(false),
       is_always_on_top_(false),
+      is_override_redirect_(false),
       use_native_frame_(false),
       should_maximize_after_map_(false),
       use_argb_visual_(false),
@@ -969,6 +970,11 @@
   if (window_mapped_) {
     XWithdrawWindow(xdisplay_, xwindow_, 0);
     window_mapped_ = false;
+    if (is_override_redirect_) {
+      // If we're override-redirect, we won't receive an UnmapNotify message,
+      // so run the unmap handler directly.
+      OnX11WindowUnmapped();
+    }
   }
   native_widget_delegate_->OnNativeWidgetVisibilityChanged(false);
 }
@@ -1123,6 +1129,8 @@
   if (swa.override_redirect)
     attribute_mask |= CWOverrideRedirect;
 
+  is_override_redirect_ = swa.override_redirect;
+
   Visual* visual;
   int depth;
   ui::ChooseVisualForWindow(&visual, &depth);
@@ -1382,6 +1390,38 @@
   }
 }
 
+void DesktopWindowTreeHostX11::OnX11WindowMapped() {
+  if (!window_mapped_) {
+    window_mapped_ = true;
+
+    FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
+                      observer_list_,
+                      OnWindowMapped(xwindow_));
+
+    UpdateMinAndMaxSize();
+
+    // Some WMs only respect maximize hints after the window has been mapped.
+    // Check whether we need to re-do a maximization.
+    if (should_maximize_after_map_) {
+      Maximize();
+      should_maximize_after_map_ = false;
+    }
+  }
+
+  // If we're an override redirect window, we need to perform a full redraw
+  // because on AMD drivers we might get a MapNotify event after performing the
+  // initial draw, which can result in a blank window. crbug.com/606661.
+  if (is_override_redirect_)
+    compositor()->ScheduleFullRedraw();
+}
+
+void DesktopWindowTreeHostX11::OnX11WindowUnmapped() {
+  window_mapped_ = false;
+  FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
+                    observer_list_,
+                    OnWindowUnmapped(xwindow_));
+}
+
 void DesktopWindowTreeHostX11::UpdateMinAndMaxSize() {
   if (!window_mapped_)
     return;
@@ -1654,12 +1694,17 @@
   }
 
   XMapWindow(xdisplay_, xwindow_);
-
-  // We now block until our window is mapped. Some X11 APIs will crash and
-  // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
-  // asynchronous.
-  if (ui::X11EventSource::GetInstance())
-    ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
+  if (is_override_redirect_) {
+    // Override redirect windows don't get routed through the window manager;
+    // we won't reliably get a MapNotify.
+    OnX11WindowMapped();
+  } else {
+    // We now block until our window is mapped. Some X11 APIs will crash and
+    // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
+    // asynchronous.
+    if (ui::X11EventSource::GetInstance())
+      ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
+  }
 }
 
 void DesktopWindowTreeHostX11::SetWindowTransparency() {
@@ -1873,27 +1918,11 @@
       break;
     }
     case MapNotify: {
-      window_mapped_ = true;
-
-      FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
-                        observer_list_,
-                        OnWindowMapped(xwindow_));
-
-      UpdateMinAndMaxSize();
-
-      // Some WMs only respect maximize hints after the window has been mapped.
-      // Check whether we need to re-do a maximization.
-      if (should_maximize_after_map_) {
-        Maximize();
-        should_maximize_after_map_ = false;
-      }
-
+      OnX11WindowMapped();
       break;
     }
     case UnmapNotify: {
-      FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
-                        observer_list_,
-                        OnWindowUnmapped(xwindow_));
+      OnX11WindowUnmapped();
       break;
     }
     case ClientMessage: {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index 1d3cbd7..30d337d 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -184,6 +184,13 @@
   // Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated.
   void OnFrameExtentsUpdated();
 
+  // Called when |xwindow_| is mapped, either directly (in the case of
+  // override-redirect windows) or in response to a MapNotify event.
+  void OnX11WindowMapped();
+
+  // Called when |xwindow_| is unmapped.
+  void OnX11WindowUnmapped();
+
   // Updates |xwindow_|'s minimum and maximum size.
   void UpdateMinAndMaxSize();
 
@@ -289,6 +296,9 @@
   // True if the window should stay on top of most other windows.
   bool is_always_on_top_;
 
+  // True if the window has the override_redirect bit set.
+  bool is_override_redirect_;
+
   // True if the window has title-bar / borders provided by the window manager.
   bool use_native_frame_;
 
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 0b41b9ca..65fc1e5 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -89,7 +89,7 @@
   void SimulateFrameSwap(const gfx::Size& size) {
     const float kScaleFactor = 1.0f;
     bridge_->compositor_widget_->GotFrame(
-        0, base::ScopedCFTypeRef<IOSurfaceRef>(), size, kScaleFactor);
+        0, false, 0, base::ScopedCFTypeRef<IOSurfaceRef>(), size, kScaleFactor);
     bridge_->AcceleratedWidgetSwapCompleted();
   }
 
diff --git a/ui/webui/resources/PRESUBMIT.py b/ui/webui/resources/PRESUBMIT.py
new file mode 100644
index 0000000..2ac87faf
--- /dev/null
+++ b/ui/webui/resources/PRESUBMIT.py
@@ -0,0 +1,28 @@
+# Copyright 2016 The Chromium 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 re
+
+
+def PostUploadHook(cl, change, output_api):
+  rietveld_obj = cl.RpcServer()
+  description = rietveld_obj.get_description(cl.issue)
+
+  existing_bots = (change.CQ_INCLUDE_TRYBOTS or '').split(';')
+  clean_bots = set(filter(None, map(lambda s: s.strip(), existing_bots)))
+  new_bots = clean_bots | set(['tryserver.chromium.linux:closure_compilation'])
+  new_tag = 'CQ_INCLUDE_TRYBOTS=%s' % ';'.join(new_bots)
+
+  if clean_bots:
+    tag_reg = '^CQ_INCLUDE_TRYBOTS=.*$'
+    new_description = re.sub(tag_reg, new_tag, description, flags=re.M | re.I)
+  else:
+    new_description = description + '\n' + new_tag
+
+  if new_description == description:
+    return []
+
+  rietveld_obj.update_description(cl.issue, new_description)
+  return [output_api.PresubmitNotifyResult(
+      'Automatically added optional Closure bots to run on CQ.')]
diff --git a/ui/webui/resources/polymer_resources.grdp b/ui/webui/resources/polymer_resources.grdp
index bad58065..9090e2a48 100644
--- a/ui/webui/resources/polymer_resources.grdp
+++ b/ui/webui/resources/polymer_resources.grdp
@@ -143,12 +143,6 @@
   <structure name="IDR_POLYMER_1_0_IRON_ICONSET_SVG_IRON_ICONSET_SVG_HTML"
              file="../../../third_party/polymer/v1_0/components-chromium/iron-iconset-svg/iron-iconset-svg.html"
              type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_IRON_IMAGE_IRON_IMAGE_EXTRACTED_JS"
-             file="../../../third_party/polymer/v1_0/components-chromium/iron-image/iron-image-extracted.js"
-             type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_IRON_IMAGE_IRON_IMAGE_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/iron-image/iron-image.html"
-             type="chrome_html" />
   <structure name="IDR_POLYMER_1_0_IRON_INPUT_IRON_INPUT_EXTRACTED_JS"
              file="../../../third_party/polymer/v1_0/components-chromium/iron-input/iron-input-extracted.js"
              type="chrome_html" />
@@ -425,12 +419,6 @@
   <structure name="IDR_POLYMER_1_0_PAPER_BUTTON_PAPER_BUTTON_HTML"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-button/paper-button.html"
              type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_PAPER_CARD_PAPER_CARD_EXTRACTED_JS"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-card/paper-card-extracted.js"
-             type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_PAPER_CARD_PAPER_CARD_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-card/paper-card.html"
-             type="chrome_html" />
   <structure name="IDR_POLYMER_1_0_PAPER_CHECKBOX_PAPER_CHECKBOX_EXTRACTED_JS"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-checkbox/paper-checkbox-extracted.js"
              type="chrome_html" />
@@ -701,12 +689,6 @@
   <structure name="IDR_POLYMER_1_0_PAPER_TOOLBAR_PAPER_TOOLBAR_HTML"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-toolbar/paper-toolbar.html"
              type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_PAPER_TOOLTIP_PAPER_TOOLTIP_EXTRACTED_JS"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip-extracted.js"
-             type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_PAPER_TOOLTIP_PAPER_TOOLTIP_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip.html"
-             type="chrome_html" />
   <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_EXTRACTED_JS"
              file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer-extracted.js"
              type="chrome_html" />