diff --git a/DEPS b/DEPS
index bd7999e..d4454b5 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,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': 'e305cc1f2a44e47d6a0dcc0ff34e2692349aed5d',
+  'skia_revision': '56ff9a1c9dfdaf1cd6db3b62a5c2b739cd242032',
   # 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': '285b9f5a26decb9cc11e14e2101ed707a321b542',
+  'v8_revision': 'b55ff5c9e19b3cc0a945ed363ea926f3406e6af4',
   # 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.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '0d73909e89a5c93917b9cb73fe5c03c484f2793d',
+  'pdfium_revision': 'c7739322e5cdcc5779bdde2a1560ea3dee891e51',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # 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': '6a5ce5c1dff46439e735135316e21b7355dde479',
+  'catapult_revision': '788644ef37e7f17a8d6183a5e19cd66b5953c1d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/tools/cts_config/webview_cts_gcs_path.json b/android_webview/tools/cts_config/webview_cts_gcs_path.json
index ad22503..5abb2fa35 100644
--- a/android_webview/tools/cts_config/webview_cts_gcs_path.json
+++ b/android_webview/tools/cts_config/webview_cts_gcs_path.json
@@ -2,14 +2,17 @@
   "arm_64": {
     "L": {
         "filename": "arm_64/L/android-cts-arm_64-3500656.zip",
+        "apk": "arm_64/L/3500656/CtsWebkitTestCases.apk",
         "_origin": "aosp-lollipop-mr1-cts-dev@3500656"
     },
     "M": {
         "filename": "arm_64/M/android-cts-arm_64-3505727.zip",
+        "apk": "arm_64/M/3505727/CtsWebkitTestCases.apk",
         "_origin": "aosp-marshmallow-cts-dev@3505727"
     },
     "N": {
         "filename": "arm_64/N/android-cts-arm_64-3367285.zip",
+        "apk": "arm_64/N/3367285/CtsWebkitTestCases.apk",
         "_origin": "aosp-nougat-cts-release@3367285"
     }
   }
diff --git a/android_webview/tools/pylintrc b/android_webview/tools/pylintrc
index 19d502ce..d14f91a 100644
--- a/android_webview/tools/pylintrc
+++ b/android_webview/tools/pylintrc
@@ -15,6 +15,7 @@
 disable=
   fixme,
   invalid-name,
+  locally-disabled,
   missing-docstring,
   too-few-public-methods,
   too-many-arguments,
diff --git a/android_webview/tools/run_cts.py b/android_webview/tools/run_cts.py
new file mode 100755
index 0000000..07fc8cf2
--- /dev/null
+++ b/android_webview/tools/run_cts.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Runs the CTS test APKs stored in GS."""
+
+import argparse
+import json
+import os
+import shutil
+import sys
+import tempfile
+
+
+sys.path.append(os.path.join(
+    os.path.dirname(__file__), os.pardir, os.pardir, 'build', 'android'))
+import devil_chromium  # pylint: disable=import-error
+from devil.utils import cmd_helper  # pylint: disable=import-error
+
+sys.path.append(os.path.join(
+    os.path.dirname(__file__), os.pardir, os.pardir, 'build'))
+import find_depot_tools  # pylint: disable=import-error
+
+_CTS_BUCKET = 'gs://chromium-cts'
+
+_GSUTIL_PATH = os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gsutil.py')
+_TEST_RUNNER_PATH = os.path.join(
+    os.path.dirname(__file__), os.pardir, os.pardir,
+    'build', 'android', 'test_runner.py')
+
+_EXPECTED_FAILURES_FILE = os.path.join(
+    os.path.dirname(__file__), 'cts_config', 'expected_failure_on_bot.json')
+_WEBVIEW_CTS_GCS_PATH_FILE = os.path.join(
+    os.path.dirname(__file__), 'cts_config', 'webview_cts_gcs_path.json')
+
+
+def GetCtsPath(arch, platform):
+  """Gets relative path the CTS APK is stored at."""
+  with open(_WEBVIEW_CTS_GCS_PATH_FILE) as f:
+    cts_gcs_path_info = json.load(f)
+  try:
+    return cts_gcs_path_info[arch][platform]['apk']
+  except KeyError:
+    raise Exception('No CTS test available for arch:%s, android:%s' %
+                    (arch, platform))
+
+
+def GetExpectedFailures():
+  """Gets list of tests expected to fail."""
+  with open(_EXPECTED_FAILURES_FILE) as f:
+    expected_failures_info = json.load(f)
+  expected_failures = []
+  for class_name, methods in expected_failures_info.iteritems():
+    expected_failures.extend(['%s#%s' % (class_name, m['name'])
+                              for m in methods])
+  return expected_failures
+
+
+def DownloadAndRunCTS(args, test_runner_args):
+  base_cts_dir = None
+  delete_cts_dir = False
+  try:
+    relative_cts_path = GetCtsPath(args.arch, args.platform)
+
+    if args.apk_dir:
+      base_cts_dir = args.apk_dir
+    else:
+      base_cts_dir = tempfile.mkdtemp()
+      delete_cts_dir = True
+
+    local_cts_path = os.path.join(base_cts_dir, relative_cts_path)
+    google_storage_cts_path = '%s/%s' % (_CTS_BUCKET, relative_cts_path)
+
+    # Download CTS APK if needed.
+    if not os.path.exists(local_cts_path):
+      if cmd_helper.RunCmd(
+          [_GSUTIL_PATH, 'cp', google_storage_cts_path, local_cts_path]):
+        raise Exception('Error downloading CTS from Google Storage.')
+
+    test_runner_args += ['--test-apk', local_cts_path]
+    # TODO(mikecase): This doesn't work at all with the
+    # --gtest-filter test runner option currently. The
+    # filter options will just override eachother.
+    if args.skip_expected_failures:
+      test_runner_args += ['-f=-%s' % ':'.join(GetExpectedFailures())]
+    return cmd_helper.RunCmd(
+        [_TEST_RUNNER_PATH, 'instrumentation'] + test_runner_args)
+  finally:
+    if delete_cts_dir and base_cts_dir:
+      shutil.rmtree(base_cts_dir)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--arch',
+      choices=['arm_64'],
+      default='arm_64',
+      help='Arch for CTS tests.')
+  parser.add_argument(
+      '--platform',
+      choices=['L', 'M', 'N'],
+      required=True,
+      help='Android platform version for CTS tests.')
+  parser.add_argument(
+      '--skip-expected-failures',
+      action='store_true',
+      help='Option to skip all tests that are expected to fail.')
+  parser.add_argument(
+      '--apk-dir',
+      help='Directory to load/save CTS APKs. Will try to load CTS APK '
+           'from this directory before downloading from Google Storage '
+           'and will then cache APK here.')
+
+  args, test_runner_args = parser.parse_known_args()
+  devil_chromium.Initialize()
+
+  return DownloadAndRunCTS(args, test_runner_args)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/ash/common/ash_switches.cc b/ash/common/ash_switches.cc
index 10e9190..d3d9571 100644
--- a/ash/common/ash_switches.cc
+++ b/ash/common/ash_switches.cc
@@ -60,11 +60,6 @@
 // Enables the palette on every display, instead of only the internal one.
 const char kAshEnablePaletteOnAllDisplays[] =
     "ash-enable-palette-on-all-displays";
-
-// Enables tablet power button behavior for convertible/tablet devices.
-// TODO(warx): Enable or remove this switch once crbug.com/633304 is fully
-// finished.
-const char kAshEnableTabletPowerButton[] = "ash-enable-tablet-power-button";
 #endif
 
 // Enables the observation of accelerometer events to enter touch-view mode.
diff --git a/ash/common/ash_switches.h b/ash/common/ash_switches.h
index 780d236..8a145f6 100644
--- a/ash/common/ash_switches.h
+++ b/ash/common/ash_switches.h
@@ -32,7 +32,6 @@
 ASH_EXPORT extern const char kAshEnableMagnifierKeyScroller[];
 ASH_EXPORT extern const char kAshEnablePalette[];
 ASH_EXPORT extern const char kAshEnablePaletteOnAllDisplays[];
-ASH_EXPORT extern const char kAshEnableTabletPowerButton[];
 #endif
 ASH_EXPORT extern const char kAshEnableTouchView[];
 ASH_EXPORT extern const char kAshEnableMirroredScreen[];
diff --git a/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc b/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
index 6675c9f..812c999 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
@@ -52,8 +52,6 @@
     dbus_setter->SetPowerManagerClient(base::WrapUnique(power_manager_client_));
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kAshEnableTouchViewTesting);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kAshEnableTabletPowerButton);
     AshTestBase::SetUp();
 
     lock_state_controller_ = Shell::GetInstance()->lock_state_controller();
diff --git a/ash/wm/power_button_controller.cc b/ash/wm/power_button_controller.cc
index 9ea85af9..8038dec 100644
--- a/ash/wm/power_button_controller.cc
+++ b/ash/wm/power_button_controller.cc
@@ -45,7 +45,9 @@
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kAshEnableTabletPowerButton)) {
+          switches::kAshEnableTouchView) ||
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kAshEnableTouchViewTesting)) {
     tablet_controller_.reset(
         new TabletPowerButtonController(lock_state_controller_));
   }
diff --git a/build/android/tombstones.py b/build/android/tombstones.py
index cba1e5a..f2b5904a 100755
--- a/build/android/tombstones.py
+++ b/build/android/tombstones.py
@@ -26,6 +26,10 @@
 from devil.utils import run_tests_helper
 from pylib import constants
 
+sys.path.insert(0, os.path.abspath(os.path.join(
+    constants.DIR_SOURCE_ROOT, 'tools', 'swarming_client')))
+from libs.logdog import bootstrap # pylint: disable=import-error
+
 
 _TZ_UTC = {'TZ': 'UTC'}
 
@@ -103,6 +107,7 @@
       return arch
   raise RuntimeError('Unknown device ABI: %s' % device_abi)
 
+
 def _ResolveSymbols(tombstone_data, include_stack, device_abi):
   """Run the stack tool for given tombstone input.
 
@@ -170,6 +175,7 @@
     resolved_tombstones.extend(tombstone)
   return resolved_tombstones
 
+
 def _GetTombstonesForDevice(device, resolve_all_tombstones,
                             include_stack_symbols,
                             wipe_tombstones):
@@ -216,6 +222,7 @@
 
   return ret
 
+
 def ClearAllTombstones(device):
   """Clear all tombstones in the device.
 
@@ -229,6 +236,7 @@
   for tombstone_file, _ in all_tombstones:
     _EraseTombstone(device, tombstone_file)
 
+
 def ResolveTombstones(device, resolve_all_tombstones, include_stack_symbols,
                       wipe_tombstones, jobs=4):
   """Resolve tombstones in the device.
@@ -239,6 +247,9 @@
     include_stack_symbols: Whether to include symbols for stack data.
     wipe_tombstones: Whether to wipe tombstones.
     jobs: Number of jobs to use when processing multiple crash stacks.
+
+  Returns:
+    A list of resolved tombstones.
   """
   return _ResolveTombstones(jobs,
                             _GetTombstonesForDevice(device,
@@ -246,6 +257,33 @@
                                                     include_stack_symbols,
                                                     wipe_tombstones))
 
+
+def LogdogTombstones(resolved_tombstones, stream_name):
+  """Save resolved tombstones to logdog and return the url.
+
+  Args:
+    stream_name: The name of the logdog stream that records tombstones.
+    resolved_tombstones: Resolved tombstones (output of ResolveTombstones).
+
+  Returns:
+    A url link to the recorded tombstones.
+  """
+  try:
+    tombstones_url = ''
+    stream_client = bootstrap.ButlerBootstrap.probe().stream_client()
+    with stream_client.text(stream_name) as logdog_stream:
+      for tombstones_line in resolved_tombstones:
+        logdog_stream.write(tombstones_line + '\n')
+      tombstones_url = logdog_stream.get_viewer_url(stream_name)
+  except bootstrap.NotBootstrappedError:
+    logging.exception('Error not bootstrapped. Failed to start logdog')
+  except (KeyError, ValueError) as e:
+    logging.exception('Error when creating stream_client/stream: %s.', e)
+  except Exception as e: # pylint: disable=broad-except
+    logging.exception('Unknown Error: %s.', e)
+  return tombstones_url
+
+
 def main():
   custom_handler = logging.StreamHandler(sys.stdout)
   custom_handler.setFormatter(run_tests_helper.CustomFormatter())
@@ -300,5 +338,6 @@
     for line in resolved_tombstones:
       logging.info(line)
 
+
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 8e6b7e5..95c03c6a 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -852,6 +852,7 @@
     "output/texture_mailbox_deleter_unittest.cc",
     "playback/discardable_image_map_unittest.cc",
     "playback/display_item_list_unittest.cc",
+    "playback/image_hijack_canvas_unittest.cc",
     "playback/raster_source_unittest.cc",
     "playback/recording_source_unittest.cc",
     "proto/base_conversions_unittest.cc",
@@ -987,6 +988,7 @@
 cc_test("cc_perftests") {
   sources = [
     "animation/animation_host_perftest.cc",
+    "base/rtree_perftest.cc",
     "ipc/cc_serialization_perftest.cc",
     "layers/layer_perftest.cc",
     "layers/picture_layer_impl_perftest.cc",
diff --git a/cc/base/DEPS b/cc/base/DEPS
index 33b4e8f..1bcb1a1 100644
--- a/cc/base/DEPS
+++ b/cc/base/DEPS
@@ -8,5 +8,9 @@
 specific_include_rules = {
   ".*unittest\.cc": [
     "+cc/test",
-  ]
+  ],
+  # Allow lap_timer.h for perftests
+  ".*perftest\.cc": [
+    "+cc/debug/lap_timer.h",
+  ],
 }
diff --git a/cc/base/rtree_perftest.cc b/cc/base/rtree_perftest.cc
new file mode 100644
index 0000000..85aee07
--- /dev/null
+++ b/cc/base/rtree_perftest.cc
@@ -0,0 +1,97 @@
+// 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 "cc/base/rtree.h"
+#include "cc/debug/lap_timer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace cc {
+namespace {
+
+static const int kTimeLimitMillis = 2000;
+static const int kWarmupRuns = 5;
+static const int kTimeCheckInterval = 10;
+
+class RTreePerfTest : public testing::Test {
+ public:
+  RTreePerfTest()
+      : timer_(kWarmupRuns,
+               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+               kTimeCheckInterval) {}
+
+  void RunConstructTest(const std::string& test_name, int rect_count) {
+    std::vector<gfx::Rect> rects = BuildRects(rect_count);
+    timer_.Reset();
+    do {
+      RTree rtree;
+      rtree.Build(rects);
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
+
+    perf_test::PrintResult("rtree_construct", "", test_name,
+                           timer_.LapsPerSecond(), "runs/s", true);
+  }
+
+  void RunSearchTest(const std::string& test_name, int rect_count) {
+    int large_query = std::sqrt(rect_count);
+
+    std::vector<gfx::Rect> queries = {
+        gfx::Rect(0, 0, 1, 1), gfx::Rect(100, 100, 2, 2),
+        gfx::Rect(-10, -10, 1, 1), gfx::Rect(0, 0, 1000, 1000),
+        gfx::Rect(large_query - 2, large_query - 2, 1, 1)};
+    size_t query_index = 0;
+
+    std::vector<gfx::Rect> rects = BuildRects(rect_count);
+    RTree rtree;
+    rtree.Build(rects);
+
+    timer_.Reset();
+    do {
+      std::vector<size_t> results;
+      rtree.Search(queries[query_index], &results);
+      query_index = (query_index + 1) % queries.size();
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
+
+    perf_test::PrintResult("rtree_search", "", test_name,
+                           timer_.LapsPerSecond(), "runs/s", true);
+  }
+
+  std::vector<gfx::Rect> BuildRects(int count) {
+    std::vector<gfx::Rect> result;
+    int width = std::sqrt(count);
+    int x = 0;
+    int y = 0;
+    for (int i = 0; i < count; ++i) {
+      result.push_back(gfx::Rect(x, y, 1, 1));
+      if (++x > width) {
+        x = 0;
+        ++y;
+      }
+    }
+    return result;
+  }
+
+ protected:
+  LapTimer timer_;
+};
+
+TEST_F(RTreePerfTest, Construct) {
+  RunConstructTest("100", 100);
+  RunConstructTest("1000", 1000);
+  RunConstructTest("10000", 10000);
+  RunConstructTest("100000", 100000);
+}
+
+TEST_F(RTreePerfTest, Search) {
+  RunSearchTest("100", 100);
+  RunSearchTest("1000", 1000);
+  RunSearchTest("10000", 10000);
+  RunSearchTest("100000", 100000);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/playback/image_hijack_canvas.cc b/cc/playback/image_hijack_canvas.cc
index 98fd4717..a5d596d7 100644
--- a/cc/playback/image_hijack_canvas.cc
+++ b/cc/playback/image_hijack_canvas.cc
@@ -83,7 +83,7 @@
     SkMatrix matrix;
     SkShader::TileMode xy[2];
     SkImage* image = shader->isAImage(&matrix, xy);
-    if (!image)
+    if (!image || !image->isLazyGenerated())
       return base::Optional<ScopedImagePaint>();
 
     SkMatrix total_image_matrix = matrix;
diff --git a/cc/playback/image_hijack_canvas.h b/cc/playback/image_hijack_canvas.h
index 46495da..be9a6ba 100644
--- a/cc/playback/image_hijack_canvas.h
+++ b/cc/playback/image_hijack_canvas.h
@@ -6,13 +6,14 @@
 #define CC_PLAYBACK_IMAGE_HIJACK_CANVAS_H_
 
 #include "base/macros.h"
+#include "cc/base/cc_export.h"
 #include "third_party/skia/include/utils/SkNWayCanvas.h"
 
 namespace cc {
 
 class ImageDecodeCache;
 
-class ImageHijackCanvas : public SkNWayCanvas {
+class CC_EXPORT ImageHijackCanvas : public SkNWayCanvas {
  public:
   ImageHijackCanvas(int width,
                     int height,
diff --git a/cc/playback/image_hijack_canvas_unittest.cc b/cc/playback/image_hijack_canvas_unittest.cc
new file mode 100644
index 0000000..214de87
--- /dev/null
+++ b/cc/playback/image_hijack_canvas_unittest.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 "cc/playback/image_hijack_canvas.h"
+
+#include "cc/tiles/image_decode_cache.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkPath.h"
+
+namespace cc {
+namespace {
+
+class MockImageDecodeCache : public ImageDecodeCache {
+ public:
+  MOCK_METHOD3(GetTaskForImageAndRef,
+               bool(const DrawImage& image,
+                    const TracingInfo& tracing_info,
+                    scoped_refptr<TileTask>* task));
+  MOCK_METHOD1(UnrefImage, void(const DrawImage& image));
+  MOCK_METHOD1(GetDecodedImageForDraw,
+               DecodedDrawImage(const DrawImage& image));
+  MOCK_METHOD2(DrawWithImageFinished,
+               void(const DrawImage& image,
+                    const DecodedDrawImage& decoded_image));
+  MOCK_METHOD0(ReduceCacheUsage, void());
+  MOCK_METHOD1(SetShouldAggressivelyFreeResources,
+               void(bool aggressively_free_resources));
+};
+
+TEST(ImageHijackCanvasTest, NonLazyImagesSkipped) {
+  // Use a strict mock so that if *any* ImageDecodeCache methods are called, we
+  // will hit an error.
+  testing::StrictMock<MockImageDecodeCache> image_decode_cache;
+  ImageHijackCanvas canvas(100, 100, &image_decode_cache);
+
+  // Use an SkBitmap backed image to ensure that the image is not
+  // lazy-generated.
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(10, 10, true);
+  sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
+
+  SkPaint paint;
+  canvas.drawImage(image, 0, 0, &paint);
+  canvas.drawImageRect(image, SkRect::MakeXYWH(0, 0, 10, 10),
+                       SkRect::MakeXYWH(10, 10, 10, 10), &paint);
+
+  SkPaint image_paint;
+  image_paint.setShader(
+      image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode));
+  SkRect paint_rect = SkRect::MakeXYWH(0, 0, 100, 100);
+  canvas.drawRect(paint_rect, image_paint);
+  SkPath path;
+  path.addRect(paint_rect, SkPath::kCW_Direction);
+  canvas.drawPath(path, image_paint);
+  canvas.drawOval(paint_rect, image_paint);
+  canvas.drawArc(paint_rect, 0, 40, true, image_paint);
+  canvas.drawRRect(SkRRect::MakeRect(paint_rect), image_paint);
+}
+
+}  // namespace
+
+}  // namespace cc
diff --git a/cc/test/ordered_simple_task_runner.cc b/cc/test/ordered_simple_task_runner.cc
index 161704f..9dc4bbcd 100644
--- a/cc/test/ordered_simple_task_runner.cc
+++ b/cc/test/ordered_simple_task_runner.cc
@@ -137,8 +137,6 @@
 }
 
 base::TimeTicks OrderedSimpleTaskRunner::NextTaskTime() {
-  RemoveCancelledTasks();
-
   if (pending_tasks_.size() <= 0) {
     return AbsoluteMaxNow();
   }
@@ -148,7 +146,6 @@
 
 base::TimeDelta OrderedSimpleTaskRunner::DelayToNextTaskTime() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  RemoveCancelledTasks();
 
   if (pending_tasks_.size() <= 0) {
     return AbsoluteMaxNow() - base::TimeTicks();
@@ -201,11 +198,6 @@
   }
 
   while (pending_tasks_.size() > 0) {
-    // Skip canceled tasks.
-    if (pending_tasks_.begin()->task.IsCancelled()) {
-      pending_tasks_.erase(pending_tasks_.begin());
-      continue;
-    }
     // Check if we should continue to run pending tasks.
     bool condition_success = true;
     for (std::vector<base::Callback<bool(void)>>::iterator it =
@@ -352,15 +344,4 @@
   return true;
 }
 
-void OrderedSimpleTaskRunner::RemoveCancelledTasks() {
-  std::set<TestOrderablePendingTask>::iterator it = pending_tasks_.begin();
-  while (it != pending_tasks_.end()) {
-    if (it->task.IsCancelled()) {
-      it = pending_tasks_.erase(it);
-    } else {
-      it++;
-    }
-  }
-}
-
 }  // namespace cc
diff --git a/cc/test/ordered_simple_task_runner.h b/cc/test/ordered_simple_task_runner.h
index 1ba8e95..9620a960 100644
--- a/cc/test/ordered_simple_task_runner.h
+++ b/cc/test/ordered_simple_task_runner.h
@@ -130,9 +130,6 @@
   // Advance Now() to the next task to run.
   base::Callback<bool(void)> AdvanceNow();
 
-  // Removes all tasks whose weak pointer has been revoked.
-  void RemoveCancelledTasks();
-
  protected:
   static bool TaskRunCountBelowCallback(size_t max_tasks, size_t* task_run);
   bool TaskExistedInitiallyCallback(
diff --git a/chrome/android/java/res/drawable-hdpi/ic_download_pause.png b/chrome/android/java/res/drawable-hdpi/ic_download_pause.png
new file mode 100644
index 0000000..294a0488
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/ic_download_pause.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_download_pending.png b/chrome/android/java/res/drawable-hdpi/ic_download_pending.png
new file mode 100644
index 0000000..17c3fff
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/ic_download_pending.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_download_pause.png b/chrome/android/java/res/drawable-mdpi/ic_download_pause.png
new file mode 100644
index 0000000..032cbb6
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/ic_download_pause.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_download_pending.png b/chrome/android/java/res/drawable-mdpi/ic_download_pending.png
new file mode 100644
index 0000000..0bf58a5
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/ic_download_pending.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_download_pause.png b/chrome/android/java/res/drawable-xhdpi/ic_download_pause.png
new file mode 100644
index 0000000..141ec2ff
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/ic_download_pause.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_download_pending.png b/chrome/android/java/res/drawable-xhdpi/ic_download_pending.png
new file mode 100644
index 0000000..803ca65d
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/ic_download_pending.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_download_pause.png b/chrome/android/java/res/drawable-xxhdpi/ic_download_pause.png
new file mode 100644
index 0000000..bff83ae
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/ic_download_pause.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_download_pending.png b/chrome/android/java/res/drawable-xxhdpi/ic_download_pending.png
new file mode 100644
index 0000000..cbf8c33
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/ic_download_pending.png
Binary files differ
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java
index 4bbbb4c..312dd77 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java
@@ -24,7 +24,7 @@
 /**
  * The {@link BackgroundSyncLauncher} singleton is created and owned by the C++ browser. It
  * registers interest in waking up the browser the next time the device goes online after the
- * browser closes via the {@link #launchBrowserIfStopped} method.
+ * browser closes via the {@link #setLaunchWhenNextOnline} method.
  *
  * Thread model: This class is to be run on the UI thread only.
  */
@@ -50,9 +50,6 @@
      */
     private static boolean sGCMEnabled = true;
 
-    @VisibleForTesting
-    protected AsyncTask<Void, Void, Void> mLaunchBrowserIfStoppedTask;
-
     /**
      * Create a BackgroundSyncLauncher object, which is owned by C++.
      * @param context The app context.
@@ -82,21 +79,18 @@
      * Callback for {@link #shouldLaunchBrowserIfStopped}. The run method is invoked on the UI
      * thread.
      */
-    public interface ShouldLaunchCallback { void run(Boolean shouldLaunch); }
+    public static interface ShouldLaunchCallback { public void run(Boolean shouldLaunch); }
 
     /**
      * Returns whether the browser should be launched when the device next goes online.
      * This is set by C++ and reset to false each time {@link BackgroundSyncLauncher}'s singleton is
      * created (the native browser is started). This call is asynchronous and will run the callback
      * on the UI thread when complete.
-     *
-     * {@link AsyncTask} is necessary as the browser process will not have warmed up the
-     * {@link SharedPreferences} before it is used here. This is likely the first usage of
-     * {@link ContextUtils#getAppSharedPreferences}.
-     *
-     * @param callback The callback after fetching prefs.
+     * @param context The application context.
+     * @param sharedPreferences The shared preferences.
      */
-    protected static void shouldLaunchBrowserIfStopped(final ShouldLaunchCallback callback) {
+    protected static void shouldLaunchBrowserIfStopped(
+            final Context context, final ShouldLaunchCallback callback) {
         new AsyncTask<Void, Void, Boolean>() {
             @Override
             protected Boolean doInBackground(Void... params) {
@@ -107,7 +101,7 @@
             protected void onPostExecute(Boolean shouldLaunch) {
                 callback.run(shouldLaunch);
             }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }.execute();
     }
 
     /**
@@ -116,16 +110,15 @@
      * This method is called by C++ as background sync registrations are added and removed. When the
      * {@link BackgroundSyncLauncher} singleton is created (on browser start), this is called to
      * remove any pre-existing scheduled tasks.
-     *
-     * See {@link #shouldLaunchBrowserIfStopped} for {@link AsyncTask}.
-     *
+     * @param context The application context.
      * @param shouldLaunch Whether or not to launch the browser in the background.
      * @param minDelayMs The minimum time to wait before checking on the browser process.
      */
     @VisibleForTesting
     @CalledByNative
-    protected void launchBrowserIfStopped(final boolean shouldLaunch, final long minDelayMs) {
-        mLaunchBrowserIfStoppedTask = new AsyncTask<Void, Void, Void>() {
+    protected void launchBrowserIfStopped(
+            final Context context, final boolean shouldLaunch, final long minDelayMs) {
+        new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
@@ -140,7 +133,7 @@
                     if (shouldLaunch) {
                         RecordHistogram.recordBooleanHistogram(
                                 "BackgroundSync.LaunchTask.ScheduleSuccess",
-                                scheduleLaunchTask(mScheduler, minDelayMs));
+                                scheduleLaunchTask(context, mScheduler, minDelayMs));
                     } else {
                         RecordHistogram.recordBooleanHistogram(
                                 "BackgroundSync.LaunchTask.CancelSuccess",
@@ -148,7 +141,7 @@
                     }
                 }
             }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }.execute();
     }
 
     /**
@@ -161,7 +154,7 @@
 
     protected BackgroundSyncLauncher(Context context) {
         mScheduler = GcmNetworkManager.getInstance(context);
-        launchBrowserIfStopped(false, 0);
+        launchBrowserIfStopped(context, false, 0);
     }
 
     private static boolean canUseGooglePlayServices(Context context) {
@@ -195,7 +188,8 @@
         return !sGCMEnabled;
     }
 
-    private static boolean scheduleLaunchTask(GcmNetworkManager scheduler, long minDelayMs) {
+    private static boolean scheduleLaunchTask(
+            Context context, GcmNetworkManager scheduler, long minDelayMs) {
         // Google Play Services may not be up to date, if the application was not installed through
         // the Play Store. In this case, scheduling the task will fail silently.
         final long minDelaySecs = minDelayMs / 1000;
@@ -258,11 +252,11 @@
                             // without delay and let the browser reschedule if necessary.
                             // TODO(iclelland): If this fails, report the failure via UMA (not now,
                             // since the browser is not running, but on next startup.)
-                            scheduleLaunchTask(scheduler, 0);
+                            scheduleLaunchTask(context, scheduler, 0);
                         }
                     }
                 };
-        BackgroundSyncLauncher.shouldLaunchBrowserIfStopped(callback);
+        BackgroundSyncLauncher.shouldLaunchBrowserIfStopped(context, callback);
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
index 2bafb9b..f8c0063 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
@@ -93,6 +93,11 @@
      */
     public static final String MARKET_URL_FOR_TESTING = "market-url-for-testing";
 
+    /**
+     * Disable multiwindow tab merging for testing.
+     */
+    public static final String DISABLE_TAB_MERGING_FOR_TESTING = "disable-tab-merging";
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Native Switches
     ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index d5620d1..d44997c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -848,10 +848,12 @@
                             // Used by the Account management screen to open a new incognito tab.
                             // Account management screen collects its metrics separately.
                             getTabCreator(true).launchUrl(
-                                    UrlConstants.NTP_URL, TabLaunchType.FROM_CHROME_UI);
+                                    UrlConstants.NTP_URL, TabLaunchType.FROM_CHROME_UI,
+                                    intent, mIntentHandlingTimeMs);
                         } else {
                             getTabCreator(true).launchUrl(
-                                    UrlConstants.NTP_URL, TabLaunchType.FROM_EXTERNAL_APP);
+                                    UrlConstants.NTP_URL, TabLaunchType.FROM_EXTERNAL_APP,
+                                    intent, mIntentHandlingTimeMs);
                             RecordUserAction.record("MobileReceivedExternalIntent");
                         }
                     } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index ceed07d..59d530e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -271,7 +271,7 @@
 
         String contentText = mContext.getResources().getString(isDownloadPending
                 ? R.string.download_notification_pending : R.string.download_started);
-        int resId = isDownloadPending ? android.R.drawable.stat_sys_download_done
+        int resId = isDownloadPending ? R.drawable.ic_download_pending
                 : android.R.drawable.stat_sys_download;
         NotificationCompat.Builder builder = buildNotification(resId, fileName, contentText);
         builder.setOngoing(true);
@@ -374,7 +374,7 @@
         String contentText = mContext.getResources().getString(
                 R.string.download_notification_paused);
         NotificationCompat.Builder builder = buildNotification(
-                android.R.drawable.ic_media_pause, entry.fileName, contentText);
+                R.drawable.ic_download_pause, entry.fileName, contentText);
 
         // Clicking on an in-progress download sends the user to see all their downloads.
         Intent downloadHomeIntent = buildActionIntent(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java
index da44d60..fe76018 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java
@@ -50,7 +50,7 @@
     public static PendingIntent getRemoveAllIncognitoTabsIntent(Context context) {
         Intent intent = new Intent(context, IncognitoNotificationService.class);
         intent.setAction(ACTION_CLOSE_ALL_INCOGNITO);
-        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     /** Empty public constructor needed by Android. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
index e0f7ad1c..36eff359 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
@@ -166,9 +166,6 @@
             // Notification.Builder.setPublicVersion was added in Android L.
             builder.setPublicVersion(createPublicNotification(mContext));
         }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            builder.setExtras(mExtras);
-        }
 
         Notification notification = builder.build();
         notification.bigContentView = bigView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
index 94ee796..6635ecca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
@@ -18,7 +18,6 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Icon;
 import android.os.Build;
-import android.os.Bundle;
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
@@ -122,7 +121,6 @@
     protected long[] mVibratePattern;
     protected long mTimestamp;
     protected boolean mRenotify;
-    protected Bundle mExtras;
 
     private Bitmap mLargeIcon;
 
@@ -311,18 +309,6 @@
     }
 
     /**
-     * Sets the extras bundle on supported platforms.
-     */
-    @TargetApi(Build.VERSION_CODES.M)
-    public NotificationBuilderBase setExtras(Bundle extras) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-            return this;
-        }
-        mExtras = extras;
-        return this;
-    }
-
-    /**
      * Gets the large icon for the notification.
      *
      * If a large icon was supplied to the builder, returns this icon, scaled to an appropriate size
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java
index f601d75..fd9ae67 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java
@@ -4,10 +4,7 @@
 
 package org.chromium.chrome.browser.notifications;
 
-import android.annotation.TargetApi;
 import android.app.Notification;
-import android.os.Build;
-import android.service.notification.StatusBarNotification;
 
 /**
  * A proxy for the Android Notification Manager. This allows tests to be written without having to
@@ -21,7 +18,4 @@
     void cancelAll();
     void notify(int id, Notification notification);
     void notify(String tag, int id, Notification notification);
-
-    @TargetApi(Build.VERSION_CODES.M)
-    StatusBarNotification[] getActiveNotifications();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java
index ece1f75c..b05fd0047 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java
@@ -6,7 +6,6 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
-import android.service.notification.StatusBarNotification;
 
 /**
  * Default implementation of the NotificationManagerProxy, which passes through all calls to the
@@ -43,9 +42,4 @@
     public void notify(String tag, int id, Notification notification) {
         mNotificationManager.notify(tag, id, notification);
     }
-
-    @Override
-    public StatusBarNotification[] getActiveNotifications() {
-        return mNotificationManager.getActiveNotifications();
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
index d1c4d1b..26a96f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -15,7 +15,6 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
-import android.service.notification.StatusBarNotification;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -44,8 +43,6 @@
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
 
 import javax.annotation.Nullable;
 
@@ -575,15 +572,6 @@
                 makeDefaults(vibrationPattern.length, silent, vibrateEnabled));
         notificationBuilder.setVibrate(makeVibrationPattern(vibrationPattern));
 
-        // The extras bundle is available from API 20 but it's currenlty only used to retrieve
-        // notifications which is only available from 23 (Marshmallow) onwards.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            Bundle extras = new Bundle();
-            extras.putString(NotificationConstants.EXTRA_NOTIFICATION_ID, notificationId);
-            extras.putString(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID, profileId);
-            notificationBuilder.setExtras(extras);
-        }
-
         String platformTag = makePlatformTag(notificationId, origin, tag);
         if (webApkPackage.isEmpty()) {
             mNotificationManager.notify(platformTag, PLATFORM_ID, notificationBuilder.build());
@@ -688,35 +676,6 @@
     }
 
     /**
-     * Returns the notification ids on disply for a given |profileId|.
-     *
-     * @param profileId of the profile to retrieve notifications for.
-     */
-    @CalledByNative
-    private String[] getNotificationsForProfile(String profileId) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-            return null;
-        }
-
-        StatusBarNotification[] displayedNotifications =
-                mNotificationManager.getActiveNotifications();
-        List<String> notifications = new ArrayList<String>();
-        for (StatusBarNotification notification : displayedNotifications) {
-            Bundle extras = notification.getNotification().extras;
-            String notificationId = extras.getString(NotificationConstants.EXTRA_NOTIFICATION_ID);
-            String notificationProfileId =
-                    extras.getString(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID);
-            if (notificationId != null && profileId.equals(notificationProfileId)) {
-                notifications.add(notificationId);
-            }
-        }
-        if (notifications.size() == 0) return null;
-
-        String[] result = new String[notifications.size()];
-        return notifications.toArray(result);
-    }
-
-    /**
      * Calls NotificationPlatformBridgeAndroid::OnNotificationClicked in native code to indicate
      * that the notification with the given parameters has been clicked on.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java
index 705d7c0..337b6f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java
@@ -60,9 +60,6 @@
             // Notification.Builder.setPublicVersion was added in Android L.
             builder.setPublicVersion(createPublicNotification(mContext));
         }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            builder.setExtras(mExtras);
-        }
         return builder.build();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 8caa3ef..88a22fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -234,6 +234,9 @@
      * @return True if tab model merging for Android N+ is enabled.
      */
     public static boolean isTabModelMergingEnabled() {
+        if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_TAB_MERGING_FOR_TESTING)) {
+            return false;
+        }
         return Build.VERSION.SDK_INT > Build.VERSION_CODES.M;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java
index 1d89766..042f609 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java
@@ -49,11 +49,6 @@
     boolean exitFromVr(int requestCode, final Intent intent);
 
     /**
-     * Sets VR Mode to |enabled|.
-     */
-    void setVrModeEnabled(boolean enabled);
-
-    /**
      * @return Whether the current Viewer is a Daydream Viewer.
      */
     Boolean isDaydreamCurrentViewer();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
index 5d4f546..2947d37d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
@@ -9,7 +9,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 
-import com.google.vr.ndk.base.AndroidCompat;
 import com.google.vr.ndk.base.DaydreamApi;
 import com.google.vr.ndk.base.GvrApi;
 
@@ -71,11 +70,6 @@
     }
 
     @Override
-    public void setVrModeEnabled(boolean enabled) {
-        AndroidCompat.setVrModeEnabled(mActivity, enabled);
-    }
-
-    @Override
     public Boolean isDaydreamCurrentViewer() {
         DaydreamApi daydreamApi = DaydreamApi.create(mActivity);
         if (daydreamApi == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
index 204b630..45a38a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
@@ -34,6 +34,11 @@
     void teardown();
 
     /**
+     * Sets VR Mode to |enabled|.
+     */
+    void setVrModeEnabled(boolean enabled);
+
+    /**
      * Sets whether we're presenting WebVR content or not.
      */
     void setWebVrModeEnabled(boolean enabled);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index 1a91c98..6e45dbea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -90,11 +90,9 @@
     public VrShellDelegate(ChromeTabbedActivity activity) {
         mActivity = activity;
 
-        // TODO(bshe): refactor code so that mCardboardSupportOnly does not depend on mVrAvailable
-        // and mVrAvailable does not depend on createVrDaydreamApi.
-        mVrAvailable = createVrClassesBuilder() && isVrCoreCompatible() && createVrDaydreamApi();
-        // Only Cardboard mode is supported on non-daydream devices.
-        if (mVrDaydreamApi != null && mVrDaydreamApi.isDaydreamReadyDevice()) {
+        mVrAvailable = createVrClassesBuilder() && isVrCoreCompatible();
+        // Daydream ready devices support both Cardboard and Daydream mode.
+        if (mVrAvailable && createVrDaydreamApi() && mVrDaydreamApi.isDaydreamReadyDevice()) {
             mCardboardSupportOnly = false;
         }
 
@@ -181,7 +179,7 @@
         } else {
             if (mRequestedWebVR) nativeSetPresentResult(mNativeVrShellDelegate, false);
             if (!mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
-                mVrDaydreamApi.setVrModeEnabled(false);
+                mVrShell.setVrModeEnabled(false);
             }
         }
 
@@ -190,7 +188,6 @@
 
     private boolean enterVR() {
         if (mInVr) return true;
-        mVrDaydreamApi.setVrModeEnabled(true);
 
         Tab tab = mActivity.getActivityTab();
         if (!canEnterVR(tab)) return false;
@@ -201,6 +198,7 @@
             mActivity.setRequestedOrientation(mRestoreOrientation);
             return false;
         }
+        mVrShell.setVrModeEnabled(true);
         mInVr = true;
         mTab = tab;
         mTab.addObserver(mTabObserver);
@@ -402,11 +400,11 @@
     public void onExitVRResult(int resultCode) {
         assert mVrAvailable;
         if (resultCode == Activity.RESULT_OK) {
-            mVrDaydreamApi.setVrModeEnabled(false);
+            mVrShell.setVrModeEnabled(false);
         } else {
             // For now, we don't handle re-entering VR when exit fails, so keep trying to exit.
             if (!mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
-                mVrDaydreamApi.setVrModeEnabled(false);
+                mVrShell.setVrModeEnabled(false);
             }
         }
     }
@@ -464,10 +462,10 @@
         mRequestedWebVR = false;
         if (!isPausing) {
             if (!showTransition || !mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
-                mVrDaydreamApi.setVrModeEnabled(false);
+                mVrShell.setVrModeEnabled(false);
             }
         } else {
-            mVrDaydreamApi.setVrModeEnabled(false);
+            mVrShell.setVrModeEnabled(false);
             mLastVRExit = SystemClock.uptimeMillis();
         }
         mActivity.setRequestedOrientation(mRestoreOrientation);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index cf7e4a5..35783b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -297,6 +297,11 @@
     }
 
     @Override
+    public void setVrModeEnabled(boolean enabled) {
+        AndroidCompat.setVrModeEnabled(mActivity, enabled);
+    }
+
+    @Override
     public void setWebVrModeEnabled(boolean enabled) {
         nativeSetWebVrMode(mNativeVrShell, enabled);
     }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 078ec55..0ecd02d 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1289,6 +1289,7 @@
   "javatests/src/org/chromium/chrome/browser/media/ui/AutoplayMutedNotificationTest.java",
   "javatests/src/org/chromium/chrome/browser/media/ui/NotificationTitleUpdatedTest.java",
   "javatests/src/org/chromium/chrome/browser/media/ui/PauseOnHeadsetUnplugTest.java",
+  "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java",
   "javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java
index 2b1f41f..f26d62d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java
@@ -13,7 +13,6 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Semaphore;
 
 /**
@@ -52,7 +51,7 @@
                     }
                 };
 
-        BackgroundSyncLauncher.shouldLaunchBrowserIfStopped(callback);
+        BackgroundSyncLauncher.shouldLaunchBrowserIfStopped(mContext, callback);
         try {
             // Wait on the callback to be called.
             semaphore.acquire();
@@ -62,16 +61,6 @@
         return mShouldLaunchResult;
     }
 
-    private void waitForLaunchBrowserTask() {
-        try {
-            mLauncher.mLaunchBrowserIfStoppedTask.get();
-        } catch (InterruptedException e) {
-            fail("Launch task was interrupted");
-        } catch (ExecutionException e) {
-            fail("Launch task had execution exception");
-        }
-    }
-
     @SmallTest
     @Feature({"BackgroundSync"})
     @RetryOnFailure
@@ -92,11 +81,9 @@
     @RetryOnFailure
     public void testSetLaunchWhenNextOnline() {
         assertFalse(shouldLaunchBrowserIfStoppedSync());
-        mLauncher.launchBrowserIfStopped(true, 0);
-        waitForLaunchBrowserTask();
+        mLauncher.launchBrowserIfStopped(mContext, true, 0);
         assertTrue(shouldLaunchBrowserIfStoppedSync());
-        mLauncher.launchBrowserIfStopped(false, 0);
-        waitForLaunchBrowserTask();
+        mLauncher.launchBrowserIfStopped(mContext, false, 0);
         assertFalse(shouldLaunchBrowserIfStoppedSync());
     }
 
@@ -104,8 +91,7 @@
     @Feature({"BackgroundSync"})
     @RetryOnFailure
     public void testNewLauncherDisablesNextOnline() {
-        mLauncher.launchBrowserIfStopped(true, 0);
-        waitForLaunchBrowserTask();
+        mLauncher.launchBrowserIfStopped(mContext, true, 0);
         assertTrue(shouldLaunchBrowserIfStoppedSync());
 
         // Simulate restarting the browser by deleting the launcher and creating a new one.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 5f8438b..611c703 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -84,6 +84,7 @@
 // NOTE: Disable online detection so we we'll default to online on test bots with no network.
 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
 @CommandLineFlags.Add(ContextualSearchFieldTrial.ONLINE_DETECTION_DISABLED)
+@RetryOnFailure
 public class ContextualSearchManagerTest extends ChromeActivityTestCaseBase<ChromeActivity> {
     private static final String TEST_PAGE =
             "/chrome/test/data/android/contextualsearch/tap_test.html";
@@ -1019,7 +1020,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testHidesWhenOmniboxFocused() throws InterruptedException, TimeoutException {
         clickWordNode("intelligence");
 
@@ -1038,7 +1038,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testDoesContainAWord() {
         assertTrue(mSelectionController.doesContainAWord("word"));
         assertTrue(mSelectionController.doesContainAWord("word "));
@@ -1057,7 +1056,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testIsValidSelection() {
         StubbedContentViewCore stubbedCvc = new StubbedContentViewCore(
                 getActivity().getBaseContext());
@@ -1081,7 +1079,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTap() throws InterruptedException, TimeoutException {
         clickWordNode("intelligence");
 
@@ -1097,7 +1094,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testLongPress() throws InterruptedException, TimeoutException {
         longPressNode("states");
 
@@ -1190,7 +1186,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testPrefetchFailoverRequestMadeAfterOpen()
             throws InterruptedException, TimeoutException {
         mFakeServer.reset();
@@ -1219,7 +1214,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapDisablePreload() throws InterruptedException, TimeoutException {
         clickWordNode("intelligence");
 
@@ -1235,7 +1229,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testLongPressGestureSelects() throws InterruptedException, TimeoutException {
         longPressNode("intelligence");
         assertEquals("Intelligence", getSelectedText());
@@ -1255,7 +1248,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapGestureSelects() throws InterruptedException, TimeoutException {
         clickWordNode("intelligence");
         assertEquals("Intelligence", getSelectedText());
@@ -1273,7 +1265,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapGestureOnSpecialCharacterDoesntSelect()
             throws InterruptedException, TimeoutException {
         clickNode("question-mark");
@@ -1288,7 +1279,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapGestureFollowedByScrollClearsSelection()
             throws InterruptedException, TimeoutException {
         clickWordNode("intelligence");
@@ -1306,7 +1296,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapGestureFollowedByInvalidTextTapCloses()
             throws InterruptedException, TimeoutException {
         clickWordNode("states-far");
@@ -1321,7 +1310,6 @@
      * Tests that a Tap gesture followed by tapping a non-text character doesn't select.
      * @SmallTest
      * @Feature({"ContextualSearch"})
-     * @RetryOnFailure
      * crbug.com/665633
      */
     @DisabledTest
@@ -1337,7 +1325,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapGestureFarAwayTogglesSelecting()
             throws InterruptedException, TimeoutException {
         clickWordNode("states");
@@ -1358,7 +1345,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapGesturesNearbyKeepSelecting() throws InterruptedException, TimeoutException {
         clickWordNode("states");
         assertEquals("States", getSelectedText());
@@ -1443,8 +1429,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-
-    @RetryOnFailure
     public void testContextualSearchNotDismissedOnBackgroundTabCrash()
             throws InterruptedException, TimeoutException {
         ChromeTabUtils.newTabFromMenu(getInstrumentation(),
@@ -1480,7 +1464,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapSearchBarPromotesToTab() throws InterruptedException, TimeoutException {
         // -------- SET UP ---------
         // Track Tab creation with this helper.
@@ -1527,7 +1510,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapOnRoleIgnored() throws InterruptedException, TimeoutException {
         PanelState initialState = mPanel.getPanelState();
         clickNode("role");
@@ -1540,7 +1522,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapOnARIAIgnored() throws InterruptedException, TimeoutException {
         PanelState initialState = mPanel.getPanelState();
         clickNode("aria");
@@ -1552,7 +1533,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapOnFocusableIgnored() throws InterruptedException, TimeoutException {
         PanelState initialState = mPanel.getPanelState();
         clickNode("focusable");
@@ -1566,7 +1546,6 @@
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
     @DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/596533")
-    @RetryOnFailure
     public void testTapLimitForDecided() throws InterruptedException, TimeoutException {
         mPolicy.setTapLimitForDecidedForTesting(2);
         clickToTriggerPrefetch();
@@ -1596,7 +1575,6 @@
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
     @DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/596533")
-    @RetryOnFailure
     public void testTapLimitForUndecided() throws InterruptedException, TimeoutException {
         mPolicy.setTapLimitForUndecidedForTesting(2);
         mPolicy.overrideDecidedStateForTesting(false);
@@ -1627,7 +1605,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testExpandBeforeSearchTermResolution()
             throws InterruptedException, TimeoutException {
         clickWordNode("states");
@@ -1651,7 +1628,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testSearchTermResolutionError() throws InterruptedException, TimeoutException {
         clickWordNode("states");
         assertSearchTermRequested();
@@ -1670,7 +1646,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testHttpsBeforeAcceptForOptOut() throws InterruptedException, TimeoutException {
         mPolicy.overrideDecidedStateForTesting(false);
         mFakeServer.setShouldUseHttps(true);
@@ -1685,7 +1660,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testHttpsAfterAcceptForOptOut() throws InterruptedException, TimeoutException {
         mPolicy.overrideDecidedStateForTesting(true);
         mFakeServer.setShouldUseHttps(true);
@@ -1698,7 +1672,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testHttpBeforeAcceptForOptOut() throws InterruptedException, TimeoutException {
         mPolicy.overrideDecidedStateForTesting(false);
 
@@ -1710,7 +1683,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testHttpAfterAcceptForOptOut() throws InterruptedException, TimeoutException {
         mPolicy.overrideDecidedStateForTesting(true);
 
@@ -1747,7 +1719,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
-    @RetryOnFailure
     @DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/596533")
     public void testAppMenuSuppressedWhenExpanded() throws InterruptedException, TimeoutException {
         clickWordNode("states");
@@ -1767,7 +1738,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testAppMenuSuppressedWhenMaximized() throws InterruptedException, TimeoutException {
         clickWordNode("states");
         flingPanelUpToTop();
@@ -1794,7 +1764,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testPromoTapCount() throws InterruptedException, TimeoutException {
         mPolicy.setPromoTapTriggeredLimitForTesting(2);
         mPolicy.overrideDecidedStateForTesting(false);
@@ -1869,7 +1838,6 @@
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
     @DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/596533")
-    @RetryOnFailure
     public void testPromoOpenCountForDecided() throws InterruptedException, TimeoutException {
         mPolicy.overrideDecidedStateForTesting(true);
 
@@ -1925,7 +1893,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
-    @RetryOnFailure
     public void testNotifyObserverHideAfterLongPress()
             throws InterruptedException, TimeoutException {
         TestContextualSearchObserver observer = new TestContextualSearchObserver();
@@ -1976,7 +1943,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testNotifyObserverHideOnClearSelectionAfterTap()
             throws InterruptedException, TimeoutException {
         TestContextualSearchObserver observer = new TestContextualSearchObserver();
@@ -2006,7 +1972,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testPreventHandlingCurrentSelectionModification()
             throws InterruptedException, TimeoutException {
         simulateLongPressSearch("search");
@@ -2040,7 +2005,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @CommandLineFlags.Add(ContextualSearchFieldTrial.SUPPRESSION_TAPS + "=" + PLENTY_OF_TAPS)
-    @RetryOnFailure
     public void testTapALot() throws InterruptedException, TimeoutException {
         mPolicy.setTapLimitForDecidedForTesting(PLENTY_OF_TAPS);
         mPolicy.setTapLimitForUndecidedForTesting(PLENTY_OF_TAPS);
@@ -2082,7 +2046,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testRedirectedExternalNavigationWithUserGesture() {
         final ExternalNavigationHandler externalNavHandler =
                 new ExternalNavigationHandler(getActivity().getActivityTab());
@@ -2116,7 +2079,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testExternalNavigationWithoutUserGesture() {
         final ExternalNavigationHandler externalNavHandler =
                 new ExternalNavigationHandler(getActivity().getActivityTab());
@@ -2138,7 +2100,6 @@
 
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testSelectionExpansionOnSearchTermResolution()
             throws InterruptedException, TimeoutException {
         mFakeServer.reset();
@@ -2156,11 +2117,9 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
-    @CommandLineFlags
-            .Add(ContextualSearchFieldTrial.PEEK_PROMO_ENABLED + "=true")
-            @DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/596533")
-            @RetryOnFailure
-            public void testLongPressShowsPeekPromo()
+    @CommandLineFlags.Add(ContextualSearchFieldTrial.PEEK_PROMO_ENABLED + "=true")
+    @DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/596533")
+    public void testLongPressShowsPeekPromo()
             throws InterruptedException, TimeoutException {
         // Must be in undecided state in order to trigger the Peek Promo.
         mPolicy.overrideDecidedStateForTesting(false);
@@ -2201,7 +2160,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
-    @RetryOnFailure
     public void testTapContentVisibility() throws InterruptedException, TimeoutException {
         // Simulate a tap and make sure Content is not visible.
         simulateTapSearch("search");
@@ -2280,7 +2238,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
-    @RetryOnFailure
     public void testLongPressMultipleSwipeOnlyLoadsContentOnce()
             throws InterruptedException, TimeoutException {
         // Simulate a long press and make sure no Content is created.
@@ -2316,7 +2273,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testChainedSearchCreatesNewContent() throws InterruptedException, TimeoutException {
         // Simulate a tap and make sure Content is not visible.
         simulateTapSearch("search");
@@ -2399,7 +2355,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testChainedSearchContentVisibility() throws InterruptedException, TimeoutException {
         // Simulate a tap and make sure Content is not visible.
         simulateTapSearch("search");
@@ -2433,7 +2388,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testTapCloseRemovedFromHistory() throws InterruptedException, TimeoutException {
         // Simulate a tap and make sure a URL was loaded.
         simulateTapSearch("search");
@@ -2474,7 +2428,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testChainedTapsRemovedFromHistory() throws InterruptedException, TimeoutException {
         // Simulate a tap and make sure a URL was loaded.
         simulateTapSearch("search");
@@ -2515,7 +2468,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @CommandLineFlags.Add(ContextualSearchFieldTrial.ENABLE_TRANSLATION + "=true")
-    @RetryOnFailure
     public void testTapWithLanguage() throws InterruptedException, TimeoutException {
         // Tapping a German word should trigger translation.
         simulateTapSearch("german");
@@ -2548,7 +2500,6 @@
     @Feature({"ContextualSearch"})
     @CommandLineFlags.Add({ContextualSearchFieldTrial.ENABLE_TRANSLATION + "=true",
             ContextualSearchFieldTrial.ENABLE_SERVER_CONTROLLED_ONEBOX + "=true"})
-    @RetryOnFailure
     public void testTapWithoutLanguageCanBeForced() throws InterruptedException, TimeoutException {
         // Tapping an English word should trigger translation.
         simulateTapSearch("search");
@@ -2563,7 +2514,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @CommandLineFlags.Add(ContextualSearchFieldTrial.ENABLE_TRANSLATION + "=true")
-    @RetryOnFailure
     public void testLongpressTranslates() throws InterruptedException, TimeoutException {
         // LongPress on any word should trigger translation.
         simulateLongPressSearch("search");
@@ -2579,7 +2529,6 @@
     @Feature({"ContextualSearch"})
     @CommandLineFlags.Add({ContextualSearchFieldTrial.ENABLE_TRANSLATION + "=true",
             ContextualSearchFieldTrial.DISABLE_AUTO_DETECT_TRANSLATION_ONEBOX + "=true"})
-    @RetryOnFailure
     public void testLongpressAutoDetectDisabledDoesNotTranslate()
             throws InterruptedException, TimeoutException {
         // Unless disabled, LongPress on any word should trigger translation.
@@ -2596,7 +2545,6 @@
     @Feature({"ContextualSearch"})
     @CommandLineFlags.Add({ContextualSearchFieldTrial.ENABLE_TRANSLATION + "=true",
             ContextualSearchFieldTrial.DISABLE_FORCE_TRANSLATION_ONEBOX + "=true"})
-    @RetryOnFailure
     public void testLongpressTranslateDisabledDoesNotTranslate()
             throws InterruptedException, TimeoutException {
         // Unless disabled, LongPress on any word should trigger translation.
@@ -2637,7 +2585,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     public void testPanelDismissedOnToggleFullscreen()
             throws InterruptedException, TimeoutException {
         // Simulate a tap and assert that the panel peeks.
@@ -2717,7 +2664,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure
     // NOTE: Remove the flag so we will run just this test with onLine detection enabled.
     @CommandLineFlags.Remove(ContextualSearchFieldTrial.ONLINE_DETECTION_DISABLED)
     public void testNetworkDisconnectedDeactivatesSearch()
@@ -2743,7 +2689,6 @@
      */
     @SmallTest
     @Feature({"ContextualSearch"})
-    @RetryOnFailure  // crbug.com/673681
     public void testQuickActionCaptionAndImage() throws InterruptedException, TimeoutException {
         // Simulate a tap to show the Bar, then set the quick action data.
         simulateTapSearch("search");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java
new file mode 100644
index 0000000..b6a8536
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java
@@ -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.
+
+package org.chromium.chrome.browser.multiwindow;
+
+import static org.chromium.chrome.browser.multiwindow.MultiWindowUtilsTest.createSecondChromeTabbedActivity;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Build;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity2;
+import org.chromium.chrome.browser.firstrun.FirstRunStatus;
+import org.chromium.chrome.browser.tabmodel.TabWindowManager;
+import org.chromium.chrome.test.ChromeTabbedActivityTestBase;
+import org.chromium.chrome.test.util.MenuUtils;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Integration testing for Android's N+ MultiWindow.
+ */
+@MinAndroidSdkLevel(Build.VERSION_CODES.N)
+public class MultiWindowIntegrationTest extends ChromeTabbedActivityTestBase {
+
+    @Override
+    public void startMainActivity() throws InterruptedException {
+        startMainActivityOnBlankPage();
+    }
+
+    @MediumTest
+    @Feature("MultiWindow")
+    @TargetApi(Build.VERSION_CODES.N)
+    @CommandLineFlags.Add(ChromeSwitches.DISABLE_TAB_MERGING_FOR_TESTING)
+    public void testIncognitoNtpHandledCorrectly() throws InterruptedException {
+        try {
+            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+                @Override
+                public void run() {
+                    FirstRunStatus.setFirstRunFlowComplete(true);
+                }
+            });
+
+            newIncognitoTabFromMenu();
+            assertTrue(getActivity().getActivityTab().isIncognito());
+            final int incognitoTabId = getActivity().getActivityTab().getId();
+            final ChromeTabbedActivity2 cta2 = createSecondChromeTabbedActivity(getActivity());
+
+            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+                @Override
+                public void run() {
+                    Context context = ContextUtils.getApplicationContext();
+                    ActivityManager activityManager =
+                            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+                    for (ActivityManager.AppTask task : activityManager.getAppTasks()) {
+                        if (getActivity().getTaskId() == task.getTaskInfo().id) {
+                            task.moveToFront();
+                            break;
+                        }
+                    }
+                }
+            });
+            CriteriaHelper.pollUiThread(Criteria.equals(ActivityState.RESUMED,
+                    new Callable<Integer>() {
+                        @Override
+                        public Integer call() throws Exception {
+                            return ApplicationStatus.getStateForActivity(getActivity());
+                        }
+                    }));
+
+            MenuUtils.invokeCustomMenuActionSync(
+                    getInstrumentation(), getActivity(), R.id.move_to_other_window_menu_id);
+
+            CriteriaHelper.pollUiThread(Criteria.equals(1,
+                    new Callable<Integer>() {
+                        @Override
+                        public Integer call() throws Exception {
+                            return cta2.getTabModelSelector().getModel(true).getCount();
+                        }
+                    }));
+
+            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+                @Override
+                public void run() {
+                    assertEquals(1, TabWindowManager.getInstance().getIncognitoTabCount());
+
+                    // Ensure the same tab exists in the new activity.
+                    assertEquals(incognitoTabId, cta2.getActivityTab().getId());
+                }
+            });
+        } finally {
+            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+                @Override
+                public void run() {
+                    FirstRunStatus.setFirstRunFlowComplete(false);
+                }
+            });
+        }
+    }
+
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java
index 37b585c..9d0bbe6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java
@@ -28,8 +28,7 @@
 /**
  * Class for testing MultiWindowUtils.
  */
-// TODO(twellington): Replace with Build.VERSION_CODES.N when available.
-@MinAndroidSdkLevel(24)
+@MinAndroidSdkLevel(Build.VERSION_CODES.N)
 public class MultiWindowUtilsTest extends  ChromeTabbedActivityTestBase {
 
     @Override
diff --git a/chrome/browser/android/background_sync_launcher_android.cc b/chrome/browser/android/background_sync_launcher_android.cc
index bac2de6..0b13a14 100644
--- a/chrome/browser/android/background_sync_launcher_android.cc
+++ b/chrome/browser/android/background_sync_launcher_android.cc
@@ -44,7 +44,8 @@
 
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_BackgroundSyncLauncher_launchBrowserIfStopped(
-      env, java_launcher_, launch_when_next_online, min_delay_ms);
+      env, java_launcher_, base::android::GetApplicationContext(),
+      launch_when_next_online, min_delay_ms);
 }
 
 // static
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc
index f56a182..dab16324 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/browser/scoped_group_bookmark_actions.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/common/android/bookmark_type.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
@@ -586,13 +586,13 @@
                                       jint max_results) {
   DCHECK(bookmark_model_->loaded());
 
-  std::vector<bookmarks::BookmarkMatch> results;
+  std::vector<bookmarks::TitledUrlMatch> results;
   bookmark_model_->GetBookmarksMatching(
       base::android::ConvertJavaStringToUTF16(env, j_query),
       max_results,
       query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH,
       &results);
-  for (const bookmarks::BookmarkMatch& match : results) {
+  for (const bookmarks::TitledUrlMatch& match : results) {
     const BookmarkNode* node = static_cast<const BookmarkNode*>(match.node);
 
     std::vector<int> title_match_start_positions;
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 428c7ea6..caee5129 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -177,7 +177,6 @@
 #endif
     app_list::switches::kDisableSyncAppList,
     app_list::switches::kEnableSyncAppList,
-    ash::switches::kAshEnableTabletPowerButton,
     ash::switches::kAshEnableTouchView,
     ash::switches::kAshEnablePalette,
     ash::switches::kAshEnablePaletteOnAllDisplays,
diff --git a/chrome/browser/chromeos/login/ui/webui_login_display.cc b/chrome/browser/chromeos/login/ui/webui_login_display.cc
index 5eb3a38e..74ff645 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_display.cc
+++ b/chrome/browser/chromeos/login/ui/webui_login_display.cc
@@ -227,8 +227,11 @@
 }
 
 void WebUILoginDisplay::LoadSigninWallpaper() {
-  WallpaperManager::Get()->SetDefaultWallpaperDelayed(
-      user_manager::SignInAccountId());
+  if (!WallpaperManager::Get()->SetDeviceWallpaperIfApplicable(
+          user_manager::SignInAccountId())) {
+    WallpaperManager::Get()->SetDefaultWallpaperDelayed(
+        user_manager::SignInAccountId());
+  }
 }
 
 void WebUILoginDisplay::OnSigninScreenReady() {
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index e2e49ef..2b7b988 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
@@ -29,6 +30,7 @@
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/users/avatar/user_image_loader.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/image_decoder.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/common/chrome_paths.h"
@@ -49,7 +51,9 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_manager_connection.h"
+#include "crypto/sha2.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "url/gurl.h"
 
 using content::BrowserThread;
 using wallpaper::WallpaperManagerBase;
@@ -78,6 +82,11 @@
 // Known user keys.
 const char kWallpaperFilesId[] = "wallpaper-files-id";
 
+// The directory and file name to save the downloaded device policy controlled
+// wallpaper.
+const char kDeviceWallpaperDir[] = "device_wallpaper";
+const char kDeviceWallpaperFile[] = "device_wallpaper_image.jpg";
+
 // These global default values are used to set customized default
 // wallpaper path in WallpaperManager::InitializeWallpaper().
 base::FilePath GetCustomizedWallpaperDefaultRescaledFileName(
@@ -195,6 +204,21 @@
   }
 }
 
+// A helper function to check the existing/downloaded device wallpaper file's
+// hash value matches with the hash value provided in the policy settings.
+bool CheckDeviceWallpaperMatchHash(const base::FilePath& device_wallpaper_file,
+                                   const std::string& hash) {
+  std::string image_data;
+  if (base::ReadFileToString(device_wallpaper_file, &image_data)) {
+    std::string sha_hash = crypto::SHA256HashString(image_data);
+    if (base::ToLowerASCII(base::HexEncode(
+            sha_hash.c_str(), sha_hash.size())) == base::ToLowerASCII(hash)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 // This is "wallpaper either scheduled to load, or loading right now".
@@ -347,6 +371,7 @@
 
 WallpaperManager::~WallpaperManager() {
   show_user_name_on_signin_subscription_.reset();
+  device_wallpaper_image_subscription_.reset();
   user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
   weak_factory_.InvalidateWeakPtrs();
 }
@@ -386,6 +411,11 @@
           kAccountsPrefShowUserNamesOnSignIn,
           base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
                      weak_factory_.GetWeakPtr()));
+  device_wallpaper_image_subscription_ =
+      CrosSettings::Get()->AddSettingsObserver(
+          kDeviceWallpaperImage,
+          base::Bind(&WallpaperManager::OnDeviceWallpaperPolicyChanged,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
@@ -697,6 +727,12 @@
     return;
   }
 
+  // For a enterprise managed user, set the device wallpaper if we're at the
+  // login screen.
+  if (!user_manager::UserManager::Get()->IsUserLoggedIn() &&
+      SetDeviceWallpaperIfApplicable(account_id))
+    return;
+
   // Guest user or regular user in ephemeral mode.
   if ((user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
            account_id) &&
@@ -734,16 +770,23 @@
     }
 
     if (info.type == user_manager::User::CUSTOMIZED ||
-        info.type == user_manager::User::POLICY) {
-      const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
-      // Wallpaper is not resized when layout is
-      // wallpaper::WALLPAPER_LAYOUT_CENTER.
-      // Original wallpaper should be used in this case.
-      // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
-      if (info.layout == wallpaper::WALLPAPER_LAYOUT_CENTER)
-        sub_dir = wallpaper::kOriginalWallpaperSubDir;
-      base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
-      wallpaper_path = wallpaper_path.Append(info.location);
+        info.type == user_manager::User::POLICY ||
+        info.type == user_manager::User::DEVICE) {
+      base::FilePath wallpaper_path;
+      if (info.type != user_manager::User::DEVICE) {
+        const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
+        // Wallpaper is not resized when layout is
+        // wallpaper::WALLPAPER_LAYOUT_CENTER.
+        // Original wallpaper should be used in this case.
+        // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
+        if (info.layout == wallpaper::WALLPAPER_LAYOUT_CENTER)
+          sub_dir = wallpaper::kOriginalWallpaperSubDir;
+        wallpaper_path = GetCustomWallpaperDir(sub_dir);
+        wallpaper_path = wallpaper_path.Append(info.location);
+      } else {
+        wallpaper_path = GetDeviceWallpaperFilePath();
+      }
+
       CustomWallpaperMap::iterator it = wallpaper_cache_.find(account_id);
       // Do not try to load the wallpaper if the path is the same. Since loading
       // could still be in progress, we ignore the existence of the image.
@@ -891,6 +934,114 @@
                      true /* update wallpaper */);
 }
 
+void WallpaperManager::OnDeviceWallpaperPolicyChanged() {
+  SetDeviceWallpaperIfApplicable(
+      user_manager::UserManager::Get()->IsUserLoggedIn()
+          ? user_manager::UserManager::Get()->GetActiveUser()->GetAccountId()
+          : user_manager::SignInAccountId());
+}
+
+void WallpaperManager::OnDeviceWallpaperExists(const AccountId& account_id,
+                                               const std::string& url,
+                                               const std::string& hash,
+                                               bool exist) {
+  if (exist) {
+    base::PostTaskAndReplyWithResult(
+        BrowserThread::GetBlockingPool(), FROM_HERE,
+        base::Bind(&CheckDeviceWallpaperMatchHash, GetDeviceWallpaperFilePath(),
+                   hash),
+        base::Bind(&WallpaperManager::OnCheckDeviceWallpaperMatchHash,
+                   weak_factory_.GetWeakPtr(), account_id, url, hash));
+  } else {
+    GURL device_wallpaper_url(url);
+    device_wallpaper_downloader_.reset(new CustomizationWallpaperDownloader(
+        g_browser_process->system_request_context(), device_wallpaper_url,
+        GetDeviceWallpaperDir(), GetDeviceWallpaperFilePath(),
+        base::Bind(&WallpaperManager::OnDeviceWallpaperDownloaded,
+                   weak_factory_.GetWeakPtr(), account_id, hash)));
+    device_wallpaper_downloader_->Start();
+  }
+}
+
+void WallpaperManager::OnDeviceWallpaperDownloaded(const AccountId& account_id,
+                                                   const std::string& hash,
+                                                   bool success,
+                                                   const GURL& url) {
+  if (!success) {
+    LOG(ERROR) << "Failed to download the device wallpaper. Fallback to "
+                  "default wallpaper.";
+    SetDefaultWallpaperDelayed(account_id);
+    return;
+  }
+
+  base::PostTaskAndReplyWithResult(
+      BrowserThread::GetBlockingPool(), FROM_HERE,
+      base::Bind(&CheckDeviceWallpaperMatchHash, GetDeviceWallpaperFilePath(),
+                 hash),
+      base::Bind(&WallpaperManager::OnCheckDeviceWallpaperMatchHash,
+                 weak_factory_.GetWeakPtr(), account_id, url.spec(), hash));
+}
+
+void WallpaperManager::OnCheckDeviceWallpaperMatchHash(
+    const AccountId& account_id,
+    const std::string& url,
+    const std::string& hash,
+    bool match) {
+  if (!match) {
+    if (retry_download_if_failed_) {
+      // We only retry to download the device wallpaper one more time if the
+      // hash doesn't match.
+      retry_download_if_failed_ = false;
+      GURL device_wallpaper_url(url);
+      device_wallpaper_downloader_.reset(new CustomizationWallpaperDownloader(
+          g_browser_process->system_request_context(), device_wallpaper_url,
+          GetDeviceWallpaperDir(), GetDeviceWallpaperFilePath(),
+          base::Bind(&WallpaperManager::OnDeviceWallpaperDownloaded,
+                     weak_factory_.GetWeakPtr(), account_id, hash)));
+      device_wallpaper_downloader_->Start();
+    } else {
+      LOG(ERROR) << "The device wallpaper hash doesn't match with provided "
+                    "hash value. Fallback to default wallpaper! ";
+      SetDefaultWallpaperDelayed(account_id);
+
+      // Reset the boolean variable so that it can retry to download when the
+      // next device wallpaper request comes in.
+      retry_download_if_failed_ = true;
+    }
+    return;
+  }
+
+  user_image_loader::StartWithFilePath(
+      task_runner_, GetDeviceWallpaperFilePath(),
+      ImageDecoder::ROBUST_JPEG_CODEC,
+      0,  // Do not crop.
+      base::Bind(&WallpaperManager::OnDeviceWallpaperDecoded,
+                 weak_factory_.GetWeakPtr(), account_id));
+}
+
+void WallpaperManager::OnDeviceWallpaperDecoded(
+    const AccountId& account_id,
+    std::unique_ptr<user_manager::UserImage> user_image) {
+  WallpaperInfo wallpaper_info = {GetDeviceWallpaperFilePath().value(),
+                                  wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED,
+                                  user_manager::User::DEVICE,
+                                  base::Time::Now().LocalMidnight()};
+  if (user_manager::UserManager::Get()->IsUserLoggedIn()) {
+    // In a user's session treat the device wallpaper as a normal custom
+    // wallpaper. It should be persistent and can be overriden by other user
+    // selected wallpapers.
+    SetUserWallpaperInfo(account_id, wallpaper_info, true /* is_persistent */);
+    GetPendingWallpaper(account_id, false)
+        ->ResetSetWallpaperImage(user_image->image(), wallpaper_info);
+    wallpaper_cache_[account_id] = CustomWallpaperElement(
+        GetDeviceWallpaperFilePath(), user_image->image());
+  } else {
+    // In the login screen set the device wallpaper as the wallpaper.
+    GetPendingWallpaper(user_manager::SignInAccountId(), false)
+        ->ResetSetWallpaperImage(user_image->image(), wallpaper_info);
+  }
+}
+
 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
   if (user_manager::UserManager::Get()->IsUserLoggedIn())
     return;
@@ -907,7 +1058,8 @@
   int public_session_user_index = FindPublicSession(users);
   if ((!show_users && public_session_user_index == -1) || users.empty()) {
     // Boot into sign in form, preload default wallpaper.
-    SetDefaultWallpaperDelayed(user_manager::SignInAccountId());
+    if (!SetDeviceWallpaperIfApplicable(user_manager::SignInAccountId()))
+      SetDefaultWallpaperDelayed(user_manager::SignInAccountId());
     return;
   }
 
@@ -966,6 +1118,53 @@
   return true;
 }
 
+bool WallpaperManager::ShouldSetDeviceWallpaper(const AccountId& account_id,
+                                                std::string* url,
+                                                std::string* hash) {
+  // Only allow the device wallpaper for enterprise managed devices.
+  if (!g_browser_process->platform_part()
+           ->browser_policy_connector_chromeos()
+           ->IsEnterpriseManaged()) {
+    return false;
+  }
+
+  const base::DictionaryValue* dict = nullptr;
+  if (!CrosSettings::Get()->GetDictionary(kDeviceWallpaperImage, &dict) ||
+      !dict->GetStringWithoutPathExpansion("url", url) ||
+      !dict->GetStringWithoutPathExpansion("hash", hash)) {
+    return false;
+  }
+
+  // Only set the device wallpaper if 1) we're at the login screen or 2) there
+  // is no user policy wallpaper in a user session and the user has the default
+  // wallpaper or device wallpaper in a user session. Note in the latter case,
+  // the device wallpaper can be overridden by user-selected wallpapers.
+  if (user_manager::UserManager::Get()->IsUserLoggedIn()) {
+    WallpaperInfo info;
+    if (GetUserWallpaperInfo(account_id, &info) &&
+        (info.type == user_manager::User::POLICY ||
+         (info.type != user_manager::User::DEFAULT &&
+          info.type != user_manager::User::DEVICE))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+base::FilePath WallpaperManager::GetDeviceWallpaperDir() {
+  base::FilePath wallpaper_dir;
+  if (!PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir)) {
+    LOG(ERROR) << "Unable to get wallpaper dir.";
+    return base::FilePath();
+  }
+  return wallpaper_dir.Append(kDeviceWallpaperDir);
+}
+
+base::FilePath WallpaperManager::GetDeviceWallpaperFilePath() {
+  return GetDeviceWallpaperDir().Append(kDeviceWallpaperFile);
+}
+
 void WallpaperManager::OnWallpaperDecoded(
     const AccountId& account_id,
     wallpaper::WallpaperLayout layout,
@@ -1071,6 +1270,36 @@
   return loading_.size();
 }
 
+wallpaper::WallpaperFilesId WallpaperManager::GetFilesId(
+    const AccountId& account_id) const {
+  std::string stored_value;
+  if (user_manager::known_user::GetStringPref(account_id, kWallpaperFilesId,
+                                              &stored_value)) {
+    return wallpaper::WallpaperFilesId::FromString(stored_value);
+  }
+  const std::string& old_id = account_id.GetUserEmail();  // Migrated
+  const wallpaper::WallpaperFilesId files_id = HashWallpaperFilesIdStr(old_id);
+  SetKnownUserWallpaperFilesId(account_id, files_id);
+  return files_id;
+}
+
+bool WallpaperManager::SetDeviceWallpaperIfApplicable(
+    const AccountId& account_id) {
+  std::string url;
+  std::string hash;
+  if (ShouldSetDeviceWallpaper(account_id, &url, &hash)) {
+    // Check if the device wallpaper exists and matches the hash. If so, use it
+    // directly. Otherwise download it first.
+    base::PostTaskAndReplyWithResult(
+        BrowserThread::GetBlockingPool(), FROM_HERE,
+        base::Bind(&base::PathExists, GetDeviceWallpaperFilePath()),
+        base::Bind(&WallpaperManager::OnDeviceWallpaperExists,
+                   weak_factory_.GetWeakPtr(), account_id, url, hash));
+    return true;
+  }
+  return false;
+}
+
 void WallpaperManager::UserChangedChildStatus(user_manager::User* user) {
   SetUserWallpaperNow(user->GetAccountId());
 }
@@ -1157,17 +1386,4 @@
     DoSetDefaultWallpaper(EmptyAccountId(), MovableOnDestroyCallbackHolder());
 }
 
-wallpaper::WallpaperFilesId WallpaperManager::GetFilesId(
-    const AccountId& account_id) const {
-  std::string stored_value;
-  if (user_manager::known_user::GetStringPref(account_id, kWallpaperFilesId,
-                                              &stored_value)) {
-    return wallpaper::WallpaperFilesId::FromString(stored_value);
-  }
-  const std::string& old_id = account_id.GetUserEmail();  // Migrated
-  const wallpaper::WallpaperFilesId files_id = HashWallpaperFilesIdStr(old_id);
-  SetKnownUserWallpaperFilesId(account_id, files_id);
-  return files_id;
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
index 206bcfef..f4c1a952 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/customization/customization_wallpaper_downloader.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_image/user_image.h"
@@ -81,6 +82,7 @@
   size_t GetPendingListSizeForTesting() const override;
   wallpaper::WallpaperFilesId GetFilesId(
       const AccountId& account_id) const override;
+  bool SetDeviceWallpaperIfApplicable(const AccountId& account_id) override;
 
   // ash::mojom::WallpaperPicker:
   void Open() override;
@@ -115,10 +117,40 @@
       const AccountId& account_id,
       std::unique_ptr<user_manager::UserImage> user_image);
 
+  // This is called when the device wallpaper policy changes.
+  void OnDeviceWallpaperPolicyChanged();
+  // This is call after checking if the device wallpaper exists.
+  void OnDeviceWallpaperExists(const AccountId& account_id,
+                               const std::string& url,
+                               const std::string& hash,
+                               bool exist);
+  // This is called after the device wallpaper is download (either successful or
+  // failed).
+  void OnDeviceWallpaperDownloaded(const AccountId& account_id,
+                                   const std::string& hash,
+                                   bool success,
+                                   const GURL& url);
+  // Check if the device wallpaper matches the hash that's provided in the
+  // device wallpaper policy setting.
+  void OnCheckDeviceWallpaperMatchHash(const AccountId& account_id,
+                                       const std::string& url,
+                                       const std::string& hash,
+                                       bool match);
+  // This is called when the device wallpaper is decoded successfully.
+  void OnDeviceWallpaperDecoded(
+      const AccountId& account_id,
+      std::unique_ptr<user_manager::UserImage> user_image);
+
   // wallpaper::WallpaperManagerBase:
   void InitializeRegisteredDeviceWallpaper() override;
   bool GetUserWallpaperInfo(const AccountId& account_id,
                             wallpaper::WallpaperInfo* info) const override;
+  // Returns true if the device wallpaper should be set for the account.
+  bool ShouldSetDeviceWallpaper(const AccountId& account_id,
+                                std::string* url,
+                                std::string* hash) override;
+  base::FilePath GetDeviceWallpaperDir() override;
+  base::FilePath GetDeviceWallpaperFilePath() override;
   void OnWallpaperDecoded(
       const AccountId& account_id,
       wallpaper::WallpaperLayout layout,
@@ -165,6 +197,12 @@
   std::unique_ptr<CrosSettings::ObserverSubscription>
       show_user_name_on_signin_subscription_;
 
+  std::unique_ptr<CrosSettings::ObserverSubscription>
+      device_wallpaper_image_subscription_;
+  std::unique_ptr<CustomizationWallpaperDownloader>
+      device_wallpaper_downloader_;
+  bool retry_download_if_failed_ = true;
+
   // Pointer to last inactive (waiting) entry of 'loading_' list.
   // NULL when there is no inactive request.
   PendingWallpaper* pending_inactive_;
diff --git a/chrome/browser/importer/profile_writer_unittest.cc b/chrome/browser/importer/profile_writer_unittest.cc
index dbed3192..8a48a67 100644
--- a/chrome/browser/importer/profile_writer_unittest.cc
+++ b/chrome/browser/importer/profile_writer_unittest.cc
@@ -16,17 +16,17 @@
 #include "chrome/browser/importer/importer_unittest_utils.h"
 #include "chrome/common/importer/imported_bookmark_entry.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using bookmarks::BookmarkMatch;
 using bookmarks::BookmarkModel;
+using bookmarks::TitledUrlMatch;
 
 class TestProfileWriter : public ProfileWriter {
  public:
@@ -81,7 +81,7 @@
       const std::vector<BookmarkModel::URLAndTitle>& bookmarks_record,
       BookmarkModel* bookmark_model,
       size_t expected) {
-    std::vector<BookmarkMatch> matches;
+    std::vector<TitledUrlMatch> matches;
     for (size_t i = 0; i < bookmarks_record.size(); ++i) {
       bookmark_model->GetBookmarksMatching(
           bookmarks_record[i].title, 10, &matches);
diff --git a/chrome/browser/metrics/antivirus_metrics_provider_win.cc b/chrome/browser/metrics/antivirus_metrics_provider_win.cc
index 44772e5fe..ada7c8a 100644
--- a/chrome/browser/metrics/antivirus_metrics_provider_win.cc
+++ b/chrome/browser/metrics/antivirus_metrics_provider_win.cc
@@ -176,6 +176,8 @@
       result = FillAntiVirusProductsFromWMI(&av_products);
   }
 
+  MaybeAddUnregisteredAntiVirusProducts(&av_products);
+
   UMA_HISTOGRAM_ENUMERATION("UMA.AntiVirusMetricsProvider.Result",
                             result,
                             RESULT_COUNT);
@@ -426,3 +428,48 @@
 
   return RESULT_SUCCESS;
 }
+
+void AntiVirusMetricsProvider::MaybeAddUnregisteredAntiVirusProducts(
+    std::vector<AvProduct>* products) {
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  // Trusteer Rapport does not register with WMI or Security Center so do some
+  // "best efforts" detection here.
+
+  // Rapport always installs into 32-bit Program Files in directory
+  // %DIR_PROGRAM_FILESX86%\Trusteer\Rapport
+  base::FilePath binary_path;
+  if (!PathService::Get(base::DIR_PROGRAM_FILESX86, &binary_path))
+    return;
+
+  binary_path = binary_path.AppendASCII("Trusteer")
+                    .AppendASCII("Rapport")
+                    .AppendASCII("bin")
+                    .AppendASCII("RapportService.exe");
+
+  if (!base::PathExists(binary_path))
+    return;
+
+  std::wstring mutable_path_str(binary_path.value());
+  std::string product_version;
+
+  if (!GetProductVersion(&mutable_path_str, &product_version))
+    return;
+
+  AvProduct av_product;
+
+  // Assume enabled, no easy way of knowing for sure.
+  av_product.set_product_state(metrics::SystemProfileProto::AntiVirusState::
+                                   SystemProfileProto_AntiVirusState_STATE_ON);
+
+  // Taken from Add/Remove programs as the product name.
+  std::string product_name("Trusteer Endpoint Protection");
+  if (ShouldReportFullNames()) {
+    av_product.set_product_name(product_name);
+    av_product.set_product_version(product_version);
+  }
+  av_product.set_product_name_hash(metrics::HashName(product_name));
+  av_product.set_product_version_hash(metrics::HashName(product_version));
+
+  products->push_back(av_product);
+}
diff --git a/chrome/browser/metrics/antivirus_metrics_provider_win.h b/chrome/browser/metrics/antivirus_metrics_provider_win.h
index 13a6b49..0bf86852 100644
--- a/chrome/browser/metrics/antivirus_metrics_provider_win.h
+++ b/chrome/browser/metrics/antivirus_metrics_provider_win.h
@@ -72,10 +72,17 @@
   // interface is only available on Windows 8 and above.
   static ResultCode FillAntiVirusProductsFromWSC(
       std::vector<AvProduct>* products);
+
   // Query WMI ROOT\SecurityCenter2 for installed AV products. This interface is
   // only available on Windows Vista and above.
   static ResultCode FillAntiVirusProductsFromWMI(
       std::vector<AvProduct>* products);
+
+  // Query local machine configuration for other products that might not be
+  // registered in WMI or Security Center and add them to the product vector.
+  static void MaybeAddUnregisteredAntiVirusProducts(
+      std::vector<AvProduct>* products);
+
   static std::vector<AvProduct> GetAntiVirusProductsOnFileThread();
 
   // Called when metrics are done being gathered from the FILE thread.
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc
index 3f64247..0c8c82f 100644
--- a/chrome/browser/notifications/notification_platform_bridge_android.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -7,7 +7,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/android/build_info.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/command_line.h"
@@ -316,30 +315,8 @@
     const std::string& profile_id,
     bool incognito,
     std::set<std::string>* notifications) const {
-  DCHECK(notifications);
-  JNIEnv* env = AttachCurrentThread();
-
-  // Android only supports retrieving existing notifications from M+
-  if (base::android::BuildInfo::GetInstance()->sdk_int() <
-      base::android::SDK_VERSION_MARSHMALLOW) {
-    return false;
-  }
-
-  const ScopedJavaLocalRef<jstring> j_profile_id =
-      ConvertUTF8ToJavaString(env, profile_id);
-
-  ScopedJavaLocalRef<jobjectArray> j_notification_ids =
-      Java_NotificationPlatformBridge_getNotificationsForProfile(
-          env, java_object_, j_profile_id);
-  if (j_notification_ids.obj()) {
-    std::vector<std::string> notification_ids;
-    base::android::AppendJavaStringArrayToStringVector(
-        env, j_notification_ids.obj(), &notification_ids);
-    for (const auto& id : notification_ids) {
-      notifications->insert(id);
-    }
-  }
-  return true;
+  // TODO(miguelg): This can actually be implemented for M+
+  return false;
 }
 
 // static
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index 5e4e36e..0e191ae 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -95,7 +95,7 @@
         color: var(--google-grey-700);
       }
 
-      paper-dropdown-menu-light,
+      .md-select,
       paper-input {
         --paper-input-container-color: var(--google-grey-500);
         --paper-input-container-input: {
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
index b9f0f59..d8c6145 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-input/iron-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index 57fb60de..c66f40e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -13,11 +13,9 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/controls/button/label_button_border.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/menu_model_adapter.h"
@@ -131,9 +129,11 @@
 void ToolbarButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   CustomButton::GetAccessibleNodeData(node_data);
   node_data->role = ui::AX_ROLE_BUTTON_DROP_DOWN;
-  node_data->AddStringAttribute(
-      ui::AX_ATTR_ACTION, l10n_util::GetStringUTF8(IDS_APP_ACCACTION_PRESS));
   node_data->AddStateFlag(ui::AX_STATE_HASPOPUP);
+  if (enabled()) {
+    node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                               ui::AX_SUPPORTED_ACTION_PRESS);
+  }
 }
 
 std::unique_ptr<views::LabelButtonBorder> ToolbarButton::CreateDefaultBorder()
diff --git a/chrome/installer/mini_installer/BUILD.gn b/chrome/installer/mini_installer/BUILD.gn
index 0f37e54b..65df57c 100644
--- a/chrome/installer/mini_installer/BUILD.gn
+++ b/chrome/installer/mini_installer/BUILD.gn
@@ -69,6 +69,7 @@
   ]
   deps = [
     "//base",
+    "//base/test:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/chrome/installer/mini_installer/chrome_appid.cc b/chrome/installer/mini_installer/chrome_appid.cc
index 801cad8..34d3134 100644
--- a/chrome/installer/mini_installer/chrome_appid.cc
+++ b/chrome/installer/mini_installer/chrome_appid.cc
@@ -5,8 +5,16 @@
 #include "chrome/installer/mini_installer/appid.h"
 
 namespace google_update {
+
+#if defined(GOOGLE_CHROME_BUILD)
 const wchar_t kAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
 const wchar_t kMultiInstallAppGuid[] =
     L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
 const wchar_t kSxSAppGuid[] = L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}";
-}
+#else
+const wchar_t kAppGuid[] = L"";
+const wchar_t kMultiInstallAppGuid[] = L"";
+const wchar_t kSxSAppGuid[] = L"";
+#endif
+
+}  // namespace google_update
diff --git a/chrome/installer/mini_installer/configuration.cc b/chrome/installer/mini_installer/configuration.cc
index 6d20d6d..8b6857c 100644
--- a/chrome/installer/mini_installer/configuration.cc
+++ b/chrome/installer/mini_installer/configuration.cc
@@ -11,6 +11,7 @@
 #include "chrome/installer/mini_installer/appid.h"
 #include "chrome/installer/mini_installer/mini_installer_constants.h"
 #include "chrome/installer/mini_installer/mini_installer_resource.h"
+#include "chrome/installer/mini_installer/mini_string.h"
 #include "chrome/installer/mini_installer/regkey.h"
 
 namespace mini_installer {
@@ -36,54 +37,10 @@
   Clear();
 }
 
-// When multi_install is true, we are potentially:
-// 1. Performing a multi-install of some product(s) on a clean machine.
-//    Neither the product(s) nor the multi-installer will have a
-//    ClientState key in the registry, so there is no key to be modified.
-// 2. Upgrading an existing multi-install.  The multi-installer will have
-//    a ClientState key in the registry.  Only it need be modified.
-// 3. Migrating a single-install into a multi-install.  The product will
-//    have a ClientState key in the registry.  Only it need be modified.
-// To handle all cases, we inspect the product's ClientState to see if it
-// exists and its "ap" value does not contain "-multi".  This is case 3,
-// so we modify the product's ClientState.  Otherwise, we check the
-// multi-installer's ClientState and modify it if it exists.
-// TODO(bcwhite): Write a unit test for this that uses registry virtualization.
-void Configuration::SetChromeAppGuid() {
-  const HKEY root_key =
-      is_system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  const wchar_t* app_guid =
-      is_side_by_side_ ? google_update::kSxSAppGuid
-                       : google_update::kAppGuid;
-
-  // This is the value for single-install and case 3.
-  chrome_app_guid_ = app_guid;
-
-  if (is_multi_install_) {
-    ValueString value;
-    LONG ret = ERROR_SUCCESS;
-    if (ReadClientStateRegistryValue(root_key, app_guid, &ret, value)) {
-      // The product has a client state key.  See if it's a single-install.
-      if (ret == ERROR_FILE_NOT_FOUND ||
-          (ret == ERROR_SUCCESS &&
-           !FindTagInStr(value.get(), kMultiInstallTag, NULL))) {
-        // yes -- case 3: use the existing key.
-        return;
-      }
-    }
-    // error, case 1, or case 2: modify the multi-installer's key.
-    chrome_app_guid_ = google_update::kMultiInstallAppGuid;
-  }
-}
-
-bool Configuration::ReadClientStateRegistryValue(
-    const HKEY root_key, const wchar_t* app_guid,
-    LONG* retval, ValueString& value) {
-  RegKey key;
-  if (!OpenClientStateKey(root_key, app_guid, KEY_QUERY_VALUE, &key))
-    return false;
-  *retval = key.ReadSZValue(kApRegistryValue, value.get(), value.capacity());
-  return true;
+bool Configuration::Initialize(HMODULE module) {
+  Clear();
+  ReadResources(module);
+  return ParseCommandLine(::GetCommandLine());
 }
 
 const wchar_t* Configuration::program() const {
@@ -99,20 +56,13 @@
   command_line_ = NULL;
   operation_ = INSTALL_PRODUCT;
   argument_count_ = 0;
-  has_chrome_ = false;
-  is_multi_install_ = false;
   is_system_level_ = false;
   is_side_by_side_ = false;
+  is_updating_multi_chrome_ = false;
   has_invalid_switch_ = false;
   previous_version_ = NULL;
 }
 
-bool Configuration::Initialize(HMODULE module) {
-  Clear();
-  ReadResources(module);
-  return ParseCommandLine(::GetCommandLine());
-}
-
 // |command_line| is shared with this instance in the sense that this
 // instance may refer to it at will throughout its lifetime, yet it will
 // not release it.
@@ -123,25 +73,24 @@
     return false;
 
   for (int i = 1; i < argument_count_; ++i) {
-    if (0 == ::lstrcmpi(args_[i], L"--chrome-sxs"))
-      is_side_by_side_ = true;
-    else if (0 == ::lstrcmpi(args_[i], L"--chrome"))
-      has_chrome_ = true;
-    else if (0 == ::lstrcmpi(args_[i], L"--multi-install"))
-      is_multi_install_ = true;
-    else if (0 == ::lstrcmpi(args_[i], L"--system-level"))
+    if (0 == ::lstrcmpi(args_[i], L"--system-level")) {
       is_system_level_ = true;
-    else if (0 == ::lstrcmpi(args_[i], L"--cleanup"))
+#if defined(GOOGLE_CHROME_BUILD)
+    } else if (0 == ::lstrcmpi(args_[i], L"--chrome-sxs")) {
+      is_side_by_side_ = true;
+      chrome_app_guid_ = google_update::kSxSAppGuid;
+#endif
+    } else if (0 == ::lstrcmpi(args_[i], L"--cleanup")) {
       operation_ = CLEANUP;
-    else if (0 == ::lstrcmpi(args_[i], L"--chrome-frame"))
+    } else if (0 == ::lstrcmpi(args_[i], L"--chrome-frame")) {
       has_invalid_switch_ = true;
+    }
   }
 
   if (!is_system_level_)
     is_system_level_ = GetGoogleUpdateIsMachineEnvVar();
-  SetChromeAppGuid();
-  if (!is_multi_install_)
-    has_chrome_ = true;
+
+  is_updating_multi_chrome_ = IsUpdatingMultiChrome();
 
   return true;
 }
@@ -175,4 +124,29 @@
   previous_version_ = version_string;
 }
 
+bool Configuration::IsUpdatingMultiChrome() const {
+#if defined(GOOGLE_CHROME_BUILD)
+  // SxS/canary does not support multi-install.
+  if (is_side_by_side_)
+    return false;
+
+  // Is Chrome already installed as multi-install?
+  const HKEY root = is_system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  StackString<128> value;
+  RegKey key;
+  return (OpenClientsKey(root, google_update::kAppGuid, KEY_QUERY_VALUE,
+                         &key) == ERROR_SUCCESS &&
+          key.ReadSZValue(kPvRegistryValue, value.get(), value.capacity()) ==
+              ERROR_SUCCESS &&
+          value.length() != 0 &&
+          OpenClientStateKey(root, google_update::kAppGuid, KEY_QUERY_VALUE,
+                             &key) == ERROR_SUCCESS &&
+          key.ReadSZValue(kUninstallArgumentsRegistryValue, value.get(),
+                          value.capacity()) == ERROR_SUCCESS &&
+          value.findi(L"--multi-install") != nullptr);
+#else
+  return false;
+#endif
+}
+
 }  // namespace mini_installer
diff --git a/chrome/installer/mini_installer/configuration.h b/chrome/installer/mini_installer/configuration.h
index d35127a..f455883 100644
--- a/chrome/installer/mini_installer/configuration.h
+++ b/chrome/installer/mini_installer/configuration.h
@@ -7,8 +7,6 @@
 
 #include <windows.h>
 
-#include "chrome/installer/mini_installer/mini_string.h"
-
 namespace mini_installer {
 
 // A simple container of the mini_installer's configuration, as dictated by the
@@ -40,23 +38,21 @@
   // Returns the original command line.
   const wchar_t* command_line() const { return command_line_; }
 
-  // Returns the app guid to be used for Chrome.  --chrome-sxs on the command
-  // line makes this the canary's app guid.
+  // Returns the app guid to be used for Chrome. --chrome-sxs on the command
+  // line makes this the canary's app guid (Google Chrome only).
   const wchar_t* chrome_app_guid() const { return chrome_app_guid_; }
 
-  // Returns true if --chrome is explicitly or implicitly on the command line.
-  bool has_chrome() const { return has_chrome_; }
-
-  // Returns true if --multi-install is on the command line.
-  bool is_multi_install() const { return is_multi_install_; }
-
   // Returns true if --system-level is on the command line or if
   // GoogleUpdateIsMachine=1 is set in the process's environment.
   bool is_system_level() const { return is_system_level_; }
 
-  // Retuns true if --chrome-sxs is on the command line.
+  // Retuns true if --chrome-sxs is on the command line (Google Chrome only).
   bool is_side_by_side() const { return is_side_by_side_; }
 
+  // Returns true if an existing multi-install Chrome is being updated (Google
+  // Chrome only).
+  bool is_updating_multi_chrome() const { return is_updating_multi_chrome_; }
+
   // Returns true if any invalid switch is found on the command line.
   bool has_invalid_switch() const { return has_invalid_switch_; }
 
@@ -73,26 +69,18 @@
   const wchar_t* command_line_;
   int argument_count_;
   Operation operation_;
-  bool has_chrome_;
-  bool is_multi_install_;
   bool is_system_level_;
   bool is_side_by_side_;
+  bool is_updating_multi_chrome_;
   bool has_invalid_switch_;
   const wchar_t* previous_version_;
 
- protected:
-  typedef StackString<128> ValueString;
-
-  // Virtual for testing.
-  virtual bool ReadClientStateRegistryValue(
-      const HKEY root_key, const wchar_t* app_guid,
-      LONG* retval, ValueString& value);
-
  private:
-  Configuration(const Configuration&);
-  Configuration& operator=(const Configuration&);
+  Configuration(const Configuration&) = delete;
+  Configuration& operator=(const Configuration&) = delete;
 
-  void SetChromeAppGuid();
+  // Returns true if multi-install Chrome is already present on the machine.
+  bool IsUpdatingMultiChrome() const;
 };
 
 }  // namespace mini_installer
diff --git a/chrome/installer/mini_installer/configuration_test.cc b/chrome/installer/mini_installer/configuration_test.cc
index 3d9766e..9c43921 100644
--- a/chrome/installer/mini_installer/configuration_test.cc
+++ b/chrome/installer/mini_installer/configuration_test.cc
@@ -8,12 +8,16 @@
 #include <stdlib.h>
 
 #include <memory>
+#include <vector>
 
 #include "base/environment.h"
+#include "base/test/test_reg_util_win.h"
+#include "base/win/registry.h"
 #include "chrome/installer/mini_installer/appid.h"
+#include "chrome/installer/mini_installer/mini_installer_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using mini_installer::Configuration;
+namespace mini_installer {
 
 namespace {
 
@@ -33,47 +37,66 @@
   std::unique_ptr<base::Environment> env_;
 };
 
-}  // namespace
-
 class TestConfiguration : public Configuration {
  public:
-  explicit TestConfiguration(const wchar_t* command_line)
-      : Configuration(),
-        open_registry_key_result_(false),
-        read_registry_value_result_(0),
-        read_registry_value_(L"") {
-    Initialize(command_line);
+  explicit TestConfiguration(const wchar_t* command_line) {
+    EXPECT_TRUE(ParseCommandLine(command_line));
   }
-  explicit TestConfiguration(const wchar_t* command_line,
-                             LONG ret, const wchar_t* value)
-      : Configuration(),
-        open_registry_key_result_(true),
-        read_registry_value_result_(ret),
-        read_registry_value_(value) {
-    Initialize(command_line);
-  }
-  void SetRegistryResults(bool openkey, LONG ret, const wchar_t* value) {
-  }
- private:
-  bool open_registry_key_result_;
-  LONG read_registry_value_result_;
-  const wchar_t* read_registry_value_ = L"";
 
-  void Initialize(const wchar_t* command_line) {
-    Clear();
-    ASSERT_TRUE(ParseCommandLine(command_line));
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestConfiguration);
+};
+
+}  // namespace
+
+class MiniInstallerConfigurationTest : public ::testing::Test {
+ protected:
+  MiniInstallerConfigurationTest() {
+    registry_overrides_.OverrideRegistry(HKEY_CURRENT_USER);
+    registry_overrides_.OverrideRegistry(HKEY_LOCAL_MACHINE);
   }
-  bool ReadClientStateRegistryValue(
-      const HKEY root_key, const wchar_t* app_guid,
-      LONG* retval, ValueString& value) override {
-    *retval = read_registry_value_result_;
-    value.assign(read_registry_value_);
-    return open_registry_key_result_;
+
+  // Adds sufficient state in the registry for Configuration to think that
+  // Chrome is already installed at |system_level| as per |multi_install|.
+  void AddChromeRegistryState(bool system_level, bool multi_install) {
+#if defined(GOOGLE_CHROME_BUILD)
+    static constexpr wchar_t kClientsPath[] =
+        L"SOFTWARE\\Google\\Update\\Clients\\"
+        L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+    static constexpr wchar_t kClientStatePath[] =
+        L"SOFTWARE\\Google\\Update\\ClientState\\"
+        L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+#else
+    static constexpr wchar_t kClientsPath[] = L"SOFTWARE\\Chromium";
+    static constexpr wchar_t kClientStatePath[] = L"SOFTWARE\\Chromium";
+#endif
+    static constexpr const wchar_t* kUninstallArguments[] = {
+        L"--uninstall", L"--uninstall --multi-install --chrome",
+        L"--uninstall --system-level",
+        L"--uninstall --system-level --multi-install --chrome",
+    };
+    const int uninstall_index =
+        ((system_level ? 0x02 : 0) | (multi_install ? 0x01 : 0));
+    const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+    base::win::RegKey key;
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.Create(root, kClientsPath, KEY_WOW64_32KEY | KEY_SET_VALUE));
+    ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"pv", L"4.3.2.1"));
+    ASSERT_EQ(ERROR_SUCCESS, key.Create(root, kClientStatePath,
+                                        KEY_WOW64_32KEY | KEY_SET_VALUE));
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.WriteValue(L"UninstallArguments",
+                             kUninstallArguments[uninstall_index]));
   }
+
+ private:
+  registry_util::RegistryOverrideManager registry_overrides_;
+
+  DISALLOW_COPY_AND_ASSIGN(MiniInstallerConfigurationTest);
 };
 
 // Test that the operation type is CLEANUP iff --cleanup is on the cmdline.
-TEST(MiniInstallerConfigurationTest, Operation) {
+TEST_F(MiniInstallerConfigurationTest, Operation) {
   EXPECT_EQ(Configuration::INSTALL_PRODUCT,
             TestConfiguration(L"spam.exe").operation());
   EXPECT_EQ(Configuration::INSTALL_PRODUCT,
@@ -87,7 +110,7 @@
             TestConfiguration(L"spam.exe --cleanup now").operation());
 }
 
-TEST(MiniInstallerConfigurationTest, Program) {
+TEST_F(MiniInstallerConfigurationTest, Program) {
   EXPECT_TRUE(NULL == mini_installer::Configuration().program());
   EXPECT_TRUE(std::wstring(L"spam.exe") ==
               TestConfiguration(L"spam.exe").program());
@@ -97,13 +120,13 @@
               TestConfiguration(L"c:\\blaz\\spam.exe --with args").program());
 }
 
-TEST(MiniInstallerConfigurationTest, ArgumentCount) {
+TEST_F(MiniInstallerConfigurationTest, ArgumentCount) {
   EXPECT_EQ(1, TestConfiguration(L"spam.exe").argument_count());
   EXPECT_EQ(2, TestConfiguration(L"spam.exe --foo").argument_count());
   EXPECT_EQ(3, TestConfiguration(L"spam.exe --foo --bar").argument_count());
 }
 
-TEST(MiniInstallerConfigurationTest, CommandLine) {
+TEST_F(MiniInstallerConfigurationTest, CommandLine) {
   static const wchar_t* const kCommandLines[] = {
     L"",
     L"spam.exe",
@@ -115,52 +138,47 @@
   }
 }
 
-TEST(MiniInstallerConfigurationTest, ChromeAppGuid) {
-  EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
-              TestConfiguration(L"spam.exe").chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
-              TestConfiguration(L"spam.exe --chrome").chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kSxSAppGuid) ==
-              TestConfiguration(L"spam.exe --chrome-sxs").chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kMultiInstallAppGuid) ==
-              TestConfiguration(L"spam.exe --multi-install --chrome")
-                  .chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kMultiInstallAppGuid) ==
-              TestConfiguration(L"spam.exe --multi-install --chrome",
-                                ERROR_INVALID_FUNCTION, L"")
-                  .chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
-              TestConfiguration(L"spam.exe --multi-install --chrome",
-                                ERROR_FILE_NOT_FOUND, L"")
-                  .chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
-              TestConfiguration(L"spam.exe --multi-install --chrome",
-                                ERROR_SUCCESS, L"foo-bar")
-                  .chrome_app_guid());
-  EXPECT_TRUE(std::wstring(google_update::kMultiInstallAppGuid) ==
-              TestConfiguration(L"spam.exe --multi-install --chrome",
-                                ERROR_SUCCESS, L"foo-multi")
-                  .chrome_app_guid());
+TEST_F(MiniInstallerConfigurationTest, IsUpdatingUserSingle) {
+  AddChromeRegistryState(false /* !system_level */, false /* !multi_install */);
+  EXPECT_FALSE(TestConfiguration(L"spam.exe").is_updating_multi_chrome());
 }
 
-TEST(MiniInstallerConfigurationTest, HasChrome) {
-  EXPECT_TRUE(TestConfiguration(L"spam.exe").has_chrome());
-  EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome").has_chrome());
-  EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome")
-                  .has_chrome());
-  EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install").has_chrome());
+TEST_F(MiniInstallerConfigurationTest, IsUpdatingSystemSingle) {
+  AddChromeRegistryState(true /* system_level */, false /* !multi_install */);
+  EXPECT_FALSE(
+      TestConfiguration(L"spam.exe --system-level").is_updating_multi_chrome());
 }
 
-TEST(MiniInstallerConfigurationTest, IsMultiInstall) {
-  EXPECT_FALSE(TestConfiguration(L"spam.exe").is_multi_install());
-  EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_multi_install());
-  EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome")
-                  .is_multi_install());
-  EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install")
-                  .is_multi_install());
+TEST_F(MiniInstallerConfigurationTest, IsUpdatingUserMulti) {
+  AddChromeRegistryState(false /* !system_level */, true /* multi_install */);
+#if defined(GOOGLE_CHROME_BUILD)
+  EXPECT_TRUE(TestConfiguration(L"spam.exe").is_updating_multi_chrome());
+#else
+  EXPECT_FALSE(TestConfiguration(L"spam.exe").is_updating_multi_chrome());
+#endif
 }
 
-TEST(MiniInstallerConfigurationTest, IsSystemLevel) {
+TEST_F(MiniInstallerConfigurationTest, IsUpdatingSystemMulti) {
+  AddChromeRegistryState(true /* system_level */, true /* multi_install */);
+#if defined(GOOGLE_CHROME_BUILD)
+  EXPECT_TRUE(
+      TestConfiguration(L"spam.exe --system-level").is_updating_multi_chrome());
+#else
+  EXPECT_FALSE(
+      TestConfiguration(L"spam.exe --system-level").is_updating_multi_chrome());
+#endif
+}
+
+TEST_F(MiniInstallerConfigurationTest, ChromeAppGuid) {
+#if defined(GOOGLE_CHROME_BUILD)
+  EXPECT_STREQ(google_update::kAppGuid,
+               TestConfiguration(L"spam.exe").chrome_app_guid());
+  EXPECT_STREQ(google_update::kSxSAppGuid,
+               TestConfiguration(L"spam.exe --chrome-sxs").chrome_app_guid());
+#endif
+}
+
+TEST_F(MiniInstallerConfigurationTest, IsSystemLevel) {
   EXPECT_FALSE(TestConfiguration(L"spam.exe").is_system_level());
   EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_system_level());
   EXPECT_TRUE(TestConfiguration(L"spam.exe --system-level").is_system_level());
@@ -176,8 +194,19 @@
   }
 }
 
-TEST(MiniInstallerConfigurationTest, HasInvalidSwitch) {
+TEST_F(MiniInstallerConfigurationTest, IsSideBySide) {
+  EXPECT_FALSE(TestConfiguration(L"spam.exe").is_side_by_side());
+#if defined(GOOGLE_CHROME_BUILD)
+  EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome-sxs").is_side_by_side());
+#else
+  EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome-sxs").is_side_by_side());
+#endif
+}
+
+TEST_F(MiniInstallerConfigurationTest, HasInvalidSwitch) {
   EXPECT_FALSE(TestConfiguration(L"spam.exe").has_invalid_switch());
   EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome-frame")
                   .has_invalid_switch());
 }
+
+}  // namespace mini_installer
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc
index 9656ca2d..7ca6e9f1 100644
--- a/chrome/installer/mini_installer/mini_installer.cc
+++ b/chrome/installer/mini_installer/mini_installer.cc
@@ -74,89 +74,99 @@
   PathString* setup_resource_path;
 };
 
-
-// Opens the Google Update ClientState key for the current install
-// configuration.  This includes locating the correct key in the face of
-// multi-install.  The flag will by default be written to HKCU, but if
-// --system-level is included in the command line, it will be written to
-// HKLM instead.
-bool OpenInstallStateKey(const Configuration& configuration, RegKey* key) {
+#if defined(GOOGLE_CHROME_BUILD)
+// Opens the Google Update ClientState key. If |binaries| is false, opens the
+// key for Google Chrome or Chrome SxS (canary). If |binaries| is true and an
+// existing multi-install Chrome is being updated, opens the key for the
+// binaries; otherwise, returns false.
+bool OpenInstallStateKey(const Configuration& configuration,
+                         bool binaries,
+                         RegKey* key) {
+  if (binaries && !configuration.is_updating_multi_chrome())
+    return false;
   const HKEY root_key =
       configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  const wchar_t* app_guid = configuration.chrome_app_guid();
+  const wchar_t* app_guid = binaries ? google_update::kMultiInstallAppGuid
+                                     : configuration.chrome_app_guid();
   const REGSAM key_access = KEY_QUERY_VALUE | KEY_SET_VALUE;
 
-  return OpenClientStateKey(root_key, app_guid, key_access, key);
+  return OpenClientStateKey(root_key, app_guid, key_access, key) ==
+         ERROR_SUCCESS;
 }
 
-// Writes install results into registry where it is read by Google Update.
+// Writes install results into the registry where it is read by Google Update.
 // Don't write anything if there is already a result present, likely
 // written by setup.exe.
 void WriteInstallResults(const Configuration& configuration,
                          ProcessExitResult result) {
-#if defined(GOOGLE_CHROME_BUILD)
   // Calls to setup.exe will write a "success" result if everything was good
   // so we don't need to write anything from here.
   if (result.IsSuccess())
     return;
 
-  RegKey key;
-  DWORD value;
-  if (OpenInstallStateKey(configuration, &key)) {
-    if (key.ReadDWValue(kInstallerResultRegistryValue, &value)
-            != ERROR_SUCCESS || value == 0) {
-      key.WriteDWValue(kInstallerResultRegistryValue,
-                       result.exit_code ? 1 /* FAILED_CUSTOM_ERROR */
-                                        : 0 /* SUCCESS */);
-      key.WriteDWValue(kInstallerErrorRegistryValue, result.exit_code);
-      key.WriteDWValue(kInstallerExtraCode1RegistryValue, result.windows_error);
+  // Write the value in Chrome ClientState key and in the binaries' if an
+  // existing multi-install Chrome is being updated.
+  for (int i = 0; i < 2; ++i) {
+    RegKey key;
+    DWORD value;
+    if (OpenInstallStateKey(configuration, i != 0, &key)) {
+      if (key.ReadDWValue(kInstallerResultRegistryValue, &value) !=
+              ERROR_SUCCESS ||
+          value == 0) {
+        key.WriteDWValue(kInstallerResultRegistryValue,
+                         result.exit_code ? 1 /* FAILED_CUSTOM_ERROR */
+                                          : 0 /* SUCCESS */);
+        key.WriteDWValue(kInstallerErrorRegistryValue, result.exit_code);
+        key.WriteDWValue(kInstallerExtraCode1RegistryValue,
+                         result.windows_error);
+      }
     }
-    key.Close();
   }
-#endif
 }
 
 // This function sets the flag in registry to indicate that Google Update
 // should try full installer next time. If the current installer works, this
 // flag is cleared by setup.exe at the end of install.
 void SetInstallerFlags(const Configuration& configuration) {
-  RegKey key;
   StackString<128> value;
-  LONG ret = ERROR_SUCCESS;
 
-  if (!OpenInstallStateKey(configuration, &key))
-    return;
+  for (int i = 0; i < 2; ++i) {
+    RegKey key;
+    if (!OpenInstallStateKey(configuration, i != 0, &key))
+      continue;
 
-  ret = key.ReadSZValue(kApRegistryValue, value.get(), value.capacity());
+    LONG ret = key.ReadSZValue(kApRegistryValue, value.get(), value.capacity());
 
-  // The conditions below are handling two cases:
-  // 1. When ap value is present, we want to add the required tag only if it is
-  //    not present.
-  // 2. When ap value is missing, we are going to create it with the required
-  //    tag.
-  if ((ret == ERROR_SUCCESS) || (ret == ERROR_FILE_NOT_FOUND)) {
-    if (ret == ERROR_FILE_NOT_FOUND)
-      value.clear();
+    // The conditions below are handling two cases:
+    // 1. When ap value is present, we want to add the required tag only if it
+    //    is not present.
+    // 2. When ap value is missing, we are going to create it with the required
+    //    tag.
+    if ((ret == ERROR_SUCCESS) || (ret == ERROR_FILE_NOT_FOUND)) {
+      if (ret == ERROR_FILE_NOT_FOUND)
+        value.clear();
 
-    if (!StrEndsWith(value.get(), kFullInstallerSuffix) &&
-        value.append(kFullInstallerSuffix)) {
-      key.WriteSZValue(kApRegistryValue, value.get());
+      if (!StrEndsWith(value.get(), kFullInstallerSuffix) &&
+          value.append(kFullInstallerSuffix)) {
+        key.WriteSZValue(kApRegistryValue, value.get());
+      }
     }
   }
 }
+#endif  // GOOGLE_CHROME_BUILD
 
 // Gets the setup.exe path from Registry by looking at the value of Uninstall
 // string.  |size| is measured in wchar_t units.
 ProcessExitResult GetSetupExePathForAppGuid(bool system_level,
-                                          const wchar_t* app_guid,
-                                          const wchar_t* previous_version,
-                                          wchar_t* path,
-                                          size_t size) {
+                                            const wchar_t* app_guid,
+                                            const wchar_t* previous_version,
+                                            wchar_t* path,
+                                            size_t size) {
   const HKEY root_key = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
   RegKey key;
-  if (!OpenClientStateKey(root_key, app_guid, KEY_QUERY_VALUE, &key))
-    return ProcessExitResult(UNABLE_TO_FIND_REGISTRY_KEY);
-  DWORD result = key.ReadSZValue(kUninstallRegistryValue, path, size);
+  LONG result = OpenClientStateKey(root_key, app_guid, KEY_QUERY_VALUE, &key);
+  if (result == ERROR_SUCCESS)
+    result = key.ReadSZValue(kUninstallRegistryValue, path, size);
   if (result != ERROR_SUCCESS)
     return ProcessExitResult(UNABLE_TO_FIND_REGISTRY_KEY, result);
 
@@ -173,24 +183,23 @@
 // in the Uninstall string in the registry. A previous version number specified
 // in |configuration| is used if available. |size| is measured in wchar_t units.
 ProcessExitResult GetPreviousSetupExePath(const Configuration& configuration,
-                                        wchar_t* path,
-                                        size_t size) {
+                                          wchar_t* path,
+                                          size_t size) {
   bool system_level = configuration.is_system_level();
   const wchar_t* previous_version = configuration.previous_version();
   ProcessExitResult exit_code = ProcessExitResult(GENERIC_ERROR);
 
-  // If this is a multi install, first try looking in the binaries for the path.
-  if (configuration.is_multi_install()) {
-    exit_code = GetSetupExePathForAppGuid(
-        system_level, google_update::kMultiInstallAppGuid, previous_version,
-        path, size);
-  }
+  // Check Chrome's ClientState key for the path to setup.exe. This will have
+  // the correct path for all well-functioning installs.
+  exit_code =
+      GetSetupExePathForAppGuid(system_level, configuration.chrome_app_guid(),
+                                previous_version, path, size);
 
-  // Make a last-ditch effort to look in the Chrome client state key.
-  if (!exit_code.IsSuccess()) {
-    exit_code = GetSetupExePathForAppGuid(
-        system_level, configuration.chrome_app_guid(), previous_version,
-        path, size);
+  // Failing that, check the binaries if updating multi-install Chrome.
+  if (!exit_code.IsSuccess() && configuration.is_updating_multi_chrome()) {
+    exit_code = GetSetupExePathForAppGuid(system_level,
+                                          google_update::kMultiInstallAppGuid,
+                                          previous_version, path, size);
   }
 
   return exit_code;
@@ -451,8 +460,8 @@
 
 // Executes setup.exe, waits for it to finish and returns the exit code.
 ProcessExitResult RunSetup(const Configuration& configuration,
-                         const wchar_t* archive_path,
-                         const wchar_t* setup_path) {
+                           const wchar_t* archive_path,
+                           const wchar_t* setup_path) {
   // There could be three full paths in the command line for setup.exe (path
   // to exe itself, path to archive and path to log file), so we declare
   // total size as three + one additional to hold command line options.
@@ -848,8 +857,9 @@
 #if defined(GOOGLE_CHROME_BUILD)
   // Set the magic suffix in registry to try full installer next time. We ignore
   // any errors here and we try to set the suffix for user level unless
-  // --system-level is on the command line in which case we set it for system
-  // level instead. This only applies to the Google Chrome distribution.
+  // GoogleUpdateIsMachine=1 is present in the environment or --system-level is
+  // on the command line in which case we set it for system level instead. This
+  // only applies to the Google Chrome distribution.
   SetInstallerFlags(configuration);
 #endif
 
@@ -869,7 +879,10 @@
   if (ShouldDeleteExtractedFiles())
     DeleteExtractedFiles(base_path.get(), archive_path.get(), setup_path.get());
 
+#if defined(GOOGLE_CHROME_BUILD)
   WriteInstallResults(configuration, exit_code);
+#endif
+
   return exit_code;
 }
 
diff --git a/chrome/installer/mini_installer/mini_installer_constants.cc b/chrome/installer/mini_installer/mini_installer_constants.cc
index e21e433..751d25b 100644
--- a/chrome/installer/mini_installer/mini_installer_constants.cc
+++ b/chrome/installer/mini_installer/mini_installer_constants.cc
@@ -50,12 +50,16 @@
 const wchar_t kInstallerErrorRegistryValue[] = L"InstallerError";
 const wchar_t kInstallerExtraCode1RegistryValue[] = L"InstallerExtraCode1";
 const wchar_t kInstallerResultRegistryValue[] = L"InstallerResult";
+const wchar_t kPvRegistryValue[] = L"pv";
+const wchar_t kUninstallArgumentsRegistryValue[] = L"UninstallArguments";
 // The name of an app's Client State registry value that holds the path to its
 // uninstaller.
 const wchar_t kUninstallRegistryValue[] = L"UninstallString";
 
 // Registry key paths.
 #if defined(GOOGLE_CHROME_BUILD)
+// The path to the key containing each app's Clients registry key.
+const wchar_t kClientsKeyBase[] = L"Software\\Google\\Update\\Clients\\";
 // The path to the key containing each app's Client State registry key.
 const wchar_t kClientStateKeyBase[] =
     L"Software\\Google\\Update\\ClientState\\";
@@ -63,6 +67,9 @@
 const wchar_t kCleanupRegistryKey[] =
     L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Google Chrome";
 #else
+// The path to the key containing each app's Clients registry key.
+// No trailing slash on this one because the app's GUID is not appended.
+const wchar_t kClientsKeyBase[] = L"Software\\Chromium";
 // The path to the key containing each app's Client State registry key.
 // No trailing slash on this one because the app's GUID is not appended.
 const wchar_t kClientStateKeyBase[] = L"Software\\Chromium";
diff --git a/chrome/installer/mini_installer/mini_installer_constants.h b/chrome/installer/mini_installer/mini_installer_constants.h
index bd80c696..a176df99c 100644
--- a/chrome/installer/mini_installer/mini_installer_constants.h
+++ b/chrome/installer/mini_installer/mini_installer_constants.h
@@ -37,9 +37,12 @@
 extern const wchar_t kInstallerErrorRegistryValue[];
 extern const wchar_t kInstallerExtraCode1RegistryValue[];
 extern const wchar_t kInstallerResultRegistryValue[];
+extern const wchar_t kPvRegistryValue[];
+extern const wchar_t kUninstallArgumentsRegistryValue[];
 extern const wchar_t kUninstallRegistryValue[];
 
 // Registry key paths.
+extern const wchar_t kClientsKeyBase[];
 extern const wchar_t kClientStateKeyBase[];
 extern const wchar_t kCleanupRegistryKey[];
 
diff --git a/chrome/installer/mini_installer/regkey.cc b/chrome/installer/mini_installer/regkey.cc
index 44bdb5d..ca95507 100644
--- a/chrome/installer/mini_installer/regkey.cc
+++ b/chrome/installer/mini_installer/regkey.cc
@@ -72,7 +72,6 @@
   }
 }
 
-
 // static
 bool RegKey::ReadSZValue(HKEY root_key, const wchar_t *sub_key,
                          const wchar_t *value_name, wchar_t *value,
@@ -82,17 +81,34 @@
           key.ReadSZValue(value_name, value, size) == ERROR_SUCCESS);
 }
 
-// Opens the Google Update ClientState key for a product.  This finds only
-// registry entries for Chrome; it does not support the Chromium registry
-// layout.
-bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid,
-                        REGSAM access, RegKey* key) {
+// Opens the Google Update Clients key for a product.
+LONG OpenClientsKey(HKEY root_key,
+                    const wchar_t* app_guid,
+                    REGSAM access,
+                    RegKey* key) {
+  StackString<MAX_PATH> clients_key;
+  if (!clients_key.assign(kClientsKeyBase))
+    return ERROR_BUFFER_OVERFLOW;
+#if defined(GOOGLE_CHROME_BUILD)
+  if (!clients_key.append(app_guid))
+    return ERROR_BUFFER_OVERFLOW;
+#endif
+  return key->Open(root_key, clients_key.get(), access | KEY_WOW64_32KEY);
+}
+
+// Opens the Google Update ClientState key for a product.
+LONG OpenClientStateKey(HKEY root_key,
+                        const wchar_t* app_guid,
+                        REGSAM access,
+                        RegKey* key) {
   StackString<MAX_PATH> client_state_key;
-  return client_state_key.assign(kClientStateKeyBase) &&
-         client_state_key.append(app_guid) &&
-         (key->Open(root_key,
-                    client_state_key.get(),
-                    access | KEY_WOW64_32KEY) == ERROR_SUCCESS);
+  if (!client_state_key.assign(kClientStateKeyBase))
+    return ERROR_BUFFER_OVERFLOW;
+#if defined(GOOGLE_CHROME_BUILD)
+  if (!client_state_key.append(app_guid))
+    return ERROR_BUFFER_OVERFLOW;
+#endif
+  return key->Open(root_key, client_state_key.get(), access | KEY_WOW64_32KEY);
 }
 
 }  // namespace mini_installer
diff --git a/chrome/installer/mini_installer/regkey.h b/chrome/installer/mini_installer/regkey.h
index f323b7c6..86dbf8b1 100644
--- a/chrome/installer/mini_installer/regkey.h
+++ b/chrome/installer/mini_installer/regkey.h
@@ -55,11 +55,19 @@
   HKEY key_;
 };  // class RegKey
 
+// Initializes |key| with the desired |access| to |app_guid|'s Clients key.
+// Returns ERROR_SUCCESS on success, or a Windows error code on failure.
+LONG OpenClientsKey(HKEY root_key,
+                    const wchar_t* app_guid,
+                    REGSAM access,
+                    RegKey* key);
 
-// Helper function to get the RegKey associated with the "client state" of
-// the given |app_guid|.
-bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid,
-                        REGSAM access, RegKey* key);
+// Initializes |key| with the desired |access| to |app_guid|'s ClientState key.
+// Returns ERROR_SUCCESS on success, or a Windows error code on failure.
+LONG OpenClientStateKey(HKEY root_key,
+                        const wchar_t* app_guid,
+                        REGSAM access,
+                        RegKey* key);
 
 }  // namespace mini_installer
 
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index 9b36e81..b180811 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -897,12 +897,9 @@
     return;
   }
 
-  const ProductState* chrome_state = original_state.GetProductState(
-      installer_state.system_install(),
-      BrowserDistribution::CHROME_BROWSER);
-  // Bail out if there is not an existing multi-install Chrome that is being
-  // updated.
-  if (!chrome_state || !chrome_state->is_multi_install()) {
+  // Bail out if an existing multi-install Chrome is not being migrated to
+  // single-install.
+  if (!installer_state.is_migrating_to_single()) {
     VLOG(1) << "No multi-install Chrome found to migrate to single-install.";
     return;
   }
diff --git a/chrome/installer/setup/installer_state.cc b/chrome/installer/setup/installer_state.cc
index 17ad8616..b6a9ffb 100644
--- a/chrome/installer/setup/installer_state.cc
+++ b/chrome/installer/setup/installer_state.cc
@@ -73,7 +73,8 @@
       root_key_(NULL),
       msi_(false),
       background_mode_(false),
-      verbose_logging_(false) {}
+      verbose_logging_(false),
+      is_migrating_to_single_(false) {}
 
 InstallerState::InstallerState(Level level)
     : operation_(UNINITIALIZED),
@@ -84,7 +85,8 @@
       root_key_(NULL),
       msi_(false),
       background_mode_(false),
-      verbose_logging_(false) {
+      verbose_logging_(false),
+      is_migrating_to_single_(false) {
   // Use set_level() so that root_key_ is updated properly.
   set_level(level);
 }
@@ -138,6 +140,10 @@
     // For a single-install, the current browser dist is the operand.
     operand = BrowserDistribution::GetDistribution();
     operation_ = SINGLE_INSTALL_OR_UPDATE;
+    // Is this a migration from multi-install to single-install?
+    const ProductState* state =
+        machine_state.GetProductState(system_install(), operand->GetType());
+    is_migrating_to_single_ = state && state->is_multi_install();
   } else if (IsMultiInstallUpdate(prefs, machine_state)) {
     // Updates driven by Google Update take place under the multi-installer's
     // app guid.
@@ -473,6 +479,7 @@
   root_key_ = NULL;
   msi_ = false;
   verbose_logging_ = false;
+  is_migrating_to_single_ = false;
 }
 
 bool InstallerState::AnyExistsAndIsInUse(const InstallationState& machine_state,
@@ -625,6 +632,24 @@
     InstallUtil::AddInstallerResultItems(
         system_install, multi_package_binaries_distribution()->GetStateKey(),
         status, string_resource_id, launch_cmd, install_list.get());
+  } else if (is_migrating_to_single() &&
+             InstallUtil::GetInstallReturnCode(status)) {
+#if defined(GOOGLE_CHROME_BUILD)
+    // Also write to the binaries on error if this is a migration back to
+    // single-install for Google Chrome builds. Skip this for Chromium builds
+    // because they lump the "ClientState" and "Clients" keys into a single
+    // key. As a consequence, writing this value causes Software\Chromium to be
+    // re-created after it was deleted during the migration to single-install.
+    // Google Chrome builds don't suffer this since the two keys are distinct
+    // and have different lifetimes. The result is only written on failure since
+    // for success, the binaries have been uninstalled and therefore the result
+    // will not be read by Google Update.
+    InstallUtil::AddInstallerResultItems(
+        system_install, BrowserDistribution::GetSpecificDistribution(
+                            BrowserDistribution::CHROME_BINARIES)
+                            ->GetStateKey(),
+        status, string_resource_id, launch_cmd, install_list.get());
+#endif
   }
   install_list->Do();
 }
diff --git a/chrome/installer/setup/installer_state.h b/chrome/installer/setup/installer_state.h
index 9a5e9704..2c25f3c 100644
--- a/chrome/installer/setup/installer_state.h
+++ b/chrome/installer/setup/installer_state.h
@@ -148,6 +148,10 @@
   // with the ClientState key we will be interacting with.
   BrowserDistribution::Type state_type() const { return state_type_; }
 
+  // Returns true if this is an update of multi-install Chrome to
+  // single-install.
+  bool is_migrating_to_single() const { return is_migrating_to_single_; }
+
   // Returns the BrowserDistribution instance corresponding to the binaries for
   // this run if we're operating on a multi-package product.
   BrowserDistribution* multi_package_binaries_distribution() const {
@@ -270,6 +274,7 @@
   bool msi_;
   bool background_mode_;
   bool verbose_logging_;
+  bool is_migrating_to_single_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InstallerState);
diff --git a/chrome/installer/setup/installer_state_unittest.cc b/chrome/installer/setup/installer_state_unittest.cc
index c34c3a09..9f82c5c 100644
--- a/chrome/installer/setup/installer_state_unittest.cc
+++ b/chrome/installer/setup/installer_state_unittest.cc
@@ -123,12 +123,16 @@
   std::wstring value;
   DWORD dw_value;
 
-  // check results for a fresh install of single Chrome
-  {
+  // Check results for a fresh install of single Chrome and the same for an
+  // attempt at multi-install, which is now ignored.
+  static constexpr const wchar_t* kCommandLines[] = {
+      L"setup.exe --system-level",
+      L"setup.exe --system-level --multi-install --chrome",
+  };
+  for (const wchar_t* command_line : kCommandLines) {
     RegistryOverrideManager override_manager;
     override_manager.OverrideRegistry(root);
-    base::CommandLine cmd_line =
-        base::CommandLine::FromString(L"setup.exe --system-level");
+    base::CommandLine cmd_line = base::CommandLine::FromString(command_line);
     const MasterPreferences prefs(cmd_line);
     InstallationState machine_state;
     machine_state.Initialize();
@@ -154,38 +158,6 @@
         key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
     EXPECT_EQ(launch_cmd, value);
   }
-
-  // check results for a fresh install of multi Chrome
-  {
-    RegistryOverrideManager override_manager;
-    override_manager.OverrideRegistry(root);
-    base::CommandLine cmd_line = base::CommandLine::FromString(
-        L"setup.exe --system-level --multi-install --chrome");
-    const MasterPreferences prefs(cmd_line);
-    InstallationState machine_state;
-    machine_state.Initialize();
-    InstallerState state;
-    state.Initialize(cmd_line, prefs, machine_state);
-    state.WriteInstallerResult(installer::FIRST_INSTALL_SUCCESS, 0,
-                               &launch_cmd);
-    BrowserDistribution* distribution =
-        BrowserDistribution::GetSpecificDistribution(
-            BrowserDistribution::CHROME_BROWSER);
-    BrowserDistribution* binaries =
-        BrowserDistribution::GetSpecificDistribution(
-            BrowserDistribution::CHROME_BINARIES);
-    EXPECT_EQ(ERROR_SUCCESS,
-        key.Open(root, distribution->GetStateKey().c_str(), KEY_READ));
-    EXPECT_EQ(ERROR_SUCCESS,
-        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
-    EXPECT_EQ(launch_cmd, value);
-    EXPECT_EQ(ERROR_SUCCESS,
-        key.Open(root, binaries->GetStateKey().c_str(), KEY_READ));
-    EXPECT_EQ(ERROR_SUCCESS,
-        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
-    EXPECT_EQ(launch_cmd, value);
-    key.Close();
-  }
 }
 
 // Test GetCurrentVersion when migrating single Chrome to multi
@@ -259,27 +231,27 @@
 
   InstallerState installer_state;
 
-  // Initialize the instance to install multi Chrome.
+  // Initialize the instance to install user-level Chrome.
   {
-    base::CommandLine cmd_line(
-        base::CommandLine::FromString(L"setup.exe --multi-install --chrome"));
+    base::CommandLine cmd_line(base::CommandLine::FromString(L"setup.exe"));
     MasterPreferences prefs(cmd_line);
     installer_state.Initialize(cmd_line, prefs, machine_state);
   }
   // Confirm the expected state.
   EXPECT_EQ(InstallerState::USER_LEVEL, installer_state.level());
-  EXPECT_EQ(InstallerState::MULTI_PACKAGE, installer_state.package_type());
-  EXPECT_EQ(InstallerState::MULTI_INSTALL, installer_state.operation());
+  EXPECT_EQ(InstallerState::SINGLE_PACKAGE, installer_state.package_type());
+  EXPECT_EQ(InstallerState::SINGLE_INSTALL_OR_UPDATE,
+            installer_state.operation());
   EXPECT_TRUE(wcsstr(installer_state.target_path().value().c_str(),
                      BrowserDistribution::GetSpecificDistribution(
-                         BrowserDistribution::CHROME_BINARIES)->
-                         GetInstallSubDir().c_str()));
+                         BrowserDistribution::CHROME_BROWSER)
+                         ->GetInstallSubDir()
+                         .c_str()));
   EXPECT_FALSE(installer_state.verbose_logging());
   EXPECT_EQ(installer_state.state_key(),
             BrowserDistribution::GetSpecificDistribution(
                 BrowserDistribution::CHROME_BROWSER)->GetStateKey());
   EXPECT_EQ(installer_state.state_type(), BrowserDistribution::CHROME_BROWSER);
-  EXPECT_TRUE(installer_state.multi_package_binaries_distribution());
   EXPECT_TRUE(installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER));
 
   // Now initialize it to install system-level single Chrome.
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index d0d02980..94f3853 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -1705,6 +1705,9 @@
   InstallerState installer_state;
   installer_state.Initialize(cmd_line, prefs, original_state);
 
+  VLOG(1) << "is_migrating_to_single is "
+          << installer_state.is_migrating_to_single();
+
   persistent_histogram_storage.set_storage_dir(
       installer::PersistentHistogramStorage::GetReportedStorageDir(
           installer_state.target_path()));
diff --git a/chrome/installer/util/master_preferences.cc b/chrome/installer/util/master_preferences.cc
index 8ec3e04..ef58f813 100644
--- a/chrome/installer/util/master_preferences.cc
+++ b/chrome/installer/util/master_preferences.cc
@@ -75,34 +75,19 @@
 
 namespace installer {
 
-MasterPreferences::MasterPreferences() : distribution_(NULL),
-                                         preferences_read_from_file_(false),
-                                         chrome_(true),
-                                         multi_install_(false) {
+MasterPreferences::MasterPreferences() {
   InitializeFromCommandLine(*base::CommandLine::ForCurrentProcess());
 }
 
-MasterPreferences::MasterPreferences(const base::CommandLine& cmd_line)
-    : distribution_(NULL),
-      preferences_read_from_file_(false),
-      chrome_(true),
-      multi_install_(false) {
+MasterPreferences::MasterPreferences(const base::CommandLine& cmd_line) {
   InitializeFromCommandLine(cmd_line);
 }
 
-MasterPreferences::MasterPreferences(const base::FilePath& prefs_path)
-    : distribution_(NULL),
-      preferences_read_from_file_(false),
-      chrome_(true),
-      multi_install_(false) {
+MasterPreferences::MasterPreferences(const base::FilePath& prefs_path) {
   InitializeFromFilePath(prefs_path);
 }
 
-MasterPreferences::MasterPreferences(const std::string& prefs)
-    : distribution_(NULL),
-      preferences_read_from_file_(false),
-      chrome_(true),
-      multi_install_(false) {
+MasterPreferences::MasterPreferences(const std::string& prefs) {
   InitializeFromString(prefs);
 }
 
@@ -129,14 +114,10 @@
     const char* cmd_line_switch;
     const char* distribution_switch;
   } translate_switches[] = {
-    { installer::switches::kChrome,
-      installer::master_preferences::kChrome },
     { installer::switches::kDisableLogging,
       installer::master_preferences::kDisableLogging },
     { installer::switches::kMsi,
       installer::master_preferences::kMsi },
-    { installer::switches::kMultiInstall,
-      installer::master_preferences::kMultiInstall },
     { installer::switches::kDoNotRegisterForUpdateLaunch,
       installer::master_preferences::kDoNotRegisterForUpdateLaunch },
     { installer::switches::kDoNotLaunchChrome,
@@ -181,11 +162,17 @@
     }
   }
 
+  // Strip multi-install from the dictionary, if present. This ensures that any
+  // code that probes the dictionary directly to check for multi-install (rather
+  // than calling is_multi_install()) receives false. The updated dictionary is
+  // not written back to disk.
+  master_dictionary_->Remove(std::string(master_preferences::kDistroDict) +
+                                 '.' + master_preferences::kMultiInstall,
+                             nullptr);
+
   // Cache a pointer to the distribution dictionary. Ignore errors if any.
   master_dictionary_->GetDictionary(installer::master_preferences::kDistroDict,
                                     &distribution_);
-
-  InitializeProductFlags();
 #endif
 }
 
@@ -217,30 +204,10 @@
         installer::master_preferences::kDistroDict, &distribution_);
   }
 
-  InitializeProductFlags();
   EnforceLegacyPreferences();
   return data_is_valid;
 }
 
-void MasterPreferences::InitializeProductFlags() {
-  // Make sure we start out with the correct defaults.
-  multi_install_ = false;
-  chrome_ = true;
-
-  GetBool(installer::master_preferences::kMultiInstall, &multi_install_);
-
-  // When multi-install is specified, the checks are pretty simple (in theory):
-  // In order to be installed/uninstalled, each product must have its switch
-  // present on the command line.
-  // When multi-install is not set, operate on Chrome.
-  if (multi_install_) {
-    if (!GetBool(installer::master_preferences::kChrome, &chrome_))
-      chrome_ = false;
-  } else {
-    chrome_ = true;
-  }
-}
-
 void MasterPreferences::EnforceLegacyPreferences() {
   // If create_all_shortcuts was explicitly set to false, set
   // do_not_create_(desktop|quick_launch)_shortcut to true.
diff --git a/chrome/installer/util/master_preferences.h b/chrome/installer/util/master_preferences.h
index 424c4df..c2ffc62 100644
--- a/chrome/installer/util/master_preferences.h
+++ b/chrome/installer/util/master_preferences.h
@@ -170,13 +170,9 @@
     return preferences_read_from_file_;
   }
 
-  bool install_chrome() const {
-    return chrome_;
-  }
+  bool install_chrome() const { return true; }
 
-  bool is_multi_install() const {
-    return multi_install_;
-  }
+  bool is_multi_install() const { return false; }
 
   // Returns a reference to this MasterPreferences' root dictionary of values.
   const base::DictionaryValue& master_dictionary() const {
@@ -197,8 +193,6 @@
   // string was successfully parsed.
   bool InitializeFromString(const std::string& json_data);
 
-  void InitializeProductFlags();
-
   // Enforces legacy preferences that should no longer be used, but could be
   // found in older master_preferences files.
   void EnforceLegacyPreferences();
@@ -209,10 +203,8 @@
   std::string ExtractPrefString(const std::string& name) const;
 
   std::unique_ptr<base::DictionaryValue> master_dictionary_;
-  base::DictionaryValue* distribution_;
-  bool preferences_read_from_file_;
-  bool chrome_;
-  bool multi_install_;
+  base::DictionaryValue* distribution_ = nullptr;
+  bool preferences_read_from_file_ = false;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MasterPreferences);
diff --git a/chrome/installer/util/master_preferences_constants.cc b/chrome/installer/util/master_preferences_constants.cc
index 27eeed8..e65450b 100644
--- a/chrome/installer/util/master_preferences_constants.cc
+++ b/chrome/installer/util/master_preferences_constants.cc
@@ -6,7 +6,6 @@
 
 namespace installer {
 namespace master_preferences {
-  const char kChrome[] = "chrome";
   const char kCreateAllShortcuts[] = "create_all_shortcuts";
   const char kDisableLogging[] = "disable_logging";
   const char kDistroDict[] = "distribution";
diff --git a/chrome/installer/util/master_preferences_constants.h b/chrome/installer/util/master_preferences_constants.h
index d49947b..cb394167 100644
--- a/chrome/installer/util/master_preferences_constants.h
+++ b/chrome/installer/util/master_preferences_constants.h
@@ -15,8 +15,6 @@
 // is specified in master preference as well as command line, the command line
 // value takes precedence.
 
-// Boolean. This is to be a Chrome install. (When using MultiInstall)
-extern const char kChrome[];
 // Boolean. This is a legacy preference and should no longer be used; it is
 // kept around so that old master_preferences which specify
 // "create_all_shortcuts":false still enforce the new
diff --git a/chrome/installer/util/master_preferences_unittest.cc b/chrome/installer/util/master_preferences_unittest.cc
index 93a8c19..a8bfba3 100644
--- a/chrome/installer/util/master_preferences_unittest.cc
+++ b/chrome/installer/util/master_preferences_unittest.cc
@@ -326,7 +326,7 @@
   EXPECT_TRUE(pref_chrome.install_chrome());
 }
 
-TEST_F(MasterPreferencesTest, TestMultiInstallConfig) {
+TEST_F(MasterPreferencesTest, TestMultiInstallIgnoredConfig) {
   using installer::switches::kMultiInstall;
   using installer::switches::kChrome;
 
@@ -338,7 +338,7 @@
 
   installer::MasterPreferences pref_chrome(chrome_install);
 
-  EXPECT_TRUE(pref_chrome.is_multi_install());
+  EXPECT_FALSE(pref_chrome.is_multi_install());
   EXPECT_TRUE(pref_chrome.install_chrome());
 }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java
index 0d774e2..bd9b9b7 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.test.util.browser.notifications;
 
 import android.app.Notification;
-import android.service.notification.StatusBarNotification;
 
 import org.chromium.chrome.browser.notifications.NotificationManagerProxy;
 
@@ -102,11 +101,6 @@
         mMutationCount++;
     }
 
-    @Override
-    public StatusBarNotification[] getActiveNotifications() {
-        return null;
-    }
-
     private static String makeKey(int id, @Nullable String tag) {
         String key = Integer.toString(id);
         if (tag != null) key += KEY_SEPARATOR + tag;
diff --git a/chrome/test/mini_installer/config/chrome_multi_system_installed.prop b/chrome/test/mini_installer/config/chrome_multi_system_installed.prop
new file mode 100644
index 0000000..9a8dd5df3
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_multi_system_installed.prop
@@ -0,0 +1,49 @@
+{
+  "Files": {
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\chrome.exe": {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\chrome.VisualElementsManifest.xml": {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome.dll":
+        {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
+        {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
+        {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
+        {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\$MINI_INSTALLER_FILE_VERSION.manifest":
+        {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION":
+        {"exists": false}
+  },
+  "RegistryEntries": {
+    "HKEY_LOCAL_MACHINE\\$CHROME_UPDATE_REGISTRY_SUBKEY": {
+      "exists": "required",
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
+      "exists": "required",
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\$LAUNCHER_UPDATE_REGISTRY_SUBKEY": {
+      "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
+      "exists": "forbidden"
+    },
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME": {
+      "exists": "required",
+      "values": {
+        "UninstallString": {
+          "type": "SZ",
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --multi-install --chrome --system-level --verbose-logging"
+        },
+        "Version": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\$CHROME_SHORT_NAME": {
+      "exists": "forbidden"
+    }
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_single_user_installed.prop b/chrome/test/mini_installer/config/chrome_multi_user_installed.prop
similarity index 80%
rename from chrome/test/mini_installer/config/chrome_single_user_installed.prop
rename to chrome/test/mini_installer/config/chrome_multi_user_installed.prop
index 5d02054..0ec67d1 100644
--- a/chrome/test/mini_installer/config/chrome_single_user_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_multi_user_installed.prop
@@ -1,6 +1,7 @@
 {
   "Files": {
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe": {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.VisualElementsManifest.xml": {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome.dll":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
@@ -10,7 +11,9 @@
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\$MINI_INSTALLER_FILE_VERSION.manifest":
-        {"exists": true}
+        {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION":
+        {"exists": false}
   },
   "RegistryEntries": {
     "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY": {
@@ -20,7 +23,10 @@
       }
     },
     "HKEY_CURRENT_USER\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
-      "exists": "forbidden"
+      "exists": "required",
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
     },
     "HKEY_CURRENT_USER\\$LAUNCHER_UPDATE_REGISTRY_SUBKEY": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
@@ -31,7 +37,7 @@
       "values": {
         "UninstallString": {
           "type": "SZ",
-          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --verbose-logging"
+          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --multi-install --chrome --verbose-logging"
         },
         "Version": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
       }
diff --git a/chrome/test/mini_installer/config/chrome_system_installed.prop b/chrome/test/mini_installer/config/chrome_system_installed.prop
index 9a8dd5df3..bc1c7c4 100644
--- a/chrome/test/mini_installer/config/chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_installed.prop
@@ -23,10 +23,7 @@
       }
     },
     "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
-      "exists": "required",
-      "values": {
-        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
-      }
+      "exists": "forbidden"
     },
     "HKEY_LOCAL_MACHINE\\$LAUNCHER_UPDATE_REGISTRY_SUBKEY": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
@@ -37,7 +34,7 @@
       "values": {
         "UninstallString": {
           "type": "SZ",
-          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --multi-install --chrome --system-level --verbose-logging"
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --system-level --verbose-logging"
         },
         "Version": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
       }
diff --git a/chrome/test/mini_installer/config/chrome_system_updated.prop b/chrome/test/mini_installer/config/chrome_system_updated.prop
index 2e7be0c8..dddf793b 100644
--- a/chrome/test/mini_installer/config/chrome_system_updated.prop
+++ b/chrome/test/mini_installer/config/chrome_system_updated.prop
@@ -23,12 +23,7 @@
       }
     },
     "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
-      "exists": "required",
-      "values": {
-        "pv": {"type": "SZ", "data": "$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION"},
-        "opv": {},
-        "cmd": {}
-      }
+      "exists": "forbidden"
     },
     "HKEY_LOCAL_MACHINE\\$LAUNCHER_UPDATE_REGISTRY_SUBKEY": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
@@ -39,7 +34,7 @@
       "values": {
         "UninstallString": {
           "type": "SZ",
-          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --multi-install --chrome --system-level --verbose-logging"
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --system-level --verbose-logging"
         },
         "Version": {"type": "SZ", "data": "$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION"}
       }
diff --git a/chrome/test/mini_installer/config/chrome_user_installed.prop b/chrome/test/mini_installer/config/chrome_user_installed.prop
index 0ec67d1..1dccebf 100644
--- a/chrome/test/mini_installer/config/chrome_user_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_installed.prop
@@ -23,10 +23,7 @@
       }
     },
     "HKEY_CURRENT_USER\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
-      "exists": "required",
-      "values": {
-        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
-      }
+      "exists": "forbidden"
     },
     "HKEY_CURRENT_USER\\$LAUNCHER_UPDATE_REGISTRY_SUBKEY": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
@@ -37,7 +34,7 @@
       "values": {
         "UninstallString": {
           "type": "SZ",
-          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --multi-install --chrome --verbose-logging"
+          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --verbose-logging"
         },
         "Version": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
       }
diff --git a/chrome/test/mini_installer/config/chrome_user_updated.prop b/chrome/test/mini_installer/config/chrome_user_updated.prop
index d8b478fa..a0e74a1 100644
--- a/chrome/test/mini_installer/config/chrome_user_updated.prop
+++ b/chrome/test/mini_installer/config/chrome_user_updated.prop
@@ -23,12 +23,7 @@
       }
     },
     "HKEY_CURRENT_USER\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
-      "exists": "required",
-      "values": {
-        "pv": {"type": "SZ", "data": "$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION"},
-        "opv": {},
-        "cmd": {}
-      }
+      "exists": "forbidden"
     },
     "HKEY_CURRENT_USER\\$LAUNCHER_UPDATE_REGISTRY_SUBKEY": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
@@ -39,7 +34,7 @@
       "values": {
         "UninstallString": {
           "type": "SZ",
-          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --multi-install --chrome --verbose-logging"
+          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --verbose-logging"
         },
         "Version": {"type": "SZ", "data": "$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION"}
       }
diff --git a/chrome/test/mini_installer/config/config.config b/chrome/test/mini_installer/config/config.config
index 9467fb2b..6860526 100644
--- a/chrome/test/mini_installer/config/config.config
+++ b/chrome/test/mini_installer/config/config.config
@@ -21,8 +21,8 @@
                                            "chrome_user_not_inuse.prop",
                                            "chrome_canary_not_inuse.prop",
                                            "chrome_system_not_inuse.prop"]],
-    ["chrome_single_user_installed_not_inuse",
-      ["chrome_single_user_installed.prop",
+    ["chrome_multi_user_installed_not_inuse",
+      ["chrome_multi_user_installed.prop",
        "chrome_canary_not_installed.prop",
        "chrome_system_not_installed.prop",
        "chrome_user_not_inuse.prop",
@@ -80,16 +80,14 @@
                                          "chrome_system_not_inuse.prop"]]
   ],
   "actions": [
-    ["delete_user_chrome_lastrun",
-     "reg.exe delete \"HKEY_CURRENT_USER\\$CHROME_CLIENT_STATE_KEY\" /v lastrun /f /reg:32"],
     ["install_chrome_canary",
      "\"$MINI_INSTALLER\" --chrome-sxs --verbose-logging --do-not-launch-chrome"],
-    ["install_chrome_single_user",
-     "\"$MINI_INSTALLER\" --verbose-logging --do-not-launch-chrome"],
     ["install_chrome_system",
-     "\"$MINI_INSTALLER\" --chrome --multi-install --verbose-logging --system-level --do-not-launch-chrome"],
+     "\"$MINI_INSTALLER\" --verbose-logging --system-level --do-not-launch-chrome"],
     ["install_chrome_user",
-     "\"$MINI_INSTALLER\" --chrome --multi-install --verbose-logging --do-not-launch-chrome"],
+     "\"$MINI_INSTALLER\" --verbose-logging --do-not-launch-chrome"],
+    ["make_chrome_user_multi",
+     "python make_chrome_multi.py --chrome-long-name \"$CHROME_LONG_NAME\" --chrome-clients-key \"$CHROME_UPDATE_REGISTRY_SUBKEY\" --chrome-client-state-key \"$CHROME_CLIENT_STATE_KEY\" --binaries-clients-key \"$BINARIES_UPDATE_REGISTRY_SUBKEY\""],
     ["kill_user_binaries",
      "reg.exe delete \"HKEY_CURRENT_USER\\$BINARIES_UPDATE_REGISTRY_SUBKEY\" /v pv /f /reg:32"],
     ["kill_user_chrome",
@@ -115,88 +113,14 @@
     ["update_chrome_canary",
      "\"$NEXT_VERSION_MINI_INSTALLER\" --chrome-sxs --verbose-logging --do-not-launch-chrome"],
     ["update_chrome_system",
-     "\"$NEXT_VERSION_MINI_INSTALLER\" --multi-install --verbose-logging --system-level --do-not-launch-chrome"],
+     "\"$NEXT_VERSION_MINI_INSTALLER\" --verbose-logging --system-level --do-not-launch-chrome"],
     ["update_chrome_user",
-     "\"$NEXT_VERSION_MINI_INSTALLER\" --multi-install --verbose-logging --do-not-launch-chrome"],
-    ["update_multi_chrome_user",
-     "\"$MINI_INSTALLER\" --multi-install --verbose-logging --do-not-launch-chrome"],
-    ["update_user_chrome_lastrun",
-     "python update_lastrun.py \"$CHROME_CLIENT_STATE_KEY\""]
+     "\"$NEXT_VERSION_MINI_INSTALLER\" --verbose-logging --do-not-launch-chrome"]
   ],
   "tests": [
     {
-      "name": "RepairChromeStrandedBinaries",
-      "description": "Verifies that a multi-install update will repair Chrome if Chrome is missing from the Clients key yet appears to be actively used.",
-      "traversal": [
-        "no_pv",
-        "install_chrome_user", "chrome_user_installed_not_inuse",
-        "update_user_chrome_lastrun", "chrome_user_installed_not_inuse",
-        "kill_user_chrome", "no_chrome_user",
-        "update_multi_chrome_user", "chrome_user_installed_not_inuse",
-        "uninstall_chrome_user", "clean"
-      ]
-    },
-    {
-      "name": "RemoveStrandedBinaries",
-      "description": "Verifies that a multi-install update will uninstall Chrome and the binaries if Chrome is missing from the Clients key and does not appear to be actively used.",
-      "traversal": [
-        "no_pv",
-        "install_chrome_user", "chrome_user_installed_not_inuse",
-        "update_user_chrome_lastrun", "chrome_user_installed_not_inuse",
-        "delete_user_chrome_lastrun", "chrome_user_installed_not_inuse",
-        "kill_user_chrome", "no_chrome_user",
-        "update_multi_chrome_user", "clean"
-      ]
-    },
-    {
-      "name": "MultiToSingle",
-      "description": "Verifies that a single-install update on top of multi-install Chrome will migrate.",
-      "traversal": [
-        "no_pv",
-        "install_chrome_user", "chrome_user_installed_not_inuse",
-        "install_chrome_single_user", "chrome_single_user_installed_not_inuse",
-        "uninstall_chrome_user", "clean"
-      ]
-    },
-    {
-      "name": "MultiToSingleNoBinaries",
-      "description": "Verifies that a single-install update on top of multi-install Chrome will migrate when the binaries are missing from the Clients key.",
-      "traversal": [
-        "no_pv",
-        "install_chrome_user", "chrome_user_installed_not_inuse",
-        "kill_user_binaries", "no_chrome_user_binaries",
-        "install_chrome_single_user", "chrome_single_user_installed_not_inuse",
-        "uninstall_chrome_user", "clean"
-      ]
-    },
-    {
-      "name": "MultiToSingleNoChromeRepairActive",
-      "description": "Verifies that a single-install update on top of multi-install Chrome will migrate when Chrome is missing from the Clients key yet appears to be actively used.",
-      "traversal": [
-        "no_pv",
-        "install_chrome_user", "chrome_user_installed_not_inuse",
-        "update_user_chrome_lastrun", "chrome_user_installed_not_inuse",
-        "kill_user_chrome", "no_chrome_user",
-        "install_chrome_single_user", "chrome_single_user_installed_not_inuse",
-        "uninstall_chrome_user", "clean"
-      ]
-    },
-    {
-      "name": "MultiToSingleNoChromeRepairInactive",
-      "description": "Verifies that a single-install update on top of multi-install Chrome will migrate if Chrome is missing from the Clients key and does not appear to be actively used.",
-      "traversal": [
-        "no_pv",
-        "install_chrome_user", "chrome_user_installed_not_inuse",
-        "update_user_chrome_lastrun", "chrome_user_installed_not_inuse",
-        "delete_user_chrome_lastrun", "chrome_user_installed_not_inuse",
-        "kill_user_chrome", "no_chrome_user",
-        "install_chrome_single_user", "chrome_single_user_installed_not_inuse",
-        "uninstall_chrome_user", "clean"
-      ]
-    },
-    {
       "name": "ChromeUserLevel",
-      "description": "Verifies that multi-install user-level Chrome can be installed and uninstalled.",
+      "description": "Verifies that user-level Chrome can be installed and uninstalled.",
       "traversal": [
         "no_pv",
         "install_chrome_user", "chrome_user_installed_not_inuse",
@@ -205,7 +129,7 @@
     },
     {
       "name": "ChromeUserLevelUpdate",
-      "description": "Verifies that multi-install user-level Chrome can be updated.",
+      "description": "Verifies that user-level Chrome can be updated.",
       "traversal": [
         "no_pv",
         "install_chrome_user", "chrome_user_installed_not_inuse",
@@ -236,7 +160,7 @@
     },
     {
       "name": "ChromeSystemLevel",
-      "description": "Verifies that multi-install system-level Chrome can be installed and uninstalled.",
+      "description": "Verifies that system-level Chrome can be installed and uninstalled.",
       "traversal": [
         "no_pv",
         "install_chrome_system", "chrome_system_installed_not_inuse",
@@ -245,7 +169,7 @@
     },
     {
       "name": "ChromeSystemLevelUpdate",
-      "description": "Verifies that multi-install system-level Chrome can be updated.",
+      "description": "Verifies that system-level Chrome can be updated.",
       "traversal": [
         "no_pv",
         "install_chrome_system", "chrome_system_installed_not_inuse",
@@ -255,7 +179,7 @@
     },
     {
       "name": "ChromeUserLevelWithCanary",
-      "description": "Verifies that multi-install user-level Chrome and Chrome SxS can be installed simultaneously.",
+      "description": "Verifies that user-level Chrome and Chrome SxS can be installed simultaneously.",
       "condition": "$SUPPORTS_SXS",
       "traversal": [
         "no_pv",
@@ -264,6 +188,53 @@
         "uninstall_chrome_user", "chrome_canary_installed_not_inuse",
         "uninstall_chrome_canary", "clean"
       ]
+    },
+    {
+      "name": "MigrateMultiSimple",
+      "description": "Verifies that an update on top of multi-install Chrome will migrate.",
+      "traversal": [
+        "no_pv",
+        "install_chrome_user", "chrome_user_installed_not_inuse",
+        "make_chrome_user_multi", "chrome_multi_user_installed_not_inuse",
+        "update_chrome_user", "chrome_user_updated_not_inuse",
+        "uninstall_chrome_user", "clean"
+      ]
+    },
+    {
+      "name": "MigrateMultiStrandedBinariesOnUpdate",
+      "description": "Verifies that an update on top of multi-install Chrome where Chrome is missing from the Clients key will repair and migrate.",
+      "traversal": [
+        "no_pv",
+        "install_chrome_user", "chrome_user_installed_not_inuse",
+        "make_chrome_user_multi", "chrome_multi_user_installed_not_inuse",
+        "kill_user_chrome", "no_chrome_user",
+        "update_chrome_user", "chrome_user_updated_not_inuse",
+        "uninstall_chrome_user", "clean"
+      ]
+    },
+    {
+      "name": "MigrateMultiStrandedBinariesOnInstall",
+      "description": "Verifies that a same-version install on top of multi-install Chrome where Chrome is missing from the Clients key will repair and migrate.",
+      "traversal": [
+        "no_pv",
+        "install_chrome_user", "chrome_user_installed_not_inuse",
+        "make_chrome_user_multi", "chrome_multi_user_installed_not_inuse",
+        "kill_user_chrome", "no_chrome_user",
+        "install_chrome_user", "chrome_user_installed_not_inuse",
+        "uninstall_chrome_user", "clean"
+      ]
+    },
+    {
+      "name": "MigrateMultiNoBinaries",
+      "description": "Verifies that an update on top of multi-install Chrome where the binaries are missing from the Clients key will repair and migrate.",
+      "traversal": [
+        "no_pv",
+        "install_chrome_user", "chrome_user_installed_not_inuse",
+        "make_chrome_user_multi", "chrome_multi_user_installed_not_inuse",
+	"kill_user_binaries", "no_chrome_user_binaries",
+        "update_chrome_user", "chrome_user_updated_not_inuse",
+        "uninstall_chrome_user", "clean"
+      ]
     }
   ]
 }
diff --git a/chrome/test/mini_installer/make_chrome_multi.py b/chrome/test/mini_installer/make_chrome_multi.py
new file mode 100644
index 0000000..8910a45
--- /dev/null
+++ b/chrome/test/mini_installer/make_chrome_multi.py
@@ -0,0 +1,79 @@
+# 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.
+
+"""Makes an existing per-user Chrome appear to be multi-install Chrome.
+
+This script makes minimal mutations to the Windows registry to make ordinary
+single-install Chrome appear to be multi-install for purposes of testing multi-
+to single- migrations.
+"""
+
+import _winreg
+import argparse
+import sys
+
+
+def MakeChromeMulti(chrome_long_name, chrome_clients_key,
+                    chrome_client_state_key, binaries_clients_key):
+  # Update the control panel's uninstall string.
+  key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
+                        ('Software\\Microsoft\\Windows\\CurrentVersion\\'
+                         'Uninstall\\%s' % chrome_long_name), 0,
+                        _winreg.KEY_QUERY_VALUE | _winreg.KEY_SET_VALUE |
+                        _winreg.KEY_WOW64_32KEY)
+  string = _winreg.QueryValueEx(key, 'UninstallString')[0]
+  string = string.replace('--uninstall', '--uninstall --multi-install --chrome')
+  _winreg.SetValueEx(key, 'UninstallString', 0, _winreg.REG_SZ, string)
+
+  # Read Chrome's version number.
+  key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
+                        chrome_clients_key, 0,
+                        _winreg.KEY_QUERY_VALUE | _winreg.KEY_WOW64_32KEY)
+  pv = _winreg.QueryValueEx(key, 'pv')[0]
+  _winreg.CloseKey(key)
+
+  # Write that version for the binaries.
+  key = _winreg.CreateKeyEx(_winreg.HKEY_CURRENT_USER,
+                            binaries_clients_key, 0,
+                            _winreg.KEY_SET_VALUE | _winreg.KEY_WOW64_32KEY)
+  _winreg.SetValueEx(key, 'pv', 0, _winreg.REG_SZ, pv)
+  _winreg.CloseKey(key)
+
+  # Add "--multi-install --chrome" to Chrome's UninstallArguments.
+  key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
+                        chrome_client_state_key, 0,
+                        _winreg.KEY_QUERY_VALUE | _winreg.KEY_SET_VALUE |
+                        _winreg.KEY_WOW64_32KEY)
+  args = _winreg.QueryValueEx(key, 'UninstallArguments')[0]
+  args += ' --multi-install --chrome'
+  _winreg.SetValueEx(key, 'UninstallArguments', 0, _winreg.REG_SZ, args)
+
+
+def main():
+  parser = argparse.ArgumentParser(
+    description='Transforms single-install Chrome into multi-install.')
+  parser.add_argument('--chrome-long-name', default='Google Chrome',
+                      help='The full name of the product.')
+  parser.add_argument('--chrome-clients-key',
+                      default='Software\\Google\\Update\\Clients\\'
+                      '{8A69D345-D564-463c-AFF1-A69D9E530F96}',
+                      help='Chrome\'s Clients registry key path.')
+  parser.add_argument('--chrome-client-state-key',
+                      default='Software\\Google\\Update\\ClientState\\'
+                      '{8A69D345-D564-463c-AFF1-A69D9E530F96}',
+                      help='Chrome\'s ClientState registry key path.')
+  parser.add_argument('--binaries-clients-key',
+                      default='Software\\Google\\Update\\Clients\\'
+                      '{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}',
+                      help='Chrome Binaries\' Clients registry key path.')
+  args = parser.parse_args()
+  MakeChromeMulti(args.chrome_long_name,
+                  args.chrome_clients_key,
+                  args.chrome_client_state_key,
+                  args.binaries_clients_key)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/components/bookmarks/browser/BUILD.gn b/components/bookmarks/browser/BUILD.gn
index 6188b10..a4df5f1a 100644
--- a/components/bookmarks/browser/BUILD.gn
+++ b/components/bookmarks/browser/BUILD.gn
@@ -14,10 +14,6 @@
     "bookmark_codec.h",
     "bookmark_expanded_state_tracker.cc",
     "bookmark_expanded_state_tracker.h",
-    "bookmark_index.cc",
-    "bookmark_index.h",
-    "bookmark_match.cc",
-    "bookmark_match.h",
     "bookmark_model.cc",
     "bookmark_model.h",
     "bookmark_model_observer.h",
@@ -39,6 +35,10 @@
     "scoped_group_bookmark_actions.h",
     "startup_task_runner_service.cc",
     "startup_task_runner_service.h",
+    "titled_url_index.cc",
+    "titled_url_index.h",
+    "titled_url_match.cc",
+    "titled_url_match.h",
     "titled_url_node.h",
     "titled_url_node_sorter.h",
     "typed_count_sorter.cc",
diff --git a/components/bookmarks/browser/bookmark_index_unittest.cc b/components/bookmarks/browser/bookmark_index_unittest.cc
index 50be374a..5e80f39 100644
--- a/components/bookmarks/browser/bookmark_index_unittest.cc
+++ b/components/bookmarks/browser/bookmark_index_unittest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/bookmarks/browser/bookmark_index.h"
-
 #include <stddef.h>
 
 #include <string>
@@ -15,8 +13,8 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/bookmarks/test/test_bookmark_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -93,7 +91,7 @@
   void ExpectMatches(const std::string& query,
                      query_parser::MatchingAlgorithm matching_algorithm,
                      const std::vector<std::string>& expected_titles) {
-    std::vector<BookmarkMatch> matches;
+    std::vector<TitledUrlMatch> matches;
     model_->GetBookmarksMatching(ASCIIToUTF16(query), 1000, matching_algorithm,
                                  &matches);
     ASSERT_EQ(expected_titles.size(), matches.size());
@@ -112,14 +110,14 @@
   }
 
   void ExtractMatchPositions(const std::string& string,
-                             BookmarkMatch::MatchPositions* matches) {
+                             TitledUrlMatch::MatchPositions* matches) {
     for (const base::StringPiece& match :
          base::SplitStringPiece(string, ":",
                                 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
       std::vector<base::StringPiece> chunks = base::SplitStringPiece(
           match, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
       ASSERT_EQ(2U, chunks.size());
-      matches->push_back(BookmarkMatch::MatchPosition());
+      matches->push_back(TitledUrlMatch::MatchPosition());
       int chunks0, chunks1;
       EXPECT_TRUE(base::StringToInt(chunks[0], &chunks0));
       EXPECT_TRUE(base::StringToInt(chunks[1], &chunks1));
@@ -129,8 +127,8 @@
   }
 
   void ExpectMatchPositions(
-      const BookmarkMatch::MatchPositions& actual_positions,
-      const BookmarkMatch::MatchPositions& expected_positions) {
+      const TitledUrlMatch::MatchPositions& actual_positions,
+      const TitledUrlMatch::MatchPositions& expected_positions) {
     ASSERT_EQ(expected_positions.size(), actual_positions.size());
     for (size_t i = 0; i < expected_positions.size(); ++i) {
       EXPECT_EQ(expected_positions[i].first, actual_positions[i].first);
@@ -359,7 +357,7 @@
   GURL url(kAboutBlankURL);
   for (size_t i = 0; i < arraysize(data); ++i) {
     model_->AddURL(model_->other_node(), 0, UTF8ToUTF16(data[i].title), url);
-    std::vector<BookmarkMatch> matches;
+    std::vector<TitledUrlMatch> matches;
     model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 10, &matches);
     EXPECT_EQ(1u, matches.size());
     model_ = TestBookmarkClient::CreateModel();
@@ -389,11 +387,11 @@
     bookmarks.push_back(bookmark);
     AddBookmarks(bookmarks);
 
-    std::vector<BookmarkMatch> matches;
+    std::vector<TitledUrlMatch> matches;
     model_->GetBookmarksMatching(ASCIIToUTF16(data[i].query), 1000, &matches);
     ASSERT_EQ(1U, matches.size());
 
-    BookmarkMatch::MatchPositions expected_title_matches;
+    TitledUrlMatch::MatchPositions expected_title_matches;
     ExtractMatchPositions(data[i].expected_title_match_positions,
                           &expected_title_matches);
     ExpectMatchPositions(matches[0].title_match_positions,
@@ -441,11 +439,11 @@
     bookmarks.push_back(bookmark);
     AddBookmarks(bookmarks);
 
-    std::vector<BookmarkMatch> matches;
+    std::vector<TitledUrlMatch> matches;
     model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 1000, &matches);
     ASSERT_EQ(1U, matches.size()) << data[i].url << data[i].query;
 
-    BookmarkMatch::MatchPositions expected_url_matches;
+    TitledUrlMatch::MatchPositions expected_url_matches;
     ExtractMatchPositions(data[i].expected_url_match_positions,
                           &expected_url_matches);
     ExpectMatchPositions(matches[0].url_match_positions, expected_url_matches);
@@ -493,7 +491,7 @@
   const char* urls[] = {kAboutBlankURL, kAboutBlankURL};
   AddBookmarks(titles, urls, arraysize(titles));
 
-  std::vector<BookmarkMatch> matches;
+  std::vector<TitledUrlMatch> matches;
   model_->GetBookmarksMatching(ASCIIToUTF16("ABc"), 1, &matches);
   EXPECT_EQ(1U, matches.size());
 }
@@ -505,7 +503,7 @@
                                           base::WideToUTF16(L"\u0130 i"),
                                           GURL("http://www.google.com"));
 
-  std::vector<BookmarkMatch> matches;
+  std::vector<TitledUrlMatch> matches;
   model_->GetBookmarksMatching(ASCIIToUTF16("i"), 100, &matches);
   ASSERT_EQ(1U, matches.size());
   EXPECT_EQ(n1, matches[0].node);
@@ -538,7 +536,7 @@
         model->other_node(), i, UTF8ToUTF16(data[i].title), data[i].url);
 
   // Populate match nodes.
-  std::vector<BookmarkMatch> matches;
+  std::vector<TitledUrlMatch> matches;
   model->GetBookmarksMatching(ASCIIToUTF16("google"), 4, &matches);
 
   // The resulting order should be:
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 29fdef5..8639f752 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -18,13 +18,13 @@
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
-#include "components/bookmarks/browser/bookmark_index.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_model_observer.h"
 #include "components/bookmarks/browser/bookmark_node_data.h"
 #include "components/bookmarks/browser/bookmark_storage.h"
 #include "components/bookmarks/browser/bookmark_undo_delegate.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/titled_url_index.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/browser/typed_count_sorter.h"
 #include "components/favicon_base/favicon_types.h"
 #include "grit/components_strings.h"
@@ -718,7 +718,7 @@
 
 void BookmarkModel::GetBookmarksMatching(const base::string16& text,
                                          size_t max_count,
-                                         std::vector<BookmarkMatch>* matches) {
+                                         std::vector<TitledUrlMatch>* matches) {
   GetBookmarksMatching(text, max_count,
                        query_parser::MatchingAlgorithm::DEFAULT, matches);
 }
@@ -727,7 +727,7 @@
     const base::string16& text,
     size_t max_count,
     query_parser::MatchingAlgorithm matching_algorithm,
-    std::vector<BookmarkMatch>* matches) {
+    std::vector<TitledUrlMatch>* matches) {
   if (!loaded_)
     return;
 
@@ -1116,7 +1116,7 @@
       base::MakeUnique<TypedCountSorter>(client_.get());
   return std::unique_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails(
       bb_node, other_node, mobile_node, client_->GetLoadExtraNodesCallback(),
-      new BookmarkIndex(std::move(node_sorter)), next_node_id_));
+      new TitledUrlIndex(std::move(node_sorter)), next_node_id_));
 }
 
 void BookmarkModel::SetUndoDelegate(BookmarkUndoDelegate* undo_delegate) {
diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h
index 309e806..2a52eb1e 100644
--- a/components/bookmarks/browser/bookmark_model.h
+++ b/components/bookmarks/browser/bookmark_model.h
@@ -46,14 +46,14 @@
 
 class BookmarkCodecTest;
 class BookmarkExpandedStateTracker;
-class BookmarkIndex;
 class BookmarkLoadDetails;
 class BookmarkModelObserver;
 class BookmarkStorage;
 class BookmarkUndoDelegate;
 class ScopedGroupBookmarkActions;
 class TestBookmarkClient;
-struct BookmarkMatch;
+class TitledUrlIndex;
+struct TitledUrlMatch;
 
 // BookmarkModel --------------------------------------------------------------
 
@@ -243,14 +243,14 @@
   // in either the title or the URL. It uses default matching algorithm.
   void GetBookmarksMatching(const base::string16& text,
                             size_t max_count,
-                            std::vector<BookmarkMatch>* matches);
+                            std::vector<TitledUrlMatch>* matches);
 
   // Returns up to |max_count| of bookmarks containing each term from |text|
   // in either the title or the URL.
   void GetBookmarksMatching(const base::string16& text,
                             size_t max_count,
                             query_parser::MatchingAlgorithm matching_algorithm,
-                            std::vector<BookmarkMatch>* matches);
+                            std::vector<TitledUrlMatch>* matches);
 
   // Sets the store to NULL, making it so the BookmarkModel does not persist
   // any changes to disk. This is only useful during testing to speed up
@@ -450,7 +450,7 @@
   // Reads/writes bookmarks to disk.
   std::unique_ptr<BookmarkStorage> store_;
 
-  std::unique_ptr<BookmarkIndex> index_;
+  std::unique_ptr<TitledUrlIndex> index_;
 
   base::WaitableEvent loaded_signal_;
 
diff --git a/components/bookmarks/browser/bookmark_storage.cc b/components/bookmarks/browser/bookmark_storage.cc
index a71c3e3d..d32a90c 100644
--- a/components/bookmarks/browser/bookmark_storage.cc
+++ b/components/bookmarks/browser/bookmark_storage.cc
@@ -18,8 +18,8 @@
 #include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "components/bookmarks/browser/bookmark_codec.h"
-#include "components/bookmarks/browser/bookmark_index.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/titled_url_index.h"
 #include "components/bookmarks/common/bookmark_constants.h"
 
 using base::TimeTicks;
@@ -124,7 +124,7 @@
     BookmarkPermanentNode* other_folder_node,
     BookmarkPermanentNode* mobile_folder_node,
     const LoadExtraCallback& load_extra_callback,
-    BookmarkIndex* index,
+    TitledUrlIndex* index,
     int64_t max_id)
     : bb_node_(bb_node),
       other_folder_node_(other_folder_node),
diff --git a/components/bookmarks/browser/bookmark_storage.h b/components/bookmarks/browser/bookmark_storage.h
index 2496e42c3..6f86188 100644
--- a/components/bookmarks/browser/bookmark_storage.h
+++ b/components/bookmarks/browser/bookmark_storage.h
@@ -17,8 +17,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "components/bookmarks/browser/bookmark_index.h"
 #include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/browser/titled_url_index.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -51,7 +51,7 @@
                       BookmarkPermanentNode* other_folder_node,
                       BookmarkPermanentNode* mobile_folder_node,
                       const LoadExtraCallback& load_extra_callback,
-                      BookmarkIndex* index,
+                      TitledUrlIndex* index,
                       int64_t max_id);
   ~BookmarkLoadDetails();
 
@@ -79,8 +79,8 @@
   BookmarkPermanentNodeList owned_extra_nodes() {
     return std::move(extra_nodes_);
   }
-  BookmarkIndex* index() { return index_.get(); }
-  std::unique_ptr<BookmarkIndex> owned_index() { return std::move(index_); }
+  TitledUrlIndex* index() { return index_.get(); }
+  std::unique_ptr<TitledUrlIndex> owned_index() { return std::move(index_); }
 
   const BookmarkNode::MetaInfoMap& model_meta_info_map() const {
     return model_meta_info_map_;
@@ -125,7 +125,7 @@
   std::unique_ptr<BookmarkPermanentNode> mobile_folder_node_;
   LoadExtraCallback load_extra_callback_;
   BookmarkPermanentNodeList extra_nodes_;
-  std::unique_ptr<BookmarkIndex> index_;
+  std::unique_ptr<TitledUrlIndex> index_;
   BookmarkNode::MetaInfoMap model_meta_info_map_;
   int64_t model_sync_transaction_version_;
   int64_t max_id_;
diff --git a/components/bookmarks/browser/bookmark_index.cc b/components/bookmarks/browser/titled_url_index.cc
similarity index 90%
rename from components/bookmarks/browser/bookmark_index.cc
rename to components/bookmarks/browser/titled_url_index.cc
index 5413be63..abde1aa 100644
--- a/components/bookmarks/browser/bookmark_index.cc
+++ b/components/bookmarks/browser/titled_url_index.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 "components/bookmarks/browser/bookmark_index.h"
+#include "components/bookmarks/browser/titled_url_index.h"
 
 #include <stdint.h>
 
@@ -11,8 +11,8 @@
 #include "base/stl_util.h"
 #include "base/strings/utf_offset_string_conversions.h"
 #include "build/build_config.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/browser/titled_url_node.h"
 #include "components/bookmarks/browser/titled_url_node_sorter.h"
 #include "components/query_parser/snippet.h"
@@ -48,14 +48,14 @@
 
 }  // namespace
 
-BookmarkIndex::BookmarkIndex(std::unique_ptr<TitledUrlNodeSorter> sorter)
+TitledUrlIndex::TitledUrlIndex(std::unique_ptr<TitledUrlNodeSorter> sorter)
     : sorter_(std::move(sorter)) {
 }
 
-BookmarkIndex::~BookmarkIndex() {
+TitledUrlIndex::~TitledUrlIndex() {
 }
 
-void BookmarkIndex::Add(const TitledUrlNode* node) {
+void TitledUrlIndex::Add(const TitledUrlNode* node) {
   std::vector<base::string16> terms =
       ExtractQueryWords(Normalize(node->GetTitledUrlNodeTitle()));
   for (size_t i = 0; i < terms.size(); ++i)
@@ -66,7 +66,7 @@
     RegisterNode(terms[i], node);
 }
 
-void BookmarkIndex::Remove(const TitledUrlNode* node) {
+void TitledUrlIndex::Remove(const TitledUrlNode* node) {
   std::vector<base::string16> terms =
       ExtractQueryWords(Normalize(node->GetTitledUrlNodeTitle()));
   for (size_t i = 0; i < terms.size(); ++i)
@@ -77,11 +77,11 @@
     UnregisterNode(terms[i], node);
 }
 
-void BookmarkIndex::GetResultsMatching(
+void TitledUrlIndex::GetResultsMatching(
     const base::string16& input_query,
     size_t max_count,
     query_parser::MatchingAlgorithm matching_algorithm,
-    std::vector<BookmarkMatch>* results) {
+    std::vector<TitledUrlMatch>* results) {
   const base::string16 query = Normalize(input_query);
   std::vector<base::string16> terms = ExtractQueryWords(query);
   if (terms.empty())
@@ -116,7 +116,7 @@
     AddMatchToResults(*i, &parser, query_nodes, results);
 }
 
-void BookmarkIndex::SortMatches(const TitledUrlNodeSet& matches,
+void TitledUrlIndex::SortMatches(const TitledUrlNodeSet& matches,
                                 TitledUrlNodes* sorted_nodes) const {
   if (sorter_) {
     sorter_->SortMatches(matches, sorted_nodes);
@@ -125,11 +125,11 @@
   }
 }
 
-void BookmarkIndex::AddMatchToResults(
+void TitledUrlIndex::AddMatchToResults(
     const TitledUrlNode* node,
     query_parser::QueryParser* parser,
     const query_parser::QueryNodeVector& query_nodes,
-    std::vector<BookmarkMatch>* results) {
+    std::vector<TitledUrlMatch>* results) {
   if (!node) {
     return;
   }
@@ -156,7 +156,7 @@
     query_parser::QueryParser::SortAndCoalesceMatchPositions(&title_matches);
     query_parser::QueryParser::SortAndCoalesceMatchPositions(&url_matches);
   }
-  BookmarkMatch match;
+  TitledUrlMatch match;
   if (lower_title.length() == node->GetTitledUrlNodeTitle().length()) {
     // Only use title matches if the lowercase string is the same length
     // as the original string, otherwise the matches are meaningless.
@@ -167,16 +167,16 @@
   // matches in |url_matches| so they point to offsets in the original URL
   // spec, not the cleaned-up URL string that we used for matching.
   std::vector<size_t> offsets =
-      BookmarkMatch::OffsetsFromMatchPositions(url_matches);
+      TitledUrlMatch::OffsetsFromMatchPositions(url_matches);
   base::OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
   url_matches =
-      BookmarkMatch::ReplaceOffsetsInMatchPositions(url_matches, offsets);
+      TitledUrlMatch::ReplaceOffsetsInMatchPositions(url_matches, offsets);
   match.url_match_positions.swap(url_matches);
   match.node = node;
   results->push_back(match);
 }
 
-bool BookmarkIndex::GetResultsMatchingTerm(
+bool TitledUrlIndex::GetResultsMatchingTerm(
     const base::string16& term,
     bool first_term,
     query_parser::MatchingAlgorithm matching_algorithm,
@@ -227,7 +227,7 @@
   return !matches->empty();
 }
 
-std::vector<base::string16> BookmarkIndex::ExtractQueryWords(
+std::vector<base::string16> TitledUrlIndex::ExtractQueryWords(
     const base::string16& query) {
   std::vector<base::string16> terms;
   if (query.empty())
@@ -239,12 +239,12 @@
   return terms;
 }
 
-void BookmarkIndex::RegisterNode(const base::string16& term,
+void TitledUrlIndex::RegisterNode(const base::string16& term,
                                  const TitledUrlNode* node) {
   index_[term].insert(node);
 }
 
-void BookmarkIndex::UnregisterNode(const base::string16& term,
+void TitledUrlIndex::UnregisterNode(const base::string16& term,
                                    const TitledUrlNode* node) {
   Index::iterator i = index_.find(term);
   if (i == index_.end()) {
diff --git a/components/bookmarks/browser/bookmark_index.h b/components/bookmarks/browser/titled_url_index.h
similarity index 70%
rename from components/bookmarks/browser/bookmark_index.h
rename to components/bookmarks/browser/titled_url_index.h
index 52a510d..13a6455 100644
--- a/components/bookmarks/browser/bookmark_index.h
+++ b/components/bookmarks/browser/titled_url_index.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_INDEX_H_
-#define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_INDEX_H_
+#ifndef COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_INDEX_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_INDEX_H_
 
 #include <stddef.h>
 
@@ -20,19 +20,17 @@
 
 class TitledUrlNode;
 class TitledUrlNodeSorter;
-struct BookmarkMatch;
+struct TitledUrlMatch;
 
-// BookmarkIndex maintains an index of the titles and URLs of bookmarks for
-// quick look up. BookmarkIndex is owned and maintained by BookmarkModel, you
-// shouldn't need to interact directly with BookmarkIndex.
+// TitledUrlIndex maintains an index of paired titles and URLs for quick lookup.
 //
-// BookmarkIndex maintains the index (index_) as a map of sets. The map (type
-// Index) maps from a lower case string to the set (type NodeSet) of
+// TitledUrlIndex maintains the index (index_) as a map of sets. The map (type
+// Index) maps from a lower case string to the set (type TitledUrlNodeSet) of
 // TitledUrlNodes that contain that string in their title or URL.
-class BookmarkIndex {
+class TitledUrlIndex {
  public:
-  BookmarkIndex(std::unique_ptr<TitledUrlNodeSorter> sorter);
-  ~BookmarkIndex();
+  TitledUrlIndex(std::unique_ptr<TitledUrlNodeSorter> sorter);
+  ~TitledUrlIndex();
 
   // Invoked when a title/URL pair has been added to the model.
   void Add(const TitledUrlNode* node);
@@ -45,16 +43,15 @@
   void GetResultsMatching(const base::string16& query,
                           size_t max_count,
                           query_parser::MatchingAlgorithm matching_algorithm,
-                          std::vector<BookmarkMatch>* results);
+                          std::vector<TitledUrlMatch>* results);
 
  private:
   using TitledUrlNodes = std::vector<const TitledUrlNode*>;
   using TitledUrlNodeSet = std::set<const TitledUrlNode*>;
   using Index = std::map<base::string16, TitledUrlNodeSet>;
 
-  // Constructs |sorted_nodes| by taking the matches in |matches| and sorting
-  // them in decreasing order of typed count (if supported by the client) and
-  // deduping them.
+  // Constructs |sorted_nodes| by copying the matches in |matches| and sorting
+  // them.
   void SortMatches(const TitledUrlNodeSet& matches,
                    TitledUrlNodes* sorted_nodes) const;
 
@@ -62,7 +59,7 @@
   void AddMatchToResults(const TitledUrlNode* node,
                          query_parser::QueryParser* parser,
                          const query_parser::QueryNodeVector& query_nodes,
-                         std::vector<BookmarkMatch>* results);
+                         std::vector<TitledUrlMatch>* results);
 
   // Populates |matches| for the specified term. If |first_term| is true, this
   // is the first term in the query. Returns true if there is at least one node
@@ -86,9 +83,9 @@
 
   std::unique_ptr<TitledUrlNodeSorter> sorter_;
 
-  DISALLOW_COPY_AND_ASSIGN(BookmarkIndex);
+  DISALLOW_COPY_AND_ASSIGN(TitledUrlIndex);
 };
 
 }  // namespace bookmarks
 
-#endif  // COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_INDEX_H_
+#endif  // COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_INDEX_H_
diff --git a/components/bookmarks/browser/bookmark_match.cc b/components/bookmarks/browser/titled_url_match.cc
similarity index 78%
rename from components/bookmarks/browser/bookmark_match.cc
rename to components/bookmarks/browser/titled_url_match.cc
index f813cbb..313ae4ef 100644
--- a/components/bookmarks/browser/bookmark_match.cc
+++ b/components/bookmarks/browser/titled_url_match.cc
@@ -2,21 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/bookmarks/browser/bookmark_match.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 
 #include "base/logging.h"
 #include "base/strings/string16.h"
 
 namespace bookmarks {
 
-BookmarkMatch::BookmarkMatch() : node(NULL) {}
+TitledUrlMatch::TitledUrlMatch() : node(NULL) {}
 
-BookmarkMatch::BookmarkMatch(const BookmarkMatch& other) = default;
+TitledUrlMatch::TitledUrlMatch(const TitledUrlMatch& other) = default;
 
-BookmarkMatch::~BookmarkMatch() {}
+TitledUrlMatch::~TitledUrlMatch() {}
 
 // static
-std::vector<size_t> BookmarkMatch::OffsetsFromMatchPositions(
+std::vector<size_t> TitledUrlMatch::OffsetsFromMatchPositions(
     const MatchPositions& match_positions) {
   std::vector<size_t> offsets;
   for (MatchPositions::const_iterator i = match_positions.begin();
@@ -28,7 +28,7 @@
 }
 
 // static
-BookmarkMatch::MatchPositions BookmarkMatch::ReplaceOffsetsInMatchPositions(
+TitledUrlMatch::MatchPositions TitledUrlMatch::ReplaceOffsetsInMatchPositions(
     const MatchPositions& match_positions,
     const std::vector<size_t>& offsets) {
   DCHECK_EQ(2 * match_positions.size(), offsets.size());
diff --git a/components/bookmarks/browser/bookmark_match.h b/components/bookmarks/browser/titled_url_match.h
similarity index 81%
rename from components/bookmarks/browser/bookmark_match.h
rename to components/bookmarks/browser/titled_url_match.h
index 76cb0bf..767a190 100644
--- a/components/bookmarks/browser/bookmark_match.h
+++ b/components/bookmarks/browser/titled_url_match.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MATCH_H_
-#define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MATCH_H_
+#ifndef COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_MATCH_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_MATCH_H_
 
 #include <stddef.h>
 
@@ -15,15 +15,15 @@
 
 class TitledUrlNode;
 
-struct BookmarkMatch {
+struct TitledUrlMatch {
   // Each MatchPosition is the [begin, end) positions of a match within a
   // string.
   using MatchPosition = std::pair<size_t, size_t>;
   using MatchPositions = std::vector<MatchPosition>;
 
-  BookmarkMatch();
-  BookmarkMatch(const BookmarkMatch& other);
-  ~BookmarkMatch();
+  TitledUrlMatch();
+  TitledUrlMatch(const TitledUrlMatch& other);
+  ~TitledUrlMatch();
 
   // Extracts and returns the offsets from |match_positions|.
   static std::vector<size_t> OffsetsFromMatchPositions(
@@ -48,4 +48,4 @@
 
 }  // namespace bookmarks
 
-#endif  // COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MATCH_H_
+#endif  // COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_MATCH_H_
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 6635a19..88e6ad03 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -428,7 +428,7 @@
                    compositor_frame_sink_holder_),
         base::Bind(&CompositorFrameSinkHolder::Require,
                    compositor_frame_sink_holder_),
-        content_size_, contents_surface_to_layer_scale, content_size_);
+        content_size_, contents_surface_to_layer_scale);
     window_->layer()->SetFillsBoundsOpaquely(
         state_.blend_mode == SkBlendMode::kSrc ||
         state_.opaque_region.contains(
diff --git a/components/omnibox/browser/bookmark_provider.cc b/components/omnibox/browser/bookmark_provider.cc
index 1976e8d..cee15775a 100644
--- a/components/omnibox/browser/bookmark_provider.cc
+++ b/components/omnibox/browser/bookmark_provider.cc
@@ -12,8 +12,8 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/metrics/proto/omnibox_input_type.pb.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_result.h"
@@ -23,9 +23,9 @@
 #include "components/url_formatter/url_formatter.h"
 #include "url/url_constants.h"
 
-using bookmarks::BookmarkMatch;
-using BookmarkMatches = std::vector<BookmarkMatch>;
 using bookmarks::BookmarkNode;
+using bookmarks::TitledUrlMatch;
+using TitledUrlMatches = std::vector<TitledUrlMatch>;
 
 namespace {
 
@@ -34,7 +34,7 @@
 // characters are highlighted.
 void CorrectTitleAndMatchPositions(
     base::string16* title,
-    BookmarkMatch::MatchPositions* title_match_positions) {
+    TitledUrlMatch::MatchPositions* title_match_positions) {
   size_t leading_whitespace_chars = title->length();
   base::TrimWhitespace(*title, base::TRIM_LEADING, title);
   leading_whitespace_chars-= title->length();
@@ -78,7 +78,7 @@
   if (!bookmark_model_)
     return;
 
-  BookmarkMatches matches;
+  TitledUrlMatches matches;
   // Retrieve enough bookmarks so that we have a reasonable probability of
   // suggesting the one that the user desires.
   const size_t kMaxBookmarkMatches = 50;
@@ -98,7 +98,7 @@
   //  - Multiple terms enclosed in quotes will require those exact words in that
   //    exact order to match.
   //
-  // Please refer to the code for BookmarkIndex::GetBookmarksMatching for
+  // Please refer to the code for TitledUrlIndex::GetResultsMatching for
   // complete details of how searches are performed against the user's
   // bookmarks.
   bookmark_model_->GetBookmarksMatching(input.text(),
@@ -107,11 +107,11 @@
   if (matches.empty())
     return;  // There were no matches.
   const base::string16 fixed_up_input(FixupUserInput(input).second);
-  for (BookmarkMatches::const_iterator i = matches.begin(); i != matches.end();
+  for (TitledUrlMatches::const_iterator i = matches.begin(); i != matches.end();
        ++i) {
     // Create and score the AutocompleteMatch. If its score is 0 then the
     // match is discarded.
-    AutocompleteMatch match(BookmarkMatchToACMatch(input, fixed_up_input, *i));
+    AutocompleteMatch match(TitledUrlMatchToACMatch(input, fixed_up_input, *i));
     if (match.relevance > 0)
       matches_.push_back(match);
   }
@@ -155,17 +155,17 @@
 
 }  // namespace
 
-AutocompleteMatch BookmarkProvider::BookmarkMatchToACMatch(
+AutocompleteMatch BookmarkProvider::TitledUrlMatchToACMatch(
     const AutocompleteInput& input,
     const base::string16& fixed_up_input_text,
-    const BookmarkMatch& bookmark_match) {
+    const TitledUrlMatch& bookmark_match) {
   // The AutocompleteMatch we construct is non-deletable because the only
   // way to support this would be to delete the underlying bookmark, which is
   // unlikely to be what the user intends.
   AutocompleteMatch match(this, 0, false,
                           AutocompleteMatchType::BOOKMARK_TITLE);
   base::string16 title(bookmark_match.node->GetTitledUrlNodeTitle());
-  BookmarkMatch::MatchPositions new_title_match_positions =
+  TitledUrlMatch::MatchPositions new_title_match_positions =
       bookmark_match.title_match_positions;
   CorrectTitleAndMatchPositions(&title, &new_title_match_positions);
   const GURL& url(bookmark_match.node->GetTitledUrlNodeUrl());
@@ -177,7 +177,7 @@
       0 : bookmark_match.url_match_positions[0].first;
   const bool trim_http = !AutocompleteInput::HasHTTPScheme(input.text()) &&
       ((match_start == base::string16::npos) || (match_start != 0));
-  std::vector<size_t> offsets = BookmarkMatch::OffsetsFromMatchPositions(
+  std::vector<size_t> offsets = TitledUrlMatch::OffsetsFromMatchPositions(
       bookmark_match.url_match_positions);
   // In addition to knowing how |offsets| is transformed, we need to know how
   // |inline_autocomplete_offset| is transformed.  We add it to the end of
@@ -190,8 +190,8 @@
       net::UnescapeRule::SPACES, nullptr, nullptr, &offsets);
   inline_autocomplete_offset = offsets.back();
   offsets.pop_back();
-  BookmarkMatch::MatchPositions new_url_match_positions =
-      BookmarkMatch::ReplaceOffsetsInMatchPositions(
+  TitledUrlMatch::MatchPositions new_url_match_positions =
+      TitledUrlMatch::ReplaceOffsetsInMatchPositions(
           bookmark_match.url_match_positions, offsets);
   match.contents_class =
       ClassificationsFromMatch(new_url_match_positions,
diff --git a/components/omnibox/browser/bookmark_provider.h b/components/omnibox/browser/bookmark_provider.h
index 228400f1..bb2c71b 100644
--- a/components/omnibox/browser/bookmark_provider.h
+++ b/components/omnibox/browser/bookmark_provider.h
@@ -20,7 +20,7 @@
 
 namespace bookmarks {
 class BookmarkModel;
-struct BookmarkMatch;
+struct TitledUrlMatch;
 }
 
 // This class is an autocomplete provider which quickly (and synchronously)
@@ -60,10 +60,10 @@
   // title, as the description.  |input| is used to compute the match's
   // inline_autocompletion.  |fixed_up_input_text| is used in that way as well;
   // it's passed separately so this function doesn't have to compute it.
-  AutocompleteMatch BookmarkMatchToACMatch(
+  AutocompleteMatch TitledUrlMatchToACMatch(
       const AutocompleteInput& input,
       const base::string16& fixed_up_input_text,
-      const bookmarks::BookmarkMatch& match);
+      const bookmarks::TitledUrlMatch& match);
 
   // Converts |positions| into ACMatchClassifications and returns the
   // classifications. |text_length| is used to determine the need to add an
diff --git a/components/omnibox/browser/bookmark_provider_unittest.cc b/components/omnibox/browser/bookmark_provider_unittest.cc
index c962ad3..277f3c38 100644
--- a/components/omnibox/browser/bookmark_provider_unittest.cc
+++ b/components/omnibox/browser/bookmark_provider_unittest.cc
@@ -18,8 +18,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/bookmarks/browser/bookmark_match.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/test/test_bookmark_client.h"
 #include "components/metrics/proto/omnibox_event.pb.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
@@ -28,9 +28,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using bookmarks::BookmarkMatch;
 using bookmarks::BookmarkModel;
 using bookmarks::BookmarkNode;
+using bookmarks::TitledUrlMatch;
 
 namespace {
 
@@ -420,9 +420,9 @@
         provider_->FixupUserInput(input).second);
     BookmarkNode node(GURL(query_data[i].url));
     node.SetTitle(base::ASCIIToUTF16(query_data[i].url));
-    BookmarkMatch bookmark_match;
+    TitledUrlMatch bookmark_match;
     bookmark_match.node = &node;
-    const AutocompleteMatch& ac_match = provider_->BookmarkMatchToACMatch(
+    const AutocompleteMatch& ac_match = provider_->TitledUrlMatchToACMatch(
         input, fixed_up_input, bookmark_match);
     EXPECT_EQ(query_data[i].allowed_to_be_default_match,
               ac_match.allowed_to_be_default_match) << description;
diff --git a/components/security_state/core/BUILD.gn b/components/security_state/core/BUILD.gn
index 3ce60b7..bb28c1f 100644
--- a/components/security_state/core/BUILD.gn
+++ b/components/security_state/core/BUILD.gn
@@ -11,6 +11,8 @@
   sources = [
     "security_state.cc",
     "security_state.h",
+    "security_state_ui.cc",
+    "security_state_ui.h",
     "switches.cc",
     "switches.h",
   ]
diff --git a/components/security_state/core/security_state_ui.cc b/components/security_state/core/security_state_ui.cc
new file mode 100644
index 0000000..31aff66
--- /dev/null
+++ b/components/security_state/core/security_state_ui.cc
@@ -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.
+
+#include "components/security_state/core/security_state_ui.h"
+
+namespace security_state {
+
+bool ShouldAlwaysShowIcon(SecurityLevel security_level) {
+  // Enumerate all |SecurityLevel| values for compile-time enforcement that all
+  // cases are explicitly handled.
+  switch (security_level) {
+    case NONE:
+      return false;
+    case HTTP_SHOW_WARNING:
+    case EV_SECURE:
+    case SECURE:
+    case SECURITY_WARNING:
+    case SECURE_WITH_POLICY_INSTALLED_CERT:
+    case DANGEROUS:
+      return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
+}  // namespace security_state
diff --git a/components/security_state/core/security_state_ui.h b/components/security_state/core/security_state_ui.h
new file mode 100644
index 0000000..035091f
--- /dev/null
+++ b/components/security_state/core/security_state_ui.h
@@ -0,0 +1,21 @@
+// 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_SECURITY_STATE_SECURITY_STATE_UI_H_
+#define COMPONENTS_SECURITY_STATE_SECURITY_STATE_UI_H_
+
+#include "components/security_state/core/security_state.h"
+
+// Provides helper methods that encapsulate platform-independent security UI
+// logic.
+namespace security_state {
+
+// On some (mobile) form factors, the security indicator icon is hidden to save
+// UI space. This returns whether the icon should always be shown for the given
+// |security_level|, i.e. whether to override the hiding behaviour.
+bool ShouldAlwaysShowIcon(SecurityLevel security_level);
+
+}  // namespace security_state
+
+#endif  // COMPONENTS_SECURITY_STATE_SECURITY_STATE_UI_H_
diff --git a/components/user_manager/user.h b/components/user_manager/user.h
index c48fd6eb..cce5f4fe 100644
--- a/components/user_manager/user.h
+++ b/components/user_manager/user.h
@@ -78,7 +78,10 @@
     ONLINE = 4,        // WallpaperInfo.location denotes an URL.
     POLICY = 5,        // Controlled by policy, can't be changed by the user.
     THIRDPARTY = 6,    // Current wallpaper is set by a third party app.
-    WALLPAPER_TYPE_COUNT = 7
+    DEVICE = 7,        // Current wallpaper is the device policy controlled
+                       // wallpaper. It shows on the login screen if the device
+                       // is an enterprise managed device.
+    WALLPAPER_TYPE_COUNT = 8
   };
 
   // Returns true if user type has gaia account.
diff --git a/components/wallpaper/wallpaper_manager_base.cc b/components/wallpaper/wallpaper_manager_base.cc
index e387fba..7e6f39c4 100644
--- a/components/wallpaper/wallpaper_manager_base.cc
+++ b/components/wallpaper/wallpaper_manager_base.cc
@@ -473,7 +473,13 @@
   GetUserWallpaperInfo(account_id, &info);
   info.type = user_manager::User::DEFAULT;
   SetUserWallpaperInfo(account_id, info, true /* is_persistent */);
-  SetDefaultWallpaperNow(account_id);
+  // If the user's policy is cleared, try to set the device wallpaper first.
+  // Note We have to modify the user wallpaper info first. Otherwise, we won't
+  // be able to override the current user policy wallpaper. The wallpaper info
+  // will be set correctly if the device wallpaper is set successfully.
+  if (!SetDeviceWallpaperIfApplicable(account_id)) {
+    SetDefaultWallpaperNow(account_id);
+  }
 }
 
 // static
@@ -615,6 +621,15 @@
   current_user_wallpaper_info_.type = user_manager::User::DEFAULT;
   current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
 
+  std::string device_wallpaper_url;
+  std::string device_wallpaper_hash;
+  if (ShouldSetDeviceWallpaper(account_id, &device_wallpaper_url,
+                               &device_wallpaper_hash)) {
+    current_user_wallpaper_info_.location =
+        GetDeviceWallpaperFilePath().value();
+    current_user_wallpaper_info_.type = user_manager::User::DEVICE;
+  }
+
   WallpaperInfo info = current_user_wallpaper_info_;
   SetUserWallpaperInfo(account_id, info, is_persistent);
 }
@@ -708,10 +723,15 @@
     base::FilePath wallpaper_dir;
     base::FilePath wallpaper_path;
     if (info.type == user_manager::User::CUSTOMIZED ||
-        info.type == user_manager::User::POLICY) {
-      const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
-      base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
-      wallpaper_path = wallpaper_path.Append(info.location);
+        info.type == user_manager::User::POLICY ||
+        info.type == user_manager::User::DEVICE) {
+      base::FilePath wallpaper_path;
+      if (info.type == user_manager::User::DEVICE) {
+        wallpaper_path = GetDeviceWallpaperFilePath();
+      } else {
+        const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
+        wallpaper_path = GetCustomWallpaperDir(sub_dir).Append(info.location);
+      }
       // Set the path to the cache.
       wallpaper_cache_[account_id] =
           CustomWallpaperElement(wallpaper_path, gfx::ImageSkia());
diff --git a/components/wallpaper/wallpaper_manager_base.h b/components/wallpaper/wallpaper_manager_base.h
index a734d2f..cd18065d 100644
--- a/components/wallpaper/wallpaper_manager_base.h
+++ b/components/wallpaper/wallpaper_manager_base.h
@@ -354,6 +354,14 @@
   // Ruturns files identifier for the |account_id|.
   virtual WallpaperFilesId GetFilesId(const AccountId& account_id) const = 0;
 
+  // If the device is enterprise managed and we're at the login screen, set the
+  // device wallpaper as the login screen wallpaper. If the device is enterprise
+  // managed and we're in a user session, only set the device wallpaper if there
+  // is no user policy wallpaper and the user hasn't changed the default or the
+  // device wallpaper. Returns true if the device wallpaper should be set as the
+  // wallpaper, otherwise returns false.
+  virtual bool SetDeviceWallpaperIfApplicable(const AccountId& account_id) = 0;
+
  protected:
   friend class TestApi;
   friend class WallpaperManagerBrowserTest;
@@ -402,7 +410,8 @@
       gfx::ImageSkia* large_wallpaper_image);
 
   // Initialize wallpaper for the specified user to default and saves this
-  // settings in local state.
+  // settings in local state. Note if the device policy controlled wallpaper
+  // exists, use the device wallpaper as the default wallpaper.
   virtual void InitInitialUserWallpaper(const AccountId& account_id,
                                         bool is_persistent);
 
@@ -472,6 +481,17 @@
   virtual bool GetUserWallpaperInfo(const AccountId& account_id,
                                     WallpaperInfo* info) const = 0;
 
+  // Returns true if the device wallpaper should be set for the account.
+  virtual bool ShouldSetDeviceWallpaper(const AccountId& account_id,
+                                        std::string* url,
+                                        std::string* hash) = 0;
+
+  // Returns the file directory where the downloaded device wallpaper is saved.
+  virtual base::FilePath GetDeviceWallpaperDir() = 0;
+
+  // Returns the full path for the downloaded device wallpaper.
+  virtual base::FilePath GetDeviceWallpaperFilePath() = 0;
+
   // Sets wallpaper to the decoded wallpaper if |update_wallpaper| is true.
   // Otherwise, cache wallpaper to memory if not logged in.  (Takes a UserImage
   // because that's the callback interface provided by UserImageLoader.)
diff --git a/content/app/strings/content_strings.grd b/content/app/strings/content_strings.grd
index 172afb17..f2f53d6a 100644
--- a/content/app/strings/content_strings.grd
+++ b/content/app/strings/content_strings.grd
@@ -511,31 +511,7 @@
           tree item
         </message>
       </if>
-      <message name="IDS_AX_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a button is pressed, as used by accessibility.">
-        press
-      </message>
-      <message name="IDS_AX_RADIO_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a radio button is clicked, as used by accessibility.">
-        select
-      </message>
-      <message name="IDS_AX_TEXT_FIELD_ACTION_VERB" desc="Verb stating the action that will occur when a text field is selected, as used by accessibility.">
-        activate
-      </message>
-      <message name="IDS_AX_CHECKED_CHECK_BOX_ACTION_VERB" desc="Verb stating the action that will occur when a checked checkbox is clicked, as used by accessibility.">
-        uncheck
-      </message>
-      <message name="IDS_AX_UNCHECKED_CHECK_BOX_ACTION_VERB" desc="Verb stating the action that will occur when an unchecked checkbox is clicked, as used by accessibility.">
-        check
-      </message>
-      <message name="IDS_AX_LINK_ACTION_VERB" desc="Verb stating the action that will occur when a link is clicked, as used by accessibility.">
-        jump
-      </message>
-      <message name="IDS_AX_POP_UP_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a pop-up button is clicked, as used by accessibility.">
-        open
-      </message>
-      <message name="IDS_AX_DEFAULT_ACTION_VERB" desc="Verb stating the action that will occur when clicking on a generic clickable object, when we don't know what that action is, as used by accessibility.">
-        click
-      </message>
-
+      
       <message name="IDS_AX_AM_PM_FIELD_TEXT" desc="Accessible description of the AM/PM field in a date/time control">
         AM/PM
       </message>
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 78f886f..e7b891e6 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -184,6 +184,8 @@
     "$target_gen_dir/devtools/protocol/schema.h",
     "$target_gen_dir/devtools/protocol/security.cc",
     "$target_gen_dir/devtools/protocol/security.h",
+    "$target_gen_dir/devtools/protocol/service_worker.cc",
+    "$target_gen_dir/devtools/protocol/service_worker.h",
     "$target_gen_dir/devtools/protocol/storage.cc",
     "$target_gen_dir/devtools/protocol/storage.h",
     "$target_gen_dir/devtools/protocol/system_info.cc",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index 5e9c8bd..00d9a069 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -40,9 +40,6 @@
     const BrowserAccessibility& node,
     base::DictionaryValue* dict) {
   dict->SetInteger("id", node.GetId());
-  auto lines = node.GetLineStartOffsets();
-  for (auto& l : lines)
-    DLOG(ERROR) << "Nektar: ui::ToString(node.GetData().role)" << l;
 
   dict->SetString("internalRole", ui::ToString(node.GetData().role));
 
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 6ae417f..4258eaf 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/common/accessibility_messages.h"
 #include "ui/accessibility/ax_text_utils.h"
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 4c873c94..934cdc6 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -155,7 +155,7 @@
 
 bool BrowserAccessibilityAndroid::IsClickable() const {
   // If it has a default action, it's definitely clickable.
-  if (HasStringAttribute(ui::AX_ATTR_ACTION))
+  if (HasIntAttribute(ui::AX_ATTR_ACTION))
     return true;
 
   // Otherwise return true if it's focusable, but skip web areas and iframes.
diff --git a/content/browser/accessibility/browser_accessibility_auralinux.cc b/content/browser/accessibility/browser_accessibility_auralinux.cc
index 1af9cccd..c990120 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_auralinux.cc
@@ -7,9 +7,11 @@
 #include <stdint.h>
 #include <string.h>
 
+#include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/browser/accessibility/browser_accessibility_manager_auralinux.h"
 #include "content/common/accessibility_messages.h"
+#include "ui/accessibility/ax_text_utils.h"
 
 namespace content {
 
@@ -91,9 +93,14 @@
   BrowserAccessibilityAuraLinux* obj =
       ToBrowserAccessibilityAuraLinux(atk_action);
   if (!obj)
-    return 0;
+    return nullptr;
 
-  return obj->GetStringAttribute(ui::AX_ATTR_ACTION).c_str();
+  int action;
+  if (!obj->GetIntAttribute(ui::AX_ATTR_ACTION, &action))
+    return nullptr;
+  base::string16 action_verb =
+      ui::ActionToUnlocalizedString(static_cast<ui::AXSupportedAction>(action));
+  return base::UTF16ToUTF8(action_verb).c_str();
 }
 
 static const gchar* browser_accessibility_get_keybinding(AtkAction* atk_action,
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index e2ad1da..d40a576 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -421,7 +421,7 @@
     return E_INVALIDARG;
 
   // Return an error if it's not clickable.
-  if (!target->HasStringAttribute(ui::AX_ATTR_ACTION))
+  if (!target->HasIntAttribute(ui::AX_ATTR_ACTION))
     return DISP_E_MEMBERNOTFOUND;
 
   manager()->DoDefaultAction(*target);
@@ -577,8 +577,7 @@
   if (!target)
     return E_INVALIDARG;
 
-  return target->GetStringAttributeAsBstr(
-      ui::AX_ATTR_ACTION, def_action);
+  return target->get_localizedName(0, def_action);
 }
 
 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
@@ -2839,7 +2838,7 @@
   // |IsHyperlink| is required for |IAccessibleHyperlink::anchor/anchorTarget|
   // to work properly because the |IAccessibleHyperlink| interface inherits from
   // |IAccessibleAction|.
-  if (IsHyperlink() || HasStringAttribute(ui::AX_ATTR_ACTION)) {
+  if (IsHyperlink() || HasIntAttribute(ui::AX_ATTR_ACTION)) {
     *n_actions = 1;
   } else {
     *n_actions = 0;
@@ -2853,7 +2852,7 @@
   if (!instance_active())
     return E_FAIL;
 
-  if (!HasStringAttribute(ui::AX_ATTR_ACTION) || action_index != 0)
+  if (!HasIntAttribute(ui::AX_ATTR_ACTION) || action_index != 0)
     return E_INVALIDARG;
 
   manager()->DoDefaultAction(*this);
@@ -2882,13 +2881,19 @@
   if (!name)
     return E_INVALIDARG;
 
-  base::string16 action_verb;
-  if (!GetString16Attribute(ui::AX_ATTR_ACTION, &action_verb) ||
-      action_index != 0) {
+  int action;
+  if (!GetIntAttribute(ui::AX_ATTR_ACTION, &action) || action_index != 0) {
     *name = nullptr;
     return E_INVALIDARG;
   }
 
+  base::string16 action_verb =
+      ui::ActionToUnlocalizedString(static_cast<ui::AXSupportedAction>(action));
+  if (action_verb.empty() || action_verb == L"none") {
+    *name = nullptr;
+    return S_FALSE;
+  }
+
   *name = SysAllocString(action_verb.c_str());
   DCHECK(name);
   return S_OK;
@@ -2898,7 +2903,28 @@
 BrowserAccessibilityWin::get_localizedName(long action_index,
                                            BSTR* localized_name) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCALIZED_NAME);
-  return E_NOTIMPL;
+  if (!instance_active())
+    return E_FAIL;
+
+  if (!localized_name)
+    return E_INVALIDARG;
+
+  int action;
+  if (!GetIntAttribute(ui::AX_ATTR_ACTION, &action) || action_index != 0) {
+    *localized_name = nullptr;
+    return E_INVALIDARG;
+  }
+
+  base::string16 action_verb =
+      ui::ActionToString(static_cast<ui::AXSupportedAction>(action));
+  if (action_verb.empty()) {
+    *localized_name = nullptr;
+    return S_FALSE;
+  }
+
+  *localized_name = SysAllocString(action_verb.c_str());
+  DCHECK(localized_name);
+  return S_OK;
 }
 
 //
diff --git a/content/browser/devtools/BUILD.gn b/content/browser/devtools/BUILD.gn
index 5ef4994..ddda5baf 100644
--- a/content/browser/devtools/BUILD.gn
+++ b/content/browser/devtools/BUILD.gn
@@ -76,6 +76,8 @@
     "protocol/schema.h",
     "protocol/security.cc",
     "protocol/security.h",
+    "protocol/service_worker.cc",
+    "protocol/service_worker.h",
     "protocol/storage.cc",
     "protocol/storage.h",
     "protocol/system_info.cc",
diff --git a/content/browser/devtools/protocol/devtools_protocol_handler_generator.py b/content/browser/devtools/protocol/devtools_protocol_handler_generator.py
index 823d2185c..60178dd 100755
--- a/content/browser/devtools/protocol/devtools_protocol_handler_generator.py
+++ b/content/browser/devtools/protocol/devtools_protocol_handler_generator.py
@@ -643,7 +643,7 @@
 includes = []
 fields_init = []
 
-browser_domains_list = ["Target", "ServiceWorker", "Input"]
+browser_domains_list = ["Target", "Input"]
 browser_commands_list = []
 async_commands_list = [
     "Input.synthesizePinchGesture",
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index c34d15a1..62a962d3 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -33,10 +33,7 @@
 #include "url/gurl.h"
 
 namespace content {
-namespace devtools {
-namespace service_worker {
-
-using Response = DevToolsProtocolClient::Response;
+namespace protocol {
 
 namespace {
 
@@ -58,13 +55,15 @@
     EmbeddedWorkerStatus running_status) {
   switch (running_status) {
     case EmbeddedWorkerStatus::STOPPED:
-      return kServiceWorkerVersionRunningStatusStopped;
+      return ServiceWorker::ServiceWorkerVersionRunningStatusEnum::Stopped;
     case EmbeddedWorkerStatus::STARTING:
-      return kServiceWorkerVersionRunningStatusStarting;
+      return ServiceWorker::ServiceWorkerVersionRunningStatusEnum::Starting;
     case EmbeddedWorkerStatus::RUNNING:
-      return kServiceWorkerVersionRunningStatusRunning;
+      return ServiceWorker::ServiceWorkerVersionRunningStatusEnum::Running;
     case EmbeddedWorkerStatus::STOPPING:
-      return kServiceWorkerVersionRunningStatusStopping;
+      return ServiceWorker::ServiceWorkerVersionRunningStatusEnum::Stopping;
+    default:
+      NOTREACHED();
   }
   return std::string();
 }
@@ -73,83 +72,23 @@
     content::ServiceWorkerVersion::Status status) {
   switch (status) {
     case content::ServiceWorkerVersion::NEW:
-      return kServiceWorkerVersionStatusNew;
+      return ServiceWorker::ServiceWorkerVersionStatusEnum::New;
     case content::ServiceWorkerVersion::INSTALLING:
-      return kServiceWorkerVersionStatusInstalling;
+      return ServiceWorker::ServiceWorkerVersionStatusEnum::Installing;
     case content::ServiceWorkerVersion::INSTALLED:
-      return kServiceWorkerVersionStatusInstalled;
+      return ServiceWorker::ServiceWorkerVersionStatusEnum::Installed;
     case content::ServiceWorkerVersion::ACTIVATING:
-      return kServiceWorkerVersionStatusActivating;
+      return ServiceWorker::ServiceWorkerVersionStatusEnum::Activating;
     case content::ServiceWorkerVersion::ACTIVATED:
-      return kServiceWorkerVersionStatusActivated;
+      return ServiceWorker::ServiceWorkerVersionStatusEnum::Activated;
     case content::ServiceWorkerVersion::REDUNDANT:
-      return kServiceWorkerVersionStatusRedundant;
+      return ServiceWorker::ServiceWorkerVersionStatusEnum::Redundant;
+    default:
+      NOTREACHED();
   }
   return std::string();
 }
 
-scoped_refptr<ServiceWorkerVersion> CreateVersionDictionaryValue(
-    const ServiceWorkerVersionInfo& version_info) {
-  std::vector<std::string> clients;
-  for (const auto& client : version_info.clients) {
-    if (client.second.type == SERVICE_WORKER_PROVIDER_FOR_WINDOW) {
-      RenderFrameHostImpl* render_frame_host = RenderFrameHostImpl::FromID(
-          client.second.process_id, client.second.route_id);
-      WebContents* web_contents =
-          WebContents::FromRenderFrameHost(render_frame_host);
-      // There is a possibility that the frame is already deleted because of the
-      // thread hopping.
-      if (!web_contents)
-        continue;
-      scoped_refptr<DevToolsAgentHost> agent_host(
-          DevToolsAgentHost::GetOrCreateFor(web_contents));
-      if (agent_host)
-        clients.push_back(agent_host->GetId());
-    } else if (client.second.type ==
-               SERVICE_WORKER_PROVIDER_FOR_SHARED_WORKER) {
-      scoped_refptr<DevToolsAgentHost> agent_host(
-          DevToolsAgentHost::GetForWorker(client.second.process_id,
-                                          client.second.route_id));
-      if (agent_host)
-        clients.push_back(agent_host->GetId());
-    }
-  }
-  scoped_refptr<ServiceWorkerVersion> version(
-      ServiceWorkerVersion::Create()
-          ->set_version_id(base::Int64ToString(version_info.version_id))
-          ->set_registration_id(
-                base::Int64ToString(version_info.registration_id))
-          ->set_script_url(version_info.script_url.spec())
-          ->set_running_status(
-                GetVersionRunningStatusString(version_info.running_status))
-          ->set_status(GetVersionStatusString(version_info.status))
-          ->set_script_last_modified(
-                version_info.script_last_modified.ToDoubleT())
-          ->set_script_response_time(
-                version_info.script_response_time.ToDoubleT())
-          ->set_controlled_clients(clients));
-  scoped_refptr<DevToolsAgentHostImpl> host(
-      ServiceWorkerDevToolsManager::GetInstance()
-          ->GetDevToolsAgentHostForWorker(
-              version_info.process_id,
-              version_info.devtools_agent_route_id));
-  if (host)
-    version->set_target_id(host->GetId());
-  return version;
-}
-
-scoped_refptr<ServiceWorkerRegistration> CreateRegistrationDictionaryValue(
-    const ServiceWorkerRegistrationInfo& registration_info) {
-  scoped_refptr<ServiceWorkerRegistration> registration(
-      ServiceWorkerRegistration::Create()
-          ->set_registration_id(
-              base::Int64ToString(registration_info.registration_id))
-          ->set_scope_url(registration_info.pattern.spec())
-          ->set_is_deleted(registration_info.delete_flag ==
-                           ServiceWorkerRegistrationInfo::IS_DELETED));
-  return registration;
-}
-
 void StopServiceWorkerOnIO(scoped_refptr<ServiceWorkerContextWrapper> context,
                            int64_t version_id) {
   if (content::ServiceWorkerVersion* version =
@@ -173,11 +112,11 @@
 }
 
 Response CreateContextErrorResponse() {
-  return Response::InternalError("Could not connect to the context");
+  return Response::Error("Could not connect to the context");
 }
 
 Response CreateInvalidVersionIdErrorResponse() {
-  return Response::InternalError("Invalid version ID");
+  return Response::InvalidParams("Invalid version ID");
 }
 
 void DidFindRegistrationForDispatchSyncEventOnIO(
@@ -217,7 +156,11 @@
 }
 
 ServiceWorkerHandler::~ServiceWorkerHandler() {
-  Disable();
+}
+
+void ServiceWorkerHandler::Wire(UberDispatcher* dispatcher) {
+  frontend_.reset(new ServiceWorker::Frontend(dispatcher->channel()));
+  ServiceWorker::Dispatcher::wire(dispatcher, this);
 }
 
 void ServiceWorkerHandler::SetRenderFrameHost(
@@ -237,19 +180,11 @@
       partition->GetServiceWorkerContext());
 }
 
-void ServiceWorkerHandler::SetClient(std::unique_ptr<Client> client) {
-  client_.swap(client);
-}
-
-void ServiceWorkerHandler::Detached() {
-  Disable();
-}
-
 Response ServiceWorkerHandler::Enable() {
   if (enabled_)
     return Response::OK();
   if (!context_)
-    return Response::InternalError("Could not connect to the context");
+    return CreateContextErrorResponse();
   enabled_ = true;
 
   context_watcher_ = new ServiceWorkerContextWatcher(
@@ -409,39 +344,89 @@
 
 void ServiceWorkerHandler::OnWorkerRegistrationUpdated(
     const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
-  std::vector<scoped_refptr<ServiceWorkerRegistration>> registration_values;
+  using Registration = ServiceWorker::ServiceWorkerRegistration;
+  std::unique_ptr<protocol::Array<Registration>> result =
+      protocol::Array<Registration>::create();
   for (const auto& registration : registrations) {
-    registration_values.push_back(
-        CreateRegistrationDictionaryValue(registration));
+    result->addItem(Registration::Create()
+        .SetRegistrationId(
+            base::Int64ToString(registration.registration_id))
+        .SetScopeURL(registration.pattern.spec())
+        .SetIsDeleted(registration.delete_flag ==
+                      ServiceWorkerRegistrationInfo::IS_DELETED)
+        .Build());
   }
-  client_->WorkerRegistrationUpdated(
-      WorkerRegistrationUpdatedParams::Create()->set_registrations(
-          registration_values));
+  frontend_->WorkerRegistrationUpdated(std::move(result));
 }
 
 void ServiceWorkerHandler::OnWorkerVersionUpdated(
     const std::vector<ServiceWorkerVersionInfo>& versions) {
-  std::vector<scoped_refptr<ServiceWorkerVersion>> version_values;
+  using Version = ServiceWorker::ServiceWorkerVersion;
+  std::unique_ptr<protocol::Array<Version>> result =
+      protocol::Array<Version>::create();
   for (const auto& version : versions) {
-    version_values.push_back(CreateVersionDictionaryValue(version));
+    std::unique_ptr<protocol::Array<std::string>> clients =
+        protocol::Array<std::string>::create();
+    for (const auto& client : version.clients) {
+      if (client.second.type == SERVICE_WORKER_PROVIDER_FOR_WINDOW) {
+        RenderFrameHostImpl* render_frame_host = RenderFrameHostImpl::FromID(
+            client.second.process_id, client.second.route_id);
+        WebContents* web_contents =
+            WebContents::FromRenderFrameHost(render_frame_host);
+        // There is a possibility that the frame is already deleted
+        // because of the thread hopping.
+        if (!web_contents)
+          continue;
+        clients->addItem(
+            DevToolsAgentHost::GetOrCreateFor(web_contents)->GetId());
+      } else if (client.second.type ==
+                 SERVICE_WORKER_PROVIDER_FOR_SHARED_WORKER) {
+        scoped_refptr<DevToolsAgentHost> agent_host(
+            DevToolsAgentHost::GetForWorker(client.second.process_id,
+                                            client.second.route_id));
+        if (agent_host)
+          clients->addItem(agent_host->GetId());
+      }
+    }
+    std::unique_ptr<Version> version_value = Version::Create()
+        .SetVersionId(base::Int64ToString(version.version_id))
+        .SetRegistrationId(
+            base::Int64ToString(version.registration_id))
+        .SetScriptURL(version.script_url.spec())
+        .SetRunningStatus(
+            GetVersionRunningStatusString(version.running_status))
+        .SetStatus(GetVersionStatusString(version.status))
+        .SetScriptLastModified(
+            version.script_last_modified.ToDoubleT())
+        .SetScriptResponseTime(
+            version.script_response_time.ToDoubleT())
+        .SetControlledClients(std::move(clients))
+        .Build();
+    scoped_refptr<DevToolsAgentHostImpl> host(
+        ServiceWorkerDevToolsManager::GetInstance()
+            ->GetDevToolsAgentHostForWorker(
+                version.process_id,
+                version.devtools_agent_route_id));
+    if (host)
+      version_value->SetTargetId(host->GetId());
+    result->addItem(std::move(version_value));
   }
-  client_->WorkerVersionUpdated(
-      WorkerVersionUpdatedParams::Create()->set_versions(version_values));
+  frontend_->WorkerVersionUpdated(std::move(result));
 }
 
 void ServiceWorkerHandler::OnErrorReported(
     int64_t registration_id,
     int64_t version_id,
     const ServiceWorkerContextObserver::ErrorInfo& info) {
-  client_->WorkerErrorReported(
-      WorkerErrorReportedParams::Create()->set_error_message(
-          ServiceWorkerErrorMessage::Create()
-              ->set_error_message(base::UTF16ToUTF8(info.error_message))
-              ->set_registration_id(base::Int64ToString(registration_id))
-              ->set_version_id(base::Int64ToString(version_id))
-              ->set_source_url(info.source_url.spec())
-              ->set_line_number(info.line_number)
-              ->set_column_number(info.column_number)));
+  frontend_->WorkerErrorReported(
+      ServiceWorker::ServiceWorkerErrorMessage::Create()
+          .SetErrorMessage(base::UTF16ToUTF8(info.error_message))
+          .SetRegistrationId(base::Int64ToString(registration_id))
+          .SetVersionId(base::Int64ToString(version_id))
+          .SetSourceURL(info.source_url.spec())
+          .SetLineNumber(info.line_number)
+          .SetColumnNumber(info.column_number)
+          .Build());
 }
 
 void ServiceWorkerHandler::ClearForceUpdate() {
@@ -449,6 +434,5 @@
     context_->SetForceUpdateOnPageLoad(false);
 }
 
-}  // namespace service_worker
-}  // namespace devtools
+}  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/service_worker_handler.h b/content/browser/devtools/protocol/service_worker_handler.h
index 892a8c7..359a08e 100644
--- a/content/browser/devtools/protocol/service_worker_handler.h
+++ b/content/browser/devtools/protocol/service_worker_handler.h
@@ -11,18 +11,11 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "content/browser/devtools/protocol/devtools_protocol_dispatcher.h"
+#include "content/browser/devtools/protocol/service_worker.h"
 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
 #include "content/browser/service_worker/service_worker_context_observer.h"
 #include "content/browser/service_worker/service_worker_info.h"
-#include "content/public/browser/devtools_agent_host.h"
-#include "content/public/browser/devtools_agent_host_client.h"
-
-// Windows headers will redefine SendMessage.
-#ifdef SendMessage
-#undef SendMessage
-#endif
 
 namespace content {
 
@@ -30,37 +23,32 @@
 class ServiceWorkerContextWatcher;
 class ServiceWorkerContextWrapper;
 
-namespace devtools {
-namespace service_worker {
+namespace protocol {
 
-class ServiceWorkerHandler {
+class ServiceWorkerHandler : public ServiceWorker::Backend {
  public:
-  typedef DevToolsProtocolClient::Response Response;
-
   ServiceWorkerHandler();
-  ~ServiceWorkerHandler();
+  ~ServiceWorkerHandler() override;
 
+  void Wire(UberDispatcher*);
   void SetRenderFrameHost(RenderFrameHostImpl* render_frame_host);
-  void SetClient(std::unique_ptr<Client> client);
-  void Detached();
 
-  // Protocol 'service worker' domain implementation.
-  Response Enable();
-  Response Disable();
-  Response Unregister(const std::string& scope_url);
-  Response StartWorker(const std::string& scope_url);
-  Response SkipWaiting(const std::string& scope_url);
-  Response StopWorker(const std::string& version_id);
-  Response UpdateRegistration(const std::string& scope_url);
-  Response InspectWorker(const std::string& version_id);
-  Response SetForceUpdateOnPageLoad(bool force_update_on_page_load);
+  Response Enable() override;
+  Response Disable() override;
+  Response Unregister(const std::string& scope_url) override;
+  Response StartWorker(const std::string& scope_url) override;
+  Response SkipWaiting(const std::string& scope_url) override;
+  Response StopWorker(const std::string& version_id) override;
+  Response UpdateRegistration(const std::string& scope_url) override;
+  Response InspectWorker(const std::string& version_id) override;
+  Response SetForceUpdateOnPageLoad(bool force_update_on_page_load) override;
   Response DeliverPushMessage(const std::string& origin,
                               const std::string& registration_id,
-                              const std::string& data);
+                              const std::string& data) override;
   Response DispatchSyncEvent(const std::string& origin,
                              const std::string& registration_id,
                              const std::string& tag,
-                             bool last_chance);
+                             bool last_chance) override;
 
  private:
   void OnWorkerRegistrationUpdated(
@@ -75,7 +63,7 @@
   void ClearForceUpdate();
 
   scoped_refptr<ServiceWorkerContextWrapper> context_;
-  std::unique_ptr<Client> client_;
+  std::unique_ptr<ServiceWorker::Frontend> frontend_;
   bool enabled_;
   scoped_refptr<ServiceWorkerContextWatcher> context_watcher_;
   RenderFrameHostImpl* render_frame_host_;
@@ -85,8 +73,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerHandler);
 };
 
-}  // namespace service_worker
-}  // namespace devtools
+}  // namespace protocol
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_SERVICE_WORKER_HANDLER_H_
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index c64ec06c..d1f1d2c 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -52,6 +52,9 @@
                 "domain": "Security"
             },
             {
+                "domain": "ServiceWorker"
+            },
+            {
                 "domain": "Storage"
             },
             {
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 21f8d39b..ad707efe 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -395,8 +395,6 @@
     RenderFrameHostImpl* host)
     : DevToolsAgentHostImpl(base::GenerateGUID()),
       input_handler_(new devtools::input::InputHandler()),
-      service_worker_handler_(
-          new devtools::service_worker::ServiceWorkerHandler()),
       target_handler_(new devtools::target::TargetHandler()),
       frame_trace_recorder_(nullptr),
       protocol_handler_(new DevToolsProtocolHandler(this)),
@@ -406,7 +404,6 @@
       frame_tree_node_(host->frame_tree_node()) {
   DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
   dispatcher->SetInputHandler(input_handler_.get());
-  dispatcher->SetServiceWorkerHandler(service_worker_handler_.get());
   dispatcher->SetTargetHandler(target_handler_.get());
 
   SetPending(host);
@@ -509,6 +506,10 @@
     security_handler_->SetRenderFrameHost(handlers_frame_host_);
   }
 
+  service_worker_handler_.reset(new protocol::ServiceWorkerHandler());
+  service_worker_handler_->Wire(session()->dispatcher());
+  service_worker_handler_->SetRenderFrameHost(handlers_frame_host_);
+
   storage_handler_.reset(new protocol::StorageHandler());
   storage_handler_->Wire(session()->dispatcher());
   storage_handler_->SetRenderFrameHost(handlers_frame_host_);
@@ -549,6 +550,8 @@
     security_handler_->Disable();
     security_handler_.reset();
   }
+  service_worker_handler_->Disable();
+  service_worker_handler_.reset();
   storage_handler_->Disable();
   storage_handler_.reset();
   tracing_handler_->Disable();
@@ -615,7 +618,6 @@
 #if defined(OS_ANDROID)
   power_save_blocker_.reset();
 #endif
-  service_worker_handler_->Detached();
   target_handler_->Detached();
   frame_trace_recorder_.reset();
   in_navigation_protocol_message_buffer_.clear();
@@ -964,7 +966,8 @@
     network_handler_->SetRenderFrameHost(host);
   if (page_handler_)
     page_handler_->SetRenderFrameHost(host);
-  service_worker_handler_->SetRenderFrameHost(host);
+  if (service_worker_handler_)
+    service_worker_handler_->SetRenderFrameHost(host);
   if (security_handler_)
     security_handler_->SetRenderFrameHost(host);
   if (storage_handler_)
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 2b464da..e4fa63e8 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -41,7 +41,6 @@
 
 namespace devtools {
 namespace input { class InputHandler; }
-namespace service_worker { class ServiceWorkerHandler; }
 namespace target { class TargetHandler; }
 }
 
@@ -54,6 +53,7 @@
 class PageHandler;
 class SchemaHandler;
 class SecurityHandler;
+class ServiceWorkerHandler;
 class StorageHandler;
 class TracingHandler;
 }  // namespace protocol
@@ -191,8 +191,7 @@
   std::unique_ptr<protocol::PageHandler> page_handler_;
   std::unique_ptr<protocol::SchemaHandler> schema_handler_;
   std::unique_ptr<protocol::SecurityHandler> security_handler_;
-  std::unique_ptr<devtools::service_worker::ServiceWorkerHandler>
-      service_worker_handler_;
+  std::unique_ptr<protocol::ServiceWorkerHandler> service_worker_handler_;
   std::unique_ptr<protocol::StorageHandler> storage_handler_;
   std::unique_ptr<devtools::target::TargetHandler> target_handler_;
   std::unique_ptr<protocol::TracingHandler> tracing_handler_;
diff --git a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
index 2cb5b47..e7e5668 100644
--- a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
@@ -73,9 +73,13 @@
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
   }
-  void OnIncomingCapturedVideoFrame(
+  void OnIncomingCapturedBufferExt(
       std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
+      const media::VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const media::VideoFrameMetadata& additional_metadata) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc
index 4fe54c08..0d19dec 100644
--- a/content/browser/media/capture/desktop_capture_device_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -92,9 +92,13 @@
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
   }
-  void OnIncomingCapturedVideoFrame(
-      std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
+  void OnIncomingCapturedBufferExt(
+        std::unique_ptr<Buffer> buffer,
+        const media::VideoCaptureFormat& format,
+        base::TimeTicks reference_time,
+        base::TimeDelta timestamp,
+        gfx::Rect visible_rect,
+        const media::VideoFrameMetadata& additional_metadata) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/content/browser/media/capture/screen_capture_device_android_unittest.cc b/content/browser/media/capture/screen_capture_device_android_unittest.cc
index 1eaa60c..3aedf3c 100644
--- a/content/browser/media/capture/screen_capture_device_android_unittest.cc
+++ b/content/browser/media/capture/screen_capture_device_android_unittest.cc
@@ -49,9 +49,13 @@
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
   }
-  void OnIncomingCapturedVideoFrame(
+  void OnIncomingCapturedBufferExt(
       std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
+        const media::VideoCaptureFormat& format,
+        base::TimeTicks reference_time,
+        base::TimeDelta timestamp,
+        gfx::Rect visible_rect,
+        const media::VideoFrameMetadata& additional_metadata) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
index 3843028..3764d98 100644
--- a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -376,27 +376,32 @@
     DoOnIncomingCapturedBuffer();
   }
 
-  void OnIncomingCapturedVideoFrame(
-      std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
-    EXPECT_FALSE(frame->visible_rect().IsEmpty());
-    EXPECT_EQ(media::PIXEL_FORMAT_I420, frame->format());
-    double frame_rate = 0;
-    EXPECT_TRUE(
-        frame->metadata()->GetDouble(media::VideoFrameMetadata::FRAME_RATE,
-                                     &frame_rate));
-    EXPECT_EQ(kTestFramesPerSecond, frame_rate);
+  void OnIncomingCapturedBufferExt(
+      std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
+      const media::VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const media::VideoFrameMetadata& additional_metadata) override {
+    EXPECT_FALSE(visible_rect.IsEmpty());
+    EXPECT_EQ(media::PIXEL_FORMAT_I420, format.pixel_format);
+    EXPECT_EQ(kTestFramesPerSecond, format.frame_rate);
 
     // TODO(miu): We just look at the center pixel presently, because if the
     // analysis is too slow, the backlog of frames will grow without bound and
     // trouble erupts. http://crbug.com/174519
     using media::VideoFrame;
-    const gfx::Point center = frame->visible_rect().CenterPoint();
+    auto frame = VideoFrame::WrapExternalSharedMemory(
+        media::PIXEL_FORMAT_I420, format.frame_size, visible_rect,
+        format.frame_size, static_cast<uint8_t*>(buffer->data()),
+        buffer->mapped_size(), base::SharedMemory::NULLHandle(), 0u,
+        base::TimeDelta());
+    const gfx::Point center = visible_rect.CenterPoint();
     const int center_offset_y =
         (frame->stride(VideoFrame::kYPlane) * center.y()) + center.x();
     const int center_offset_uv =
         (frame->stride(VideoFrame::kUPlane) * (center.y() / 2)) +
-            (center.x() / 2);
+        (center.x() / 2);
     report_callback_.Run(
         SkColorSetRGB(frame->data(VideoFrame::kYPlane)[center_offset_y],
                       frame->data(VideoFrame::kUPlane)[center_offset_uv],
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 31fc6063..a4f25901 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -515,7 +515,7 @@
           cc::SurfaceId(frame_sink_id_, local_frame_id_),
           base::Bind(&SatisfyCallback, base::Unretained(manager)),
           base::Bind(&RequireCallback, base::Unretained(manager)), frame_size,
-          frame_device_scale_factor, frame_size_in_dip);
+          frame_device_scale_factor);
       current_surface_size_ = frame_size;
       current_scale_factor_ = frame_device_scale_factor;
     }
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 337e6d0..599f34e1 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -160,19 +160,6 @@
 
   void TearDown() override { base::RunLoop().RunUntilIdle(); }
 
-  scoped_refptr<media::VideoFrame> WrapBuffer(
-      gfx::Size dimensions,
-      uint8_t* data,
-      media::VideoPixelFormat format = media::PIXEL_FORMAT_I420) {
-    scoped_refptr<media::VideoFrame> video_frame =
-        media::VideoFrame::WrapExternalSharedMemory(
-            format, dimensions, gfx::Rect(dimensions), dimensions, data,
-            media::VideoFrame::AllocationSize(format, dimensions),
-            base::SharedMemory::NULLHandle(), 0u, base::TimeDelta());
-    EXPECT_TRUE(video_frame);
-    return video_frame;
-  }
-
   TestBrowserThreadBundle bundle_;
   std::unique_ptr<MockVideoCaptureControllerEventHandler> client_a_;
   std::unique_ptr<MockVideoCaptureControllerEventHandler> client_b_;
@@ -180,6 +167,9 @@
   std::unique_ptr<media::VideoCaptureDevice::Client> device_client_;
   MockFrameBufferPool* mock_frame_receiver_observer_;
   MockConsumerFeedbackObserver* mock_consumer_feedback_observer_;
+  const float arbitrary_frame_rate_ = 10.0f;
+  const base::TimeTicks arbitrary_reference_time_ = base::TimeTicks();
+  const base::TimeDelta arbitrary_timestamp_ = base::TimeDelta();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest);
@@ -299,19 +289,13 @@
 
   media::VideoCaptureParams session_1 = session_100;
 
-  const gfx::Size capture_resolution(444, 200);
-
-  // The device format needn't match the VideoCaptureParams (the camera can do
-  // what it wants). Pick something random.
-  media::VideoCaptureFormat device_format(
-      gfx::Size(10, 10), 25, media::PIXEL_FORMAT_RGB24);
+  media::VideoCaptureFormat device_format(gfx::Size(444, 200), 25, format);
 
   const VideoCaptureControllerID client_a_route_1(0xa1a1a1a1);
   const VideoCaptureControllerID client_a_route_2(0xa2a2a2a2);
   const VideoCaptureControllerID client_b_route_1(0xb1b1b1b1);
   const VideoCaptureControllerID client_b_route_2(0xb2b2b2b2);
 
-  // Start with two clients.
   controller_->AddClient(client_a_route_1,
                          client_a_.get(),
                          100,
@@ -332,8 +316,9 @@
   const int arbitrary_frame_feedback_id = 101;
   ASSERT_EQ(0.0, device_client_->GetBufferPoolUtilization());
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
-      device_client_->ReserveOutputBuffer(capture_resolution, format,
-                                          media::PIXEL_STORAGE_CPU,
+      device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                          device_format.pixel_format,
+                                          device_format.pixel_storage,
                                           arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
   ASSERT_EQ(1.0 / kPoolSize, device_client_->GetBufferPoolUtilization());
@@ -341,26 +326,24 @@
   {
     InSequence s;
     EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(1);
-    EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1, capture_resolution))
+    EXPECT_CALL(*client_a_,
+                DoBufferReady(client_a_route_1, device_format.frame_size))
         .Times(1);
   }
   {
     InSequence s;
     EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(1);
-    EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1, capture_resolution))
+    EXPECT_CALL(*client_b_,
+                DoBufferReady(client_b_route_1, device_format.frame_size))
         .Times(1);
   }
   {
     InSequence s;
     EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1);
-    EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2, capture_resolution))
+    EXPECT_CALL(*client_a_,
+                DoBufferReady(client_a_route_2, device_format.frame_size))
         .Times(1);
   }
-  scoped_refptr<media::VideoFrame> video_frame = WrapBuffer(
-      capture_resolution, static_cast<uint8_t*>(buffer->data()), format);
-  ASSERT_TRUE(video_frame);
-  ASSERT_FALSE(video_frame->metadata()->HasKey(
-      media::VideoFrameMetadata::RESOURCE_UTILIZATION));
   client_a_->resource_utilization_ = 0.5;
   client_b_->resource_utilization_ = -1.0;
   {
@@ -376,9 +359,10 @@
         .Times(1);
   }
 
-  video_frame->metadata()->SetTimeTicks(
-      media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+  device_client_->OnIncomingCapturedBuffer(std::move(buffer),
+    device_format,
+    arbitrary_reference_time_,
+    arbitrary_timestamp_);
 
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
@@ -391,17 +375,14 @@
   // delay. This shouldn't affect anything.
   const int arbitrary_frame_feedback_id_2 = 102;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer2 =
-      device_client_->ReserveOutputBuffer(capture_resolution, format,
-                                          media::PIXEL_STORAGE_CPU,
+      device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                          device_format.pixel_format,
+                                          device_format.pixel_storage,
                                           arbitrary_frame_feedback_id_2);
   ASSERT_TRUE(buffer2.get());
   memset(buffer2->data(), buffer_no++, buffer2->mapped_size());
-  video_frame = WrapBuffer(capture_resolution,
-                           static_cast<uint8_t*>(buffer2->data()), format);
   client_a_->resource_utilization_ = 0.5;
   client_b_->resource_utilization_ = 3.14;
-  video_frame->metadata()->SetTimeTicks(
-      media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
   // Expect VideoCaptureController to call the load observer with a
   // resource utilization of 3.14 (the largest of all reported values).
   {
@@ -418,25 +399,31 @@
         .Times(1);
   }
 
-  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer2), video_frame);
+  device_client_->OnIncomingCapturedBuffer(std::move(buffer2),
+    device_format,
+    arbitrary_reference_time_,
+    arbitrary_timestamp_);
 
   // The buffer should be delivered to the clients in any order.
   {
     InSequence s;
     EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(1);
-    EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1, capture_resolution))
+    EXPECT_CALL(*client_a_,
+                DoBufferReady(client_a_route_1, device_format.frame_size))
         .Times(1);
   }
   {
     InSequence s;
     EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(1);
-    EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1, capture_resolution))
+    EXPECT_CALL(*client_b_,
+                DoBufferReady(client_b_route_1, device_format.frame_size))
         .Times(1);
   }
   {
     InSequence s;
     EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1);
-    EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2, capture_resolution))
+    EXPECT_CALL(*client_a_,
+                DoBufferReady(client_a_route_2, device_format.frame_size))
         .Times(1);
   }
   base::RunLoop().RunUntilIdle();
@@ -456,42 +443,45 @@
   for (int i = 0; i < kPoolSize; i++) {
     const int arbitrary_frame_feedback_id = 200 + i;
     std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer =
-        device_client_->ReserveOutputBuffer(capture_resolution, format,
-                                            media::PIXEL_STORAGE_CPU,
+        device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                            device_format.pixel_format,
+                                            device_format.pixel_storage,
                                             arbitrary_frame_feedback_id);
     ASSERT_TRUE(buffer.get());
     memset(buffer->data(), buffer_no++, buffer->mapped_size());
-    video_frame = WrapBuffer(capture_resolution,
-                             static_cast<uint8_t*>(buffer->data()), format);
-    ASSERT_TRUE(video_frame);
-    video_frame->metadata()->SetTimeTicks(
-        media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-    device_client_->OnIncomingCapturedVideoFrame(std::move(buffer),
-                                                 video_frame);
+    device_client_->OnIncomingCapturedBuffer(std::move(buffer),
+      device_format,
+      arbitrary_reference_time_,
+      arbitrary_timestamp_);
   }
   // ReserveOutputBuffer ought to fail now, because the pool is depleted.
   ASSERT_FALSE(device_client_
-                   ->ReserveOutputBuffer(capture_resolution, format,
-                                         media::PIXEL_STORAGE_CPU,
+                   ->ReserveOutputBuffer(device_format.frame_size,
+                                         device_format.pixel_format,
+                                         device_format.pixel_storage,
                                          arbitrary_frame_feedback_id)
                    .get());
 
   // The new client needs to be notified of the creation of |kPoolSize| buffers;
   // the old clients only |kPoolSize - 2|.
   EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_2)).Times(kPoolSize);
-  EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2, capture_resolution))
+  EXPECT_CALL(*client_b_,
+              DoBufferReady(client_b_route_2, device_format.frame_size))
       .Times(kPoolSize);
   EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1))
       .Times(kPoolSize - 2);
-  EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1, capture_resolution))
+  EXPECT_CALL(*client_a_,
+              DoBufferReady(client_a_route_1, device_format.frame_size))
       .Times(kPoolSize);
   EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2))
       .Times(kPoolSize - 2);
-  EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2, capture_resolution))
+  EXPECT_CALL(*client_a_,
+              DoBufferReady(client_a_route_2, device_format.frame_size))
       .Times(kPoolSize);
   EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1))
       .Times(kPoolSize - 2);
-  EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1, capture_resolution))
+  EXPECT_CALL(*client_b_,
+              DoBufferReady(client_b_route_1, device_format.frame_size))
       .Times(kPoolSize);
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
@@ -505,21 +495,21 @@
   controller_->StopSession(300);
   // Queue up another buffer.
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer3 =
-      device_client_->ReserveOutputBuffer(capture_resolution, format,
-                                          media::PIXEL_STORAGE_CPU,
+      device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                          device_format.pixel_format,
+                                          device_format.pixel_storage,
                                           arbitrary_frame_feedback_id);
   ASSERT_TRUE(buffer3.get());
   memset(buffer3->data(), buffer_no++, buffer3->mapped_size());
-  video_frame = WrapBuffer(capture_resolution,
-                           static_cast<uint8_t*>(buffer3->data()), format);
-  ASSERT_TRUE(video_frame);
-  video_frame->metadata()->SetTimeTicks(
-      media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer3), video_frame);
+  device_client_->OnIncomingCapturedBuffer(std::move(buffer3),
+    device_format,
+    arbitrary_reference_time_,
+    arbitrary_timestamp_);
 
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer4 =
-      device_client_->ReserveOutputBuffer(capture_resolution, format,
-                                          media::PIXEL_STORAGE_CPU,
+      device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                          device_format.pixel_format,
+                                          device_format.pixel_storage,
                                           arbitrary_frame_feedback_id);
   {
     // Kill A2 via session close (posts a task to disconnect, but A2 must not
@@ -529,15 +519,14 @@
   }
   ASSERT_TRUE(buffer4.get());
   memset(buffer4->data(), buffer_no++, buffer4->mapped_size());
-  video_frame = WrapBuffer(capture_resolution,
-                           static_cast<uint8_t*>(buffer4->data()), format);
-  ASSERT_TRUE(video_frame);
-  video_frame->metadata()->SetTimeTicks(
-      media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer4), video_frame);
+  device_client_->OnIncomingCapturedBuffer(std::move(buffer4),
+    device_format,
+    arbitrary_reference_time_,
+    arbitrary_timestamp_);
   // B2 is the only client left, and is the only one that should
   // get the buffer.
-  EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2, capture_resolution))
+  EXPECT_CALL(*client_b_,
+              DoBufferReady(client_b_route_2, device_format.frame_size))
       .Times(2);
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
@@ -576,18 +565,19 @@
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_b_.get());
 
-  const int arbitrary_frame_feedback_id = 101;
+  media::VideoCaptureFormat device_format(
+      capture_resolution, arbitrary_frame_rate_, media::PIXEL_FORMAT_I420,
+      media::PIXEL_STORAGE_CPU);
+    const int arbitrary_frame_feedback_id = 101;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
-      device_client_->ReserveOutputBuffer(
-          capture_resolution, media::PIXEL_FORMAT_I420,
-          media::PIXEL_STORAGE_CPU, arbitrary_frame_feedback_id));
-  ASSERT_TRUE(buffer.get());
-  scoped_refptr<media::VideoFrame> video_frame =
-      WrapBuffer(capture_resolution, static_cast<uint8_t*>(buffer->data()));
-  ASSERT_TRUE(video_frame);
-  video_frame->metadata()->SetTimeTicks(
-      media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+      device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                          device_format.pixel_format,
+                                          device_format.pixel_storage,
+                                          arbitrary_frame_feedback_id));
+  device_client_->OnIncomingCapturedBuffer(std::move(buffer),
+    device_format,
+    arbitrary_reference_time_,
+    arbitrary_timestamp_);
 
   base::RunLoop().RunUntilIdle();
 }
@@ -605,8 +595,6 @@
 
   // Start with one client.
   controller_->AddClient(route_id, client_a_.get(), 100, session_100);
-  media::VideoCaptureFormat device_format(
-      gfx::Size(10, 10), 25, media::PIXEL_FORMAT_ARGB);
 
   // Start the device. Then, before the first buffer, signal an error and
   // deliver the buffer. The error should be propagated to clients; the buffer
@@ -614,21 +602,21 @@
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
 
-  const gfx::Size dims(320, 240);
+  media::VideoCaptureFormat device_format(
+      gfx::Size(10, 10), arbitrary_frame_rate_, media::PIXEL_FORMAT_I420);
   const int arbitrary_frame_feedback_id = 101;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
-      device_client_->ReserveOutputBuffer(dims, media::PIXEL_FORMAT_I420,
-                                          media::PIXEL_STORAGE_CPU,
+      device_client_->ReserveOutputBuffer(device_format.frame_size,
+                                          device_format.pixel_format,
+                                          device_format.pixel_storage,
                                           arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
 
-  scoped_refptr<media::VideoFrame> video_frame =
-      WrapBuffer(dims, static_cast<uint8_t*>(buffer->data()));
-  ASSERT_TRUE(video_frame);
   device_client_->OnError(FROM_HERE, "Test Error");
-  video_frame->metadata()->SetTimeTicks(
-      media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+  device_client_->OnIncomingCapturedBuffer(std::move(buffer),
+    device_format,
+    arbitrary_reference_time_,
+    arbitrary_timestamp_);
 
   EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
   base::RunLoop().RunUntilIdle();
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 872234e..a4d142d 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -80,8 +80,6 @@
   switch (name) {
     case WebLocalizedString::AXAMPMFieldText:
       return IDS_AX_AM_PM_FIELD_TEXT;
-    case WebLocalizedString::AXButtonActionVerb:
-      return IDS_AX_BUTTON_ACTION_VERB;
     case WebLocalizedString::AXCalendarShowMonthSelector:
       return IDS_AX_CALENDAR_SHOW_MONTH_SELECTOR;
     case WebLocalizedString::AXCalendarShowNextMonth:
@@ -90,20 +88,14 @@
       return IDS_AX_CALENDAR_SHOW_PREVIOUS_MONTH;
     case WebLocalizedString::AXCalendarWeekDescription:
       return IDS_AX_CALENDAR_WEEK_DESCRIPTION;
-    case WebLocalizedString::AXCheckedCheckBoxActionVerb:
-      return IDS_AX_CHECKED_CHECK_BOX_ACTION_VERB;
     case WebLocalizedString::AXDayOfMonthFieldText:
       return IDS_AX_DAY_OF_MONTH_FIELD_TEXT;
-    case WebLocalizedString::AXDefaultActionVerb:
-      return IDS_AX_DEFAULT_ACTION_VERB;
     case WebLocalizedString::AXHeadingText:
       return IDS_AX_ROLE_HEADING;
     case WebLocalizedString::AXHourFieldText:
       return IDS_AX_HOUR_FIELD_TEXT;
     case WebLocalizedString::AXImageMapText:
       return IDS_AX_ROLE_IMAGE_MAP;
-    case WebLocalizedString::AXLinkActionVerb:
-      return IDS_AX_LINK_ACTION_VERB;
     case WebLocalizedString::AXLinkText:
       return IDS_AX_ROLE_LINK;
     case WebLocalizedString::AXListMarkerText:
@@ -192,16 +184,8 @@
       return IDS_AX_MINUTE_FIELD_TEXT;
     case WebLocalizedString::AXMonthFieldText:
       return IDS_AX_MONTH_FIELD_TEXT;
-    case WebLocalizedString::AXPopUpButtonActionVerb:
-      return IDS_AX_POP_UP_BUTTON_ACTION_VERB;
-    case WebLocalizedString::AXRadioButtonActionVerb:
-      return IDS_AX_RADIO_BUTTON_ACTION_VERB;
     case WebLocalizedString::AXSecondFieldText:
       return IDS_AX_SECOND_FIELD_TEXT;
-    case WebLocalizedString::AXTextFieldActionVerb:
-      return IDS_AX_TEXT_FIELD_ACTION_VERB;
-    case WebLocalizedString::AXUncheckedCheckBoxActionVerb:
-      return IDS_AX_UNCHECKED_CHECK_BOX_ACTION_VERB;
     case WebLocalizedString::AXWebAreaText:
       return IDS_AX_ROLE_WEB_AREA;
     case WebLocalizedString::AXWeekOfYearFieldText:
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.cc b/content/renderer/accessibility/blink_ax_enum_conversion.cc
index 55479b4..1ebdb18 100644
--- a/content/renderer/accessibility/blink_ax_enum_conversion.cc
+++ b/content/renderer/accessibility/blink_ax_enum_conversion.cc
@@ -409,6 +409,32 @@
   }
 }
 
+ui::AXSupportedAction AXSupportedActionFromBlink(
+    blink::WebAXSupportedAction supported_action) {
+  switch (supported_action) {
+    case blink::WebAXSupportedAction::None:
+      return ui::AX_SUPPORTED_ACTION_NONE;
+    case blink::WebAXSupportedAction::Activate:
+      return ui::AX_SUPPORTED_ACTION_ACTIVATE;
+    case blink::WebAXSupportedAction::Check:
+      return ui::AX_SUPPORTED_ACTION_CHECK;
+    case blink::WebAXSupportedAction::Click:
+      return ui::AX_SUPPORTED_ACTION_CLICK;
+    case blink::WebAXSupportedAction::Jump:
+      return ui::AX_SUPPORTED_ACTION_JUMP;
+    case blink::WebAXSupportedAction::Open:
+      return ui::AX_SUPPORTED_ACTION_OPEN;
+    case blink::WebAXSupportedAction::Press:
+      return ui::AX_SUPPORTED_ACTION_PRESS;
+    case blink::WebAXSupportedAction::Select:
+      return ui::AX_SUPPORTED_ACTION_SELECT;
+    case blink::WebAXSupportedAction::Uncheck:
+      return ui::AX_SUPPORTED_ACTION_UNCHECK;
+  }
+  NOTREACHED();
+  return ui::AX_SUPPORTED_ACTION_NONE;
+}
+
 ui::AXMarkerType AXMarkerTypeFromBlink(blink::WebAXMarkerType marker_type) {
   switch (marker_type) {
     case blink::WebAXMarkerTypeSpelling:
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.h b/content/renderer/accessibility/blink_ax_enum_conversion.h
index bf81010..e324989 100644
--- a/content/renderer/accessibility/blink_ax_enum_conversion.h
+++ b/content/renderer/accessibility/blink_ax_enum_conversion.h
@@ -24,6 +24,9 @@
 // in AXNodeData instead.)
 uint32_t AXStateFromBlink(const blink::WebAXObject& o);
 
+ui::AXSupportedAction AXSupportedActionFromBlink(
+    blink::WebAXSupportedAction supported_action);
+
 ui::AXMarkerType AXMarkerTypeFromBlink(blink::WebAXMarkerType marker_type);
 
 ui::AXTextDirection AXTextDirectionFromBlink(
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 5fcf702..bd9b01c 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -427,8 +427,9 @@
     dst->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY, src.accessKey().utf8());
   }
 
-  if (src.actionVerb().length()) {
-    dst->AddStringAttribute(ui::AX_ATTR_ACTION, src.actionVerb().utf8());
+  if (src.action() != blink::WebAXSupportedAction::None) {
+    dst->AddIntAttribute(ui::AX_ATTR_ACTION,
+                         AXSupportedActionFromBlink(src.action()));
   }
 
   if (src.ariaAutoComplete().length()) {
diff --git a/content/test/data/accessibility/html/action-verbs-expected-blink.txt b/content/test/data/accessibility/html/action-verbs-expected-blink.txt
index ac62ce9..c588e67 100644
--- a/content/test/data/accessibility/html/action-verbs-expected-blink.txt
+++ b/content/test/data/accessibility/html/action-verbs-expected-blink.txt
@@ -5,18 +5,18 @@
 ++heading name='Heading'
 ++++staticText name='Heading'
 ++++++inlineTextBox name='Heading'
-++button action='press' name='Button'
-++link action='jump' name='Link'
-++++staticText action='click' name='Link'
+++button name='Button' action=6
+++link name='Link' action=4
+++++staticText name='Link' action=3
 ++++++inlineTextBox name='Link'
 ++textField
-++checkBox action='check'
-++checkBox action='uncheck'
-++radioButton action='select'
-++switch action='check' name='ARIA Switch'
-++popUpButton action='open'
+++checkBox action=8
+++checkBox action=2
+++radioButton action=7
+++switch name='ARIA Switch' action=8
+++popUpButton action=5
 ++++menuListPopup
-++++++menuListOption action='click' name='Pop-up button'
-++div action='click'
-++++staticText action='click' name='Div with click handler'
-++++++inlineTextBox name='Div with click handler'
\ No newline at end of file
+++++++menuListOption name='Pop-up button' action=3
+++div action=3
+++++staticText name='Div with click handler' action=3
+++++++inlineTextBox name='Div with click handler'
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index d31eac5c..16be527 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -51,7 +51,7 @@
     self.Skip('WebglExtension_WEBGL_compressed_texture_atc',
         ['win', 'mac', 'linux'])
     self.Skip('WebglExtension_WEBGL_compressed_texture_etc1',
-        ['mac', 'linux'])
+        ['win', 'mac', 'linux'])
     self.Skip('WebglExtension_WEBGL_compressed_texture_pvrtc',
         ['win', 'mac', 'linux'])
     self.Skip('WebglExtension_WEBGL_compressed_texture_s3tc_srgb',
@@ -62,8 +62,6 @@
         ['win', 'd3d9'])
     self.Fail('WebglExtension_EXT_sRGB',
         ['win', 'd3d9'])
-    self.Fail('WebglExtension_WEBGL_compressed_texture_etc1',
-        ['win', 'd3d9'])
 
     self.Fail('WebglExtension_WEBGL_depth_texture',
         ['win', 'amd', 'd3d9'])
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index c817e12..49b6fca 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -1037,7 +1037,11 @@
     validators_.g_l_state.AddValue(GL_TEXTURE_BINDING_EXTERNAL_OES);
   }
 
-  if (extensions.Contains("GL_OES_compressed_ETC1_RGB8_texture")) {
+  // TODO(kainino): If we add a way to query whether ANGLE is exposing
+  // native support for ETC1 textures, require that here. Otherwise, we could
+  // co-opt the native-ETC2-support query discussed below.
+  if (extensions.Contains("GL_OES_compressed_ETC1_RGB8_texture") &&
+      !gl_version_info_->is_angle) {
     AddExtensionString("GL_OES_compressed_ETC1_RGB8_texture");
     feature_flags_.oes_compressed_etc1_rgb8_texture = true;
     validators_.compressed_texture_format.AddValue(GL_ETC1_RGB8_OES);
diff --git a/media/capture/content/thread_safe_capture_oracle.cc b/media/capture/content/thread_safe_capture_oracle.cc
index 9fc8f93..ad869b7d 100644
--- a/media/capture/content/thread_safe_capture_oracle.cc
+++ b/media/capture/content/thread_safe_capture_oracle.cc
@@ -211,26 +211,33 @@
 
   base::AutoLock guard(lock_);
 
-  if (oracle_.CompleteCapture(frame_number, success, &reference_time)) {
-    TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded",
-                         TRACE_EVENT_SCOPE_THREAD);
+  if (!oracle_.CompleteCapture(frame_number, success, &reference_time))
+    return;
 
-    if (!client_)
-      return;  // Capture is stopped.
+  TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded",
+                       TRACE_EVENT_SCOPE_THREAD);
 
-    frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE,
-                                 params_.requested_format.frame_rate);
-    frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME,
-                                    capture_begin_time);
-    frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME,
-                                    base::TimeTicks::Now());
-    frame->metadata()->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION,
-                                    estimated_frame_duration);
-    frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
-                                    reference_time);
+  if (!client_)
+    return;  // Capture is stopped.
 
-    client_->OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
-  }
+  frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE,
+                               params_.requested_format.frame_rate);
+  frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME,
+                                  capture_begin_time);
+  frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME,
+                                  base::TimeTicks::Now());
+  frame->metadata()->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION,
+                                  estimated_frame_duration);
+  frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
+                                  reference_time);
+
+  DCHECK(frame->IsMappable());
+  media::VideoCaptureFormat format(frame->coded_size(),
+                                   params_.requested_format.frame_rate,
+                                   frame->format(), media::PIXEL_STORAGE_CPU);
+  client_->OnIncomingCapturedBufferExt(
+      std::move(buffer), format, reference_time, frame->timestamp(),
+      frame->visible_rect(), *frame->metadata());
 }
 
 void ThreadSafeCaptureOracle::OnConsumerReportingUtilization(
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index c9bbae9..21563c0 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -101,11 +101,13 @@
                                 base::TimeDelta timestamp) override {
     frame_cb_.Run(format);
   }
-  void OnIncomingCapturedVideoFrame(
+  void OnIncomingCapturedBufferExt(
       std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
-    VideoCaptureFormat format(frame->natural_size(), 30.0,
-                              PIXEL_FORMAT_I420);
+      const VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const VideoFrameMetadata& additional_metadata) override {
     frame_cb_.Run(format);
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc
index ad67ae5..3febf4456 100644
--- a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc
+++ b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc
@@ -170,9 +170,13 @@
     DoOnIncomingCapturedBuffer();
   }
   MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
-  void OnIncomingCapturedVideoFrame(
+  void OnIncomingCapturedBufferExt(
       std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
+      const VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const VideoFrameMetadata& additional_metadata) override {
     DoOnIncomingCapturedVideoFrame();
   }
   MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h
index e561267..0ac1c3b 100644
--- a/media/capture/video/video_capture_device.h
+++ b/media/capture/video/video_capture_device.h
@@ -147,28 +147,30 @@
         VideoPixelStorage storage,
         int frame_feedback_id) = 0;
 
-    // Captured new video data, held in |frame| or |buffer|, respectively for
-    // OnIncomingCapturedVideoFrame() and  OnIncomingCapturedBuffer().
-    //
-    // In both cases, as the frame is backed by a reservation returned by
-    // ReserveOutputBuffer(), delivery is guaranteed and will require no
-    // additional copies in the browser process.
+    // Provides VCD::Client with a populated Buffer containing the content of
+    // the next video frame. The |buffer| must originate from an earlier call to
+    // ReserveOutputBuffer().
     // See OnIncomingCapturedData for details of |reference_time| and
     // |timestamp|.
-    // TODO(chfremer): Consider removing one of the two in order to simplify the
-    // interface.
     virtual void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
                                           const VideoCaptureFormat& format,
                                           base::TimeTicks reference_time,
                                           base::TimeDelta timestamp) = 0;
-    virtual void OnIncomingCapturedVideoFrame(
+
+    // Extended version of OnIncomingCapturedBuffer() allowing clients to
+    // pass a custom |visible_rect| and |additional_metadata|.
+    virtual void OnIncomingCapturedBufferExt(
         std::unique_ptr<Buffer> buffer,
-        scoped_refptr<VideoFrame> frame) = 0;
+        const VideoCaptureFormat& format,
+        base::TimeTicks reference_time,
+        base::TimeDelta timestamp,
+        gfx::Rect visible_rect,
+        const VideoFrameMetadata& additional_metadata) = 0;
 
     // Attempts to reserve the same Buffer provided in the last call to one of
-    // the OnIncomingCapturedXXX() methods. This will fail if the content of the
-    // Buffer has not been preserved, or if the |dimensions|, |format|, or
-    // |storage| disagree with how it was reserved via ReserveOutputBuffer().
+    // the OnIncomingCapturedBufferXXX() methods. This will fail if the content
+    // of the Buffer has not been preserved, or if the |dimensions|, |format|,
+    // or |storage| disagree with how it was reserved via ReserveOutputBuffer().
     // When this operation fails, nullptr will be returned.
     virtual std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
         const gfx::Size& dimensions,
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 3321633..c7a92be 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -21,6 +21,7 @@
 #include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "media/capture/video/video_frame_receiver.h"
 #include "media/capture/video_capture_types.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "third_party/libyuv/include/libyuv.h"
 
 using media::VideoCaptureFormat;
@@ -295,32 +296,46 @@
     const VideoCaptureFormat& format,
     base::TimeTicks reference_time,
     base::TimeDelta timestamp) {
-  DCHECK(IsFormatSupported(format.pixel_format));
-  DCHECK_EQ(media::PIXEL_STORAGE_CPU, format.pixel_storage);
+  OnIncomingCapturedBufferExt(std::move(buffer), format, reference_time,
+                              timestamp, gfx::Rect(format.frame_size),
+                              VideoFrameMetadata());
+}
 
-  scoped_refptr<VideoFrame> frame;
-  if (buffer->IsBackedByVideoFrame()) {
-    frame = buffer->GetVideoFrame();
-    frame->set_timestamp(timestamp);
-  } else {
-    frame = VideoFrame::WrapExternalSharedMemory(
-        format.pixel_format, format.frame_size, gfx::Rect(format.frame_size),
-        format.frame_size, reinterpret_cast<uint8_t*>(buffer->data()),
-        VideoFrame::AllocationSize(format.pixel_format, format.frame_size),
-        base::SharedMemory::NULLHandle(), 0u, timestamp);
-  }
-  if (!frame)
-    return;
+void VideoCaptureDeviceClient::OnIncomingCapturedBufferExt(
+    std::unique_ptr<Buffer> buffer,
+    const VideoCaptureFormat& format,
+    base::TimeTicks reference_time,
+    base::TimeDelta timestamp,
+    gfx::Rect visible_rect,
+    const VideoFrameMetadata& additional_metadata) {
+  const int buffer_id = buffer->id();
+
+  auto buffer_mojo_handle = buffer_pool_->GetHandleForTransit(buffer_id);
+  base::SharedMemoryHandle memory_handle;
+  size_t memory_size = 0;
+  bool read_only_flag = false;
+  const MojoResult unwrap_result_code = mojo::UnwrapSharedMemoryHandle(
+      std::move(buffer_mojo_handle), &memory_handle, &memory_size,
+      &read_only_flag);
+  DCHECK_EQ(MOJO_RESULT_OK, unwrap_result_code);
+
+  scoped_refptr<media::VideoFrame> frame =
+      media::VideoFrame::WrapExternalSharedMemory(
+          format.pixel_format,                    // format
+          format.frame_size,                      // coded_size
+          visible_rect,                           // visible_rect
+          format.frame_size,                      // natural_size
+          static_cast<uint8_t*>(buffer->data()),  // data
+          buffer->mapped_size(),                  // data_size
+          memory_handle,                          // handle
+          0,                                      // shared_memory_offset
+          timestamp);                             // timestamp
+  frame->metadata()->MergeMetadataFrom(&additional_metadata);
   frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
                                format.frame_rate);
   frame->metadata()->SetTimeTicks(media::VideoFrameMetadata::REFERENCE_TIME,
                                   reference_time);
-  OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
-}
 
-void VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
-    std::unique_ptr<Buffer> buffer,
-    scoped_refptr<VideoFrame> frame) {
   receiver_->OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
 }
 
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h
index 0c85539..adac1bc1 100644
--- a/media/capture/video/video_capture_device_client.h
+++ b/media/capture/video/video_capture_device_client.h
@@ -65,9 +65,13 @@
                                 const VideoCaptureFormat& format,
                                 base::TimeTicks reference_time,
                                 base::TimeDelta timestamp) override;
-  void OnIncomingCapturedVideoFrame(
+  void OnIncomingCapturedBufferExt(
       std::unique_ptr<Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override;
+      const VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const VideoFrameMetadata& additional_metadata) override;
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc
index b3289d2..ca6a4a1 100644
--- a/media/capture/video/video_capture_device_unittest.cc
+++ b/media/capture/video/video_capture_device_unittest.cc
@@ -135,8 +135,13 @@
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
   }
-  void OnIncomingCapturedVideoFrame(std::unique_ptr<Buffer> buffer,
-                                    scoped_refptr<VideoFrame> frame) override {
+  void OnIncomingCapturedBufferExt(
+      std::unique_ptr<Buffer> buffer,
+      const VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const VideoFrameMetadata& additional_metadata) override {
     DoOnIncomingCapturedVideoFrame();
   }
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
diff --git a/net/BUILD.gn b/net/BUILD.gn
index f87cacb..9a22c35 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1104,6 +1104,7 @@
   generate_jni("net_test_jni_headers") {
     sources = [
       "android/javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java",
+      "android/javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java",
       "test/android/javatests/src/org/chromium/net/test/DummySpnegoAuthenticator.java",
       "test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java",
     ]
diff --git a/net/android/BUILD.gn b/net/android/BUILD.gn
index 74142bd..1ff8b2c 100644
--- a/net/android/BUILD.gn
+++ b/net/android/BUILD.gn
@@ -112,6 +112,7 @@
   testonly = true
   java_files = [
     "javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java",
+    "javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java",
     "javatests/src/org/chromium/net/AndroidProxySelectorTest.java",
     "javatests/src/org/chromium/net/NetErrorsTest.java",
     "javatests/src/org/chromium/net/NetworkChangeNotifierTest.java",
diff --git a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
index 1656e2c..d33321c 100644
--- a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
+++ b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
@@ -17,6 +17,7 @@
 import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.security.KeyChain;
+import android.security.NetworkSecurityPolicy;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -243,4 +244,21 @@
         }
         return "";
     }
+
+    /**
+     * Returns true if cleartext traffic to |host| is allowed by the current app. Always true on L
+     * and older.
+     */
+    @TargetApi(Build.VERSION_CODES.N)
+    @CalledByNative
+    private static boolean isCleartextPermitted(String host) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            NetworkSecurityPolicy policy = NetworkSecurityPolicy.getInstance();
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                return policy.isCleartextTrafficPermitted(host);
+            }
+            return policy.isCleartextTrafficPermitted();
+        }
+        return true;
+    }
 }
diff --git a/net/android/javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java b/net/android/javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java
new file mode 100644
index 0000000..21f39b8
--- /dev/null
+++ b/net/android/javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java
@@ -0,0 +1,32 @@
+// 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.net;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.security.NetworkSecurityPolicy;
+
+import org.chromium.base.annotations.CalledByNative;
+
+import java.lang.reflect.Method;
+
+/**
+ * Utility functions for testing features implemented in AndroidNetworkLibrary.
+ */
+public class AndroidNetworkLibraryTestUtil {
+    /**
+     * Helper for tests that simulates an app disallowing cleartext traffic entirely on M and newer.
+     */
+    @TargetApi(Build.VERSION_CODES.M)
+    @CalledByNative
+    private static void setUpSecurityPolicyForTesting(boolean cleartextPermitted) throws Exception {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            Method setCleartextTrafficPermitted = NetworkSecurityPolicy.class.getDeclaredMethod(
+                    "setCleartextTrafficPermitted", boolean.class);
+            setCleartextTrafficPermitted.invoke(
+                    NetworkSecurityPolicy.getInstance(), cleartextPermitted);
+        }
+    }
+}
\ No newline at end of file
diff --git a/net/android/network_library.cc b/net/android/network_library.cc
index 6612381..34a9194 100644
--- a/net/android/network_library.cc
+++ b/net/android/network_library.cc
@@ -79,6 +79,12 @@
   return ret;
 }
 
+bool IsCleartextPermitted(const std::string& host) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> host_string = ConvertUTF8ToJavaString(env, host);
+  return Java_AndroidNetworkLibrary_isCleartextPermitted(env, host_string);
+}
+
 bool HaveOnlyLoopbackAddresses() {
   JNIEnv* env = AttachCurrentThread();
   return Java_AndroidNetworkLibrary_haveOnlyLoopbackAddresses(env);
diff --git a/net/android/network_library.h b/net/android/network_library.h
index 548d43a..60d9abd 100644
--- a/net/android/network_library.h
+++ b/net/android/network_library.h
@@ -45,6 +45,10 @@
                   const uint8_t* private_key,
                   size_t private_len);
 
+// Returns true if cleartext traffic to |host| is allowed by the app. Always
+// true on L and older.
+bool IsCleartextPermitted(const std::string& host);
+
 // Returns true if it can determine that only loopback addresses are configured.
 // i.e. if only 127.0.0.1 and ::1 are routable.
 // Also returns false if it cannot determine this.
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 0a880f8..27e8005 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -116,6 +116,10 @@
 // heuristics that point to the possiblility of a cross-site scripting attack.
 NET_ERROR(BLOCKED_BY_XSS_AUDITOR, -28)
 
+// The request was blocked by system policy disallowing some or all cleartext
+// requests. Used for NetworkSecurityPolicy on Android.
+NET_ERROR(CLEARTEXT_NOT_PERMITTED, -29)
+
 // A connection was closed (corresponding to a TCP FIN).
 NET_ERROR(CONNECTION_CLOSED, -100)
 
diff --git a/net/quic/core/crypto/curve25519_key_exchange.cc b/net/quic/core/crypto/curve25519_key_exchange.cc
index 7bb7e30..d2e0082 100644
--- a/net/quic/core/crypto/curve25519_key_exchange.cc
+++ b/net/quic/core/crypto/curve25519_key_exchange.cc
@@ -4,9 +4,11 @@
 
 #include "net/quic/core/crypto/curve25519_key_exchange.h"
 
+#include <cstdint>
+
 #include "base/logging.h"
-#include "crypto/curve25519.h"
 #include "net/quic/core/crypto/quic_random.h"
+#include "third_party/boringssl/src/include/openssl/curve25519.h"
 
 using base::StringPiece;
 using std::string;
@@ -20,35 +22,28 @@
 // static
 Curve25519KeyExchange* Curve25519KeyExchange::New(StringPiece private_key) {
   Curve25519KeyExchange* ka;
-  // We don't want to #include the NaCl headers in the public header file, so
-  // we use literals for the sizes of private_key_ and public_key_. Here we
-  // assert that those values are equal to the values from the NaCl header.
-  static_assert(sizeof(ka->private_key_) == crypto::curve25519::kScalarBytes,
+  // We don't want to #include the BoringSSL headers in the public header file,
+  // so we use literals for the sizes of private_key_ and public_key_. Here we
+  // assert that those values are equal to the values from the BoringSSL
+  static_assert(sizeof(ka->private_key_) == X25519_PRIVATE_KEY_LEN,
                 "header out of sync");
-  static_assert(sizeof(ka->public_key_) == crypto::curve25519::kBytes,
+  static_assert(sizeof(ka->public_key_) == X25519_PUBLIC_VALUE_LEN,
                 "header out of sync");
 
-  if (private_key.size() != crypto::curve25519::kScalarBytes) {
+  if (private_key.size() != X25519_PRIVATE_KEY_LEN) {
     return nullptr;
   }
 
   ka = new Curve25519KeyExchange();
-  memcpy(ka->private_key_, private_key.data(),
-         crypto::curve25519::kScalarBytes);
-  crypto::curve25519::ScalarBaseMult(ka->private_key_, ka->public_key_);
+  memcpy(ka->private_key_, private_key.data(), X25519_PRIVATE_KEY_LEN);
+  X25519_public_from_private(ka->public_key_, ka->private_key_);
   return ka;
 }
 
 // static
 string Curve25519KeyExchange::NewPrivateKey(QuicRandom* rand) {
-  uint8_t private_key[crypto::curve25519::kScalarBytes];
+  uint8_t private_key[X25519_PRIVATE_KEY_LEN];
   rand->RandBytes(private_key, sizeof(private_key));
-
-  // This makes |private_key| a valid scalar, as specified on
-  // http://cr.yp.to/ecdh.html
-  private_key[0] &= 248;
-  private_key[31] &= 127;
-  private_key[31] |= 64;
   return string(reinterpret_cast<char*>(private_key), sizeof(private_key));
 }
 
@@ -59,18 +54,17 @@
 
 bool Curve25519KeyExchange::CalculateSharedKey(StringPiece peer_public_value,
                                                string* out_result) const {
-  if (peer_public_value.size() != crypto::curve25519::kBytes) {
+  if (peer_public_value.size() != X25519_PUBLIC_VALUE_LEN) {
     return false;
   }
 
-  uint8_t result[crypto::curve25519::kBytes];
-  if (!crypto::curve25519::ScalarMult(
-          private_key_,
-          reinterpret_cast<const uint8_t*>(peer_public_value.data()), result)) {
+  uint8_t result[X25519_PUBLIC_VALUE_LEN];
+  if (!X25519(result, private_key_,
+              reinterpret_cast<const uint8_t*>(peer_public_value.data()))) {
     return false;
   }
+
   out_result->assign(reinterpret_cast<char*>(result), sizeof(result));
-
   return true;
 }
 
diff --git a/net/quic/core/crypto/curve25519_key_exchange.h b/net/quic/core/crypto/curve25519_key_exchange.h
index ab79294..0515e8d7 100644
--- a/net/quic/core/crypto/curve25519_key_exchange.h
+++ b/net/quic/core/crypto/curve25519_key_exchange.h
@@ -5,8 +5,7 @@
 #ifndef NET_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
 #define NET_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
 
-#include <stdint.h>
-
+#include <cstdint>
 #include <string>
 
 #include "base/compiler_specific.h"
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 42bcc899..9388ceb 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -42,7 +42,8 @@
       sdch_manager_(nullptr),
       network_quality_estimator_(nullptr),
       url_requests_(new std::set<const URLRequest*>),
-      enable_brotli_(false) {
+      enable_brotli_(false),
+      check_cleartext_permitted_(false) {
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       this, "URLRequestContext", base::ThreadTaskRunnerHandle::Get());
 }
@@ -76,6 +77,7 @@
   set_http_user_agent_settings(other->http_user_agent_settings_);
   set_network_quality_estimator(other->network_quality_estimator_);
   set_enable_brotli(other->enable_brotli_);
+  set_check_cleartext_permitted(other->check_cleartext_permitted_);
 }
 
 const HttpNetworkSession::Params* URLRequestContext::GetNetworkSessionParams(
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index 24b03bb..8aeb1e01 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -238,6 +238,15 @@
 
   bool enable_brotli() const { return enable_brotli_; }
 
+  // Sets the |check_cleartext_permitted| flag, which controls whether to check
+  // system policy before allowing a cleartext http or ws request.
+  void set_check_cleartext_permitted(bool check_cleartext_permitted) {
+    check_cleartext_permitted_ = check_cleartext_permitted;
+  }
+
+  // Returns current value of the |check_cleartext_permitted| flag.
+  bool check_cleartext_permitted() const { return check_cleartext_permitted_; }
+
   // Sets a name for this URLRequestContext. Currently the name is used in
   // MemoryDumpProvier to annotate memory usage. The name does not need to be
   // unique.
@@ -285,6 +294,9 @@
 
   // Enables Brotli Content-Encoding support.
   bool enable_brotli_;
+  // Enables checking system policy before allowing a cleartext http or ws
+  // request. Only used on Android.
+  bool check_cleartext_permitted_;
 
   // An optional name which can be set to describe this URLRequestContext.
   // Used in MemoryDumpProvier to annotate memory usage. The name does not need
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 68e708f..6b5300c 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -68,6 +68,10 @@
 #include "net/websockets/websocket_handshake_stream_base.h"
 #include "url/origin.h"
 
+#if defined(OS_ANDROID)
+#include "net/android/network_library.h"
+#endif
+
 static const char kAvailDictionaryHeader[] = "Avail-Dictionary";
 
 namespace {
@@ -169,27 +173,6 @@
                             EPHEMERALITY_MAX);
 }
 
-net::URLRequestRedirectJob* MaybeInternallyRedirect(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) {
-  const GURL& url = request->url();
-  if (url.SchemeIsCryptographic())
-    return nullptr;
-
-  net::TransportSecurityState* hsts =
-      request->context()->transport_security_state();
-  if (!hsts || !hsts->ShouldUpgradeToSSL(url.host()))
-    return nullptr;
-
-  GURL::Replacements replacements;
-  replacements.SetSchemeStr(url.SchemeIs(url::kHttpScheme) ? url::kHttpsScheme
-                                                           : url::kWssScheme);
-  return new net::URLRequestRedirectJob(
-      request, network_delegate, url.ReplaceComponents(replacements),
-      // Use status code 307 to preserve the method, so POST requests work.
-      net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "HSTS");
-}
-
 }  // namespace
 
 namespace net {
@@ -208,10 +191,34 @@
         request, network_delegate, ERR_INVALID_ARGUMENT);
   }
 
-  URLRequestRedirectJob* redirect =
-      MaybeInternallyRedirect(request, network_delegate);
-  if (redirect)
-    return redirect;
+  const GURL& url = request->url();
+
+  // Check for reasons not to return a URLRequestHttpJob. These don't apply to
+  // https and wss requests.
+  if (!url.SchemeIsCryptographic()) {
+    // Check for HSTS upgrade.
+    TransportSecurityState* hsts =
+        request->context()->transport_security_state();
+    if (hsts && hsts->ShouldUpgradeToSSL(url.host())) {
+      GURL::Replacements replacements;
+      replacements.SetSchemeStr(
+          url.SchemeIs(url::kHttpScheme) ? url::kHttpsScheme : url::kWssScheme);
+      return new URLRequestRedirectJob(
+          request, network_delegate, url.ReplaceComponents(replacements),
+          // Use status code 307 to preserve the method, so POST requests work.
+          URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "HSTS");
+    }
+
+#if defined(OS_ANDROID)
+    // Check whether the app allows cleartext traffic to this host, and return
+    // ERR_BLOCKED_BY_CLIENT if not.
+    if (request->context()->check_cleartext_permitted() &&
+        !android::IsCleartextPermitted(url.host())) {
+      return new URLRequestErrorJob(request, network_delegate,
+                                    ERR_CLEARTEXT_NOT_PERMITTED);
+    }
+#endif
+  }
 
   return new URLRequestHttpJob(request,
                                network_delegate,
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index c4490553..dbaea28 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -40,6 +40,12 @@
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "base/android/jni_android.h"
+#include "jni/AndroidNetworkLibraryTestUtil_jni.h"
+#endif
+
 using net::test::IsError;
 using net::test::IsOk;
 
@@ -938,6 +944,46 @@
             request->GetTotalReceivedBytes());
 }
 
+#if defined(OS_ANDROID)
+TEST_F(URLRequestHttpJobTest, AndroidCleartextPermittedTest) {
+  context_.set_check_cleartext_permitted(true);
+
+  struct TestCase {
+    const char* url;
+    bool cleartext_permitted;
+    bool should_block;
+  } cases[] = {
+      {"http://blocked.test/", true, false},
+      {"https://blocked.test/", true, false},
+      {"http://blocked.test/", false, true},
+      {"https://blocked.test/", false, false},
+  };
+
+  for (const TestCase& test : cases) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_AndroidNetworkLibraryTestUtil_setUpSecurityPolicyForTesting(
+        env, test.cleartext_permitted);
+
+    TestDelegate delegate;
+    std::unique_ptr<URLRequest> request =
+        context_.CreateRequest(GURL(test.url), DEFAULT_PRIORITY, &delegate);
+    request->Start();
+    base::RunLoop().Run();
+
+    int sdk_int = base::android::BuildInfo::GetInstance()->sdk_int();
+    bool expect_blocked = (sdk_int >= base::android::SDK_VERSION_MARSHMALLOW &&
+                           test.should_block);
+    if (expect_blocked) {
+      EXPECT_THAT(delegate.request_status(),
+                  IsError(ERR_CLEARTEXT_NOT_PERMITTED));
+    } else {
+      // Should fail since there's no test server running
+      EXPECT_THAT(delegate.request_status(), IsError(ERR_FAILED));
+    }
+  }
+}
+#endif
+
 // This base class just serves to set up some things before the TestURLRequest
 // constructor is called.
 class URLRequestHttpJobWebSocketTestBase : public ::testing::Test {
diff --git a/services/video_capture/test/fake_device_descriptor_unittest.cc b/services/video_capture/test/fake_device_descriptor_unittest.cc
index a747e57b..cc455b8 100644
--- a/services/video_capture/test/fake_device_descriptor_unittest.cc
+++ b/services/video_capture/test/fake_device_descriptor_unittest.cc
@@ -19,7 +19,7 @@
 // Tests that when requesting a second proxy for a device without closing the
 // first one, the service revokes access to the first one by closing the
 // connection.
-TEST_F(FakeDeviceDescriptorTest, AccessIsRevokedOnSecondAccess) {
+TEST_F(FakeDeviceDescriptorTest, DISABLED_AccessIsRevokedOnSecondAccess) {
   mojom::DevicePtr device_proxy_1;
   bool device_access_1_revoked = false;
   MockCreateDeviceProxyCallback create_device_proxy_callback_1;
@@ -55,7 +55,7 @@
 }
 
 // Tests that a second proxy requested for a device can be used successfully.
-TEST_F(FakeDeviceDescriptorTest, CanUseSecondRequestedProxy) {
+TEST_F(FakeDeviceDescriptorTest, DISABLED_CanUseSecondRequestedProxy) {
   mojom::DevicePtr device_proxy_1;
   factory_->CreateDevice(
       fake_device_descriptor_.device_id, mojo::GetProxy(&device_proxy_1),
diff --git a/services/video_capture/test/fake_device_unittest.cc b/services/video_capture/test/fake_device_unittest.cc
index b0ce65b28..eac7fa0 100644
--- a/services/video_capture/test/fake_device_unittest.cc
+++ b/services/video_capture/test/fake_device_unittest.cc
@@ -30,7 +30,7 @@
 
 namespace video_capture {
 
-TEST_F(FakeDeviceTest, FrameCallbacksArrive) {
+TEST_F(FakeDeviceTest, DISABLED_FrameCallbacksArrive) {
   base::RunLoop wait_loop;
   const int kNumFramesToWaitFor = 3;
   int num_frames_arrived = 0;
@@ -51,7 +51,7 @@
 
 // Tests that frames received from a fake capture device match the requested
 // format and have increasing timestamps.
-TEST_F(FakeDeviceTest, ReceiveFramesFromFakeCaptureDevice) {
+TEST_F(FakeDeviceTest, DISABLED_ReceiveFramesFromFakeCaptureDevice) {
   base::RunLoop wait_loop;
   mojom::ReceiverPtr receiver_proxy;
   constexpr int num_frames_to_receive = 2;
diff --git a/services/video_capture/test/mock_device_unittest.cc b/services/video_capture/test/mock_device_unittest.cc
index f3035fe..40f36b4 100644
--- a/services/video_capture/test/mock_device_unittest.cc
+++ b/services/video_capture/test/mock_device_unittest.cc
@@ -12,7 +12,7 @@
 
 // Tests that the service stops the capture device when the client closes the
 // connection to the device proxy.
-TEST_F(MockDeviceTest, DeviceIsStoppedWhenDiscardingDeviceProxy) {
+TEST_F(MockDeviceTest, DISABLED_DeviceIsStoppedWhenDiscardingDeviceProxy) {
   base::RunLoop wait_loop;
 
   // The mock device must hold on to the device client that is passed to it.
@@ -34,7 +34,7 @@
 
 // Tests that the service stops the capture device when the client closes the
 // connection to the client proxy it provided to the service.
-TEST_F(MockDeviceTest, DeviceIsStoppedWhenDiscardingDeviceClient) {
+TEST_F(MockDeviceTest, DISABLED_DeviceIsStoppedWhenDiscardingDeviceClient) {
   base::RunLoop wait_loop;
 
   // The mock device must hold on to the device client that is passed to it.
diff --git a/services/video_capture/test/service_unittest.cc b/services/video_capture/test/service_unittest.cc
index c79f964..dff08e3 100644
--- a/services/video_capture/test/service_unittest.cc
+++ b/services/video_capture/test/service_unittest.cc
@@ -21,7 +21,7 @@
 
 // Tests that an answer arrives from the service when calling
 // EnumerateDeviceDescriptors().
-TEST_F(ServiceTest, EnumerateDeviceDescriptorsCallbackArrives) {
+TEST_F(ServiceTest, DISABLED_EnumerateDeviceDescriptorsCallbackArrives) {
   base::RunLoop wait_loop;
   EXPECT_CALL(descriptor_receiver_, OnEnumerateDeviceDescriptorsCallback(_))
       .Times(Exactly(1))
@@ -33,7 +33,7 @@
   wait_loop.Run();
 }
 
-TEST_F(ServiceTest, FakeDeviceFactoryEnumeratesOneDevice) {
+TEST_F(ServiceTest, DISABLED_FakeDeviceFactoryEnumeratesOneDevice) {
   base::RunLoop wait_loop;
   size_t num_devices_enumerated = 0;
   EXPECT_CALL(descriptor_receiver_, OnEnumerateDeviceDescriptorsCallback(_))
@@ -53,7 +53,7 @@
 
 // Tests that VideoCaptureDeviceFactory::CreateDeviceProxy() returns an error
 // code when trying to create a device for an invalid descriptor.
-TEST_F(ServiceTest, ErrorCodeOnCreateDeviceForInvalidDescriptor) {
+TEST_F(ServiceTest, DISABLED_ErrorCodeOnCreateDeviceForInvalidDescriptor) {
   const std::string invalid_device_id = "invalid";
   base::RunLoop wait_loop;
   mojom::DevicePtr fake_device_proxy;
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2d3b5c1..c9ab944 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -5648,6 +5648,12 @@
           "can_use_on_swarming_builders": true
         },
         "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wtf_unittests"
       }
     ]
   },
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index fc67d68..68e8a99 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1212,6 +1212,14 @@
 
 crbug.com/467477 fast/multicol/vertical-rl/nested-columns.html [ Failure ]
 
+crbug.com/674225 [ Mac ] fast/replaced/input-radio-height-inside-auto-container.html [ Failure ]
+crbug.com/669867 [ Mac Win ] fast/replaced/table-percent-height-text-controls.html [ NeedsRebaseline ]
+crbug.com/669867 [ Mac Win ] fast/replaced/table-percent-height.html [ NeedsRebaseline ]
+crbug.com/669867 [ Mac Win ] fast/table/003.html [ NeedsRebaseline ]
+crbug.com/669867 [ Mac Win ] fast/table/split-table-section-before-anonymous-block-2.html [ NeedsRebaseline ]
+crbug.com/669867 [ Mac Win ] fast/table/split-table-section-before-anonymous-block-4.html [ NeedsRebaseline ]
+crbug.com/669867 [ Mac Win ] tables/mozilla/bugs/bug30692.html [ NeedsRebaseline ]
+
 crbug.com/400841 media/video-canvas-draw.html [ Failure ]
 crbug.com/400829 media/video-object-fit.html [ Failure ]
 crbug.com/400829 virtual/stable/media/stable/video-object-fit-stable.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/fast/replaced/input-radio-height-inside-auto-container-expected.html b/third_party/WebKit/LayoutTests/fast/replaced/input-radio-height-inside-auto-container-expected.html
new file mode 100644
index 0000000..f3d79bc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/replaced/input-radio-height-inside-auto-container-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<p> crbug.com/669867: Inputs and radio boxes get a height of 0 when their container's height is treated as auto. There should be nothing below on Linux and Win. The theme on Mac gives them a full height.</p>
diff --git a/third_party/WebKit/LayoutTests/fast/replaced/input-radio-height-inside-auto-container.html b/third_party/WebKit/LayoutTests/fast/replaced/input-radio-height-inside-auto-container.html
new file mode 100644
index 0000000..9bb8784
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/replaced/input-radio-height-inside-auto-container.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<p> crbug.com/669867: Inputs and radio boxes get a height of 0 when their container's height is treated as auto. There should be nothing below on Linux and Win. The theme on Mac gives them a full height.</p>
+<div><input type="checkbox" style="height: 100%;"><div>
+<div style="display:table-cell;"><input type="checkbox" style="height: 100%;"><div>
+<table><tr><td><input type="checkbox" style="height: 100%;"></td></tr></table>
+<div><input type="radio" style="height: 100%;"><div>
+<div style="display:table-cell;"><input type="radio" style="height: 100%;"><div>
+<table><tr><td><input type="radio" style="height: 100%;"></td></tr></table>
diff --git a/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height-text-controls.html b/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height-text-controls.html
index ac48549..ea744a7 100644
--- a/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height-text-controls.html
+++ b/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height-text-controls.html
@@ -30,11 +30,7 @@
 function getFullHeight(id)
 {
     var element = document.getElementById(id);
-    var h = parseFloat(getComputedStyleForElement(element, 'border-top-width'));
-    h += parseFloat(getComputedStyleForElement(element, 'padding-top'));
-    h += parseFloat(getComputedStyleForElement(element, 'height'));
-    h += parseFloat(getComputedStyleForElement(element, 'padding-bottom'));
-    h += parseFloat(getComputedStyleForElement(element, 'border-bottom-width'));
+    var h = parseFloat(getComputedStyleForElement(element, 'height'));
     return h + 'px';
 }
 
@@ -47,39 +43,24 @@
     return parseFloat(str);
 }
 
-function is75PercentOf(expression75, expression100)
-{
-    var str75 = eval(expression75);
-    var str100 = eval(expression100);
-    var num75 = parsePixelValue(str75);
-    var num100 = parsePixelValue(str100);
-    if (num75 < 0 || num100 < 0)
-        return;
-    if (num75 == Math.floor(num100 * 75 / 100))
-        testPassed(expression75 + " is 75% of " + expression100 + ".");
-    else
-        testFailed(expression75 + " [" + str75 + "] is not 75% of " + expression100 + " [" + str100 + "].");
-}
-
 function test()
 {
     description("This test checks that text controls with percentage heights within table cells have the correct height." +
-        "Text controls are in a different test than other replaced elements because their metrics are platform-specific.");
+        "Text controls are in a different test than other replaced elements because their metrics are platform-specific." +
+        "The reason a 75% control is the same height as a 100% control is because a replaced element that depends on the" +
+        "height of its parent cell is treated as auto. So by itself it will set the height of the row. See https://drafts.csswg.org/css-tables-3/#row-layout");
 
     shouldBe("getWidth('input-password-75')", "getWidth('input-password-100')");
     shouldBeTrue("getFullHeight('input-password-75') != '0px'");
-    // Note: This behavior doesn't match to IE 8, Firefox 3.5 and Opera 10.
-    is75PercentOf("getFullHeight('input-password-75')", "getFullHeight('input-password-100')");
+    shouldBe("getFullHeight('input-password-75')", "getFullHeight('input-password-100')");
 
     shouldBe("getWidth('input-text-75')", "getWidth('input-text-100')");
     shouldBeTrue("getFullHeight('input-text-75') != '0px'");
-    // Note: This behavior doesn't match to IE 8, Firefox 3.5 and Opera 10.
-    is75PercentOf("getFullHeight('input-text-75')", "getFullHeight('input-text-100')");
+    shouldBe("getFullHeight('input-text-75')", "getFullHeight('input-text-100')");
 
     shouldBe("getWidth('textarea-75')", "getWidth('textarea-100')");
     shouldBeTrue("getFullHeight('textarea-75') != '0px'");
-    // Note: This behavior doesn't match to IE 8, Firefox 3.5 and Opera 10.
-    is75PercentOf("getFullHeight('textarea-75')", "getFullHeight('textarea-100')");
+    shouldBe("getFullHeight('textarea-75')", "getFullHeight('textarea-100')");
 
     isSuccessfullyParsed();
 
diff --git a/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height.html b/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height.html
index fce47a1..234104c 100644
--- a/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height.html
+++ b/third_party/WebKit/LayoutTests/fast/replaced/table-percent-height.html
@@ -41,21 +41,6 @@
     return parseFloat(str);
 }
 
-function is75PercentOf(expression75, expression100)
-{
-    var str75 = eval(expression75);
-    var str100 = eval(expression100);
-    var num75 = parsePixelValue(str75);
-    var num100 = parsePixelValue(str100);
-    if (num75 < 0 || num100 < 0)
-        return;
-    var expectedValue = num100 * 75 / 100;
-    if (num75 == expectedValue)
-        testPassed(expression75 + " is 75% of " + expression100 + ".");
-    else
-        testFailed(expression75 + " [" + str75 + "] is not 75% of " + expression100 + " [" + str100 + "].");
-}
-
 function test()
 {
     description("This test checks that replaced elements with percentage heights within table cells have the correct height.<br>Note, some of the button height tests fail on the Windows ports. See bug #34071.");
@@ -94,14 +79,12 @@
     shouldBe("getHeight('input-button-75')", "getHeight('input-button-100')");
 
     shouldBe("getWidth('input-checkbox-75')", "getWidth('input-checkbox-100')");
-    shouldBeTrue("getHeight('input-checkbox-75') != '0px'");
-    // Note: This behavior doesn't match to Firefox 3.5 and Opera 10.
-    is75PercentOf("getHeight('input-checkbox-75')", "getHeight('input-checkbox-100')");
+    shouldBeTrue("getHeight('input-checkbox-75') == '0px'");
+    shouldBe("getHeight('input-checkbox-75')", "getHeight('input-checkbox-100')");
 
     shouldBe("getWidth('input-file-75')", "getWidth('input-file-100')");
     shouldBeTrue("getHeight('input-file-75') != '0px'");
-    // Note: This behavior doesn't match to Firefox 3.5 and Opera 10.
-    is75PercentOf("getHeight('input-file-75')", "getHeight('input-file-100')");
+    shouldBe("getHeight('input-file-75')", "getHeight('input-file-100')");
 
     // Note: This behavior doesn't match to Firefox 3.5 and Opera 10.
     shouldBe("getWidth('input-image-75')", "'75px'");
@@ -110,9 +93,8 @@
     shouldBe("getHeight('input-image-100')", "'100px'");
 
     shouldBe("getWidth('input-radio-75')", "getWidth('input-radio-100')");
-    shouldBeTrue("getHeight('input-radio-75') != '0px'");
-    // Note: This behavior doesn't match to Firefox 3.5 and Opera 10.
-    is75PercentOf("getHeight('input-radio-75')", "getHeight('input-radio-100')");
+    shouldBeTrue("getHeight('input-radio-75') == '0px'");
+    shouldBe("getHeight('input-radio-75')", "getHeight('input-radio-100')");
 
     shouldBe("getWidth('input-reset-75')", "getWidth('input-reset-100')");
     shouldBeTrue("getHeight('input-reset-75') != '0px'");
diff --git a/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-2-expected.txt b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-2-expected.txt
new file mode 100644
index 0000000..9a4644a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-2-expected.txt
@@ -0,0 +1,4 @@
+Text
+crbug.com/669687: Percent height border-box replaced content in a cell gets the correct height.
+
+PASS
diff --git a/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-2.html b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-2.html
new file mode 100644
index 0000000..ff7c7386
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+.cell { display:table-cell; }
+.div { height: 100%; display: inline-block; box-sizing: border-box; border: 2px solid black; padding: 2px; font: 20px Ahem;}
+</style>
+<div class="cell">
+  <div class="div" data-expected-height=28>Text</div>
+</div>
+<script src="../../resources/check-layout.js"></script>
+<p> crbug.com/669687: Percent height border-box replaced content in a cell gets the correct height. </p>
+<div id="output"></div>
+<script>
+checkLayout('.div', output);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-3-expected.txt b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-3-expected.txt
new file mode 100644
index 0000000..c08317d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-3-expected.txt
@@ -0,0 +1,4 @@
+Text
+crbug.com/669687: Percent height border-box content in a cell gets the correct height.
+
+PASS
diff --git a/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-3.html b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-3.html
new file mode 100644
index 0000000..1133ec5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-3.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+.cell { display:table-cell; }
+.div { height: 100%; box-sizing: border-box; border: 2px solid black; padding: 2px; font: 20px Ahem;}
+</style>
+<div class="cell">
+  <div class="div" data-expected-height=28>Text</div>
+</div>
+<script src="../../resources/check-layout.js"></script>
+<p> crbug.com/669687: Percent height border-box content in a cell gets the correct height. </p>
+<div id="output"></div>
+<script>
+checkLayout('.div', output);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-expected.html b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-expected.html
new file mode 100644
index 0000000..4283e96
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell-expected.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<style>
+.button { font: 20px Ahem;}
+</style>
+<button type="button" class="button">Text</button>
+<p> crbug.com/669687: Percent height border-box replaced content in a cell gets the correct height. </p>
diff --git a/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell.html b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell.html
new file mode 100644
index 0000000..9fc9fdb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/percent-height-border-box-content-in-cell.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+.cell { display:table-cell; }
+.button { height: 100%; font: 20px Ahem;}
+</style>
+<div class="cell">
+  <button type="button" class="button" id="button">Text</button>
+</div>
+<p> crbug.com/669687: Percent height border-box replaced content in a cell gets the correct height. </p>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.png
index 2881b6b..cc95422 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.txt
index 88484df..1f5a7db8 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/table/003-expected.txt
@@ -19,8 +19,8 @@
         LayoutTableSection {TBODY} at (2,2) size 96x96
           LayoutTableRow {TR} at (0,2) size 96x92
             LayoutTableCell {TD} at (2,46) size 92x4 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-      LayoutTable {TABLE} at (0,152) size 191x120 [border: (2px outset #808080)]
-        LayoutTableSection {TBODY} at (2,2) size 187x116
+      LayoutTable {TABLE} at (0,152) size 191x126 [border: (2px outset #808080)]
+        LayoutTableSection {TBODY} at (2,2) size 187x122
           LayoutTableRow {TR} at (0,2) size 187x24
             LayoutTableCell {TD} at (2,2) size 183x24 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
               LayoutText {#text} at (2,2) size 28x19
@@ -33,10 +33,10 @@
             LayoutTableCell {TD} at (2,54) size 183x24 [border: (1px inset #808080)] [r=2 c=0 rs=1 cs=1]
               LayoutText {#text} at (2,2) size 35x19
                 text run at (2,2) width 35: "world"
-          LayoutTableRow {TR} at (0,80) size 187x34
-            LayoutTableCell {TD} at (2,82) size 183x30 [border: (1px inset #808080)] [r=3 c=0 rs=1 cs=1]
+          LayoutTableRow {TR} at (0,80) size 187x40
+            LayoutTableCell {TD} at (2,80) size 183x40 [border: (1px inset #808080)] [r=3 c=0 rs=1 cs=1]
               LayoutText {#text} at (0,0) size 0x0
-      LayoutTable {TABLE} at (0,272) size 106x86
+      LayoutTable {TABLE} at (0,278) size 106x86
         LayoutTableSection {TBODY} at (0,0) size 106x86
           LayoutTableRow {TR} at (0,2) size 106x82
             LayoutTableCell {TD} at (2,2) size 102x82 [r=0 c=0 rs=1 cs=1]
@@ -46,7 +46,7 @@
                 text run at (1,41) width 54: "nowrap. "
                 text run at (55,41) width 41: "I really"
                 text run at (1,61) width 43: "should."
-      LayoutTable {TABLE} at (0,358) size 106x86
+      LayoutTable {TABLE} at (0,364) size 106x86
         LayoutTableSection {TBODY} at (0,0) size 106x86
           LayoutTableRow {TR} at (0,2) size 106x82
             LayoutTableCell {TD} at (2,2) size 102x82 [r=0 c=0 rs=1 cs=1]
@@ -57,7 +57,7 @@
                   text run at (0,40) width 54: "nowrap. "
                   text run at (54,40) width 41: "I really"
                   text run at (0,60) width 43: "should."
-      LayoutTable {TABLE} at (0,444) size 345x26
+      LayoutTable {TABLE} at (0,450) size 345x26
         LayoutTableSection {TBODY} at (0,0) size 345x26
           LayoutTableRow {TR} at (0,2) size 345x22
             LayoutTableCell {TD} at (2,2) size 341x22 [r=0 c=0 rs=1 cs=1]
@@ -65,7 +65,7 @@
                 text run at (1,1) width 138: "I should have nowrap. "
                 text run at (139,1) width 92: "I really should. "
                 text run at (231,1) width 109: "Definitely. Should."
-      LayoutTable {TABLE} at (0,470) size 345x26
+      LayoutTable {TABLE} at (0,476) size 345x26
         LayoutTableSection {TBODY} at (0,0) size 345x26
           LayoutTableRow {TR} at (0,2) size 345x22
             LayoutTableCell {TD} at (2,2) size 341x22 [r=0 c=0 rs=1 cs=1]
@@ -76,6 +76,6 @@
                   text run at (230,0) width 109: "Definitely. Should."
 layer at (57,14) size 730x16
   LayoutBlockFlow {DIV} at (2,3) size 730x16
-layer at (14,246) size 179x26 clip at (15,247) size 177x24
-  LayoutTextControl {TEXTAREA} at (2,2) size 179x26 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+layer at (14,244) size 179x36 clip at (15,245) size 177x34
+  LayoutTextControl {TEXTAREA} at (2,2) size 179x36 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
     LayoutBlockFlow {DIV} at (3,3) size 175x16
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.png
index 3305ee0d4..88c0ba4 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.txt
index f248aa7..166a2281 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-2-expected.txt
@@ -14,5 +14,5 @@
           LayoutTableRow {DIV} at (0,0) size 150x0
           LayoutTableRow (anonymous) at (0,0) size 150x20
             LayoutTableCell (anonymous) at (0,0) size 150x20 [r=2 c=0 rs=1 cs=1]
-              LayoutBlockFlow {DIV} at (0,5) size 75x10 [bgcolor=#0000FF]
-              LayoutBlockFlow {DIV} at (75,5) size 75x10 [bgcolor=#0000FF]
+              LayoutBlockFlow {DIV} at (0,15) size 75x0 [bgcolor=#0000FF]
+              LayoutBlockFlow {DIV} at (75,15) size 75x0 [bgcolor=#0000FF]
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.png
index 3305ee0d4..88c0ba4 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.txt
index 22c31f0..4b293af 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/table/split-table-section-before-anonymous-block-4-expected.txt
@@ -10,8 +10,8 @@
         LayoutTableSection {DIV} at (0,0) size 150x20
           LayoutTableRow (anonymous) at (0,0) size 150x20
             LayoutTableCell (anonymous) at (0,0) size 150x20 [r=0 c=0 rs=1 cs=1]
-              LayoutBlockFlow {DIV} at (0,5) size 75x10 [bgcolor=#0000FF]
-              LayoutBlockFlow {DIV} at (75,5) size 75x10 [bgcolor=#0000FF]
+              LayoutBlockFlow {DIV} at (0,15) size 75x0 [bgcolor=#0000FF]
+              LayoutBlockFlow {DIV} at (75,15) size 75x0 [bgcolor=#0000FF]
           LayoutTableRow {DIV} at (0,20) size 150x0
           LayoutTableRow (anonymous) at (0,20) size 150x0
             LayoutTableCell (anonymous) at (0,20) size 150x0 [r=2 c=0 rs=1 cs=1]
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.png
index 560562f..cff317d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.txt
index ca08f06c..71313fe 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug30692-expected.txt
@@ -20,7 +20,7 @@
         LayoutTable {TABLE} at (0,38) size 784x100
           LayoutTableSection {TBODY} at (0,0) size 784x100
             LayoutTableRow {TR} at (0,2) size 784x96
-              LayoutTableCell {TD} at (2,13) size 780x73 [bgcolor=#FF0000] [r=0 c=0 rs=1 cs=1]
+              LayoutTableCell {TD} at (2,11) size 780x77 [bgcolor=#FF0000] [r=0 c=0 rs=1 cs=1]
                 LayoutText {#text} at (0,0) size 0x0
         LayoutBlockFlow {HR} at (0,146) size 784x2 [border: (1px inset #EEEEEE)]
         LayoutTable {TABLE} at (0,156) size 784x100
@@ -44,8 +44,8 @@
                 LayoutBlockFlow {P} at (1,1) size 622.39x80 [bgcolor=#FFFFE0]
                   LayoutText {#text} at (0,0) size 210x19
                     text run at (0,0) width 210: "OK: the height of the P is 80 pixels"
-layer at (11,100) size 622x71 clip at (12,101) size 620x69
-  LayoutTextControl {TEXTAREA} at (1,1) size 622.39x71.19 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+layer at (11,98) size 622x75 clip at (12,99) size 620x73
+  LayoutTextControl {TEXTAREA} at (1,1) size 622.39x75.19 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
     LayoutBlockFlow {DIV} at (3,3) size 618.39x16
       LayoutText {#text} at (0,0) size 336x16
         text run at (0,0) width 336: "BUG: the height of the textarea is not 80%"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-expected.txt
index 1901ea8..df407c6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-expected.txt
@@ -55,32 +55,32 @@
 PASS getHeight('object-100') is '150px'
 PASS getWidth('button-75') is getWidth('button-100')
 PASS getHeight('button-75') != '0px' is true
-FAIL getHeight('button-75') should be 16px. Was 10.5px.
+PASS getHeight('button-75') is getHeight('button-100')
 PASS getWidth('input-button-75') is getWidth('input-button-100')
 PASS getHeight('input-button-75') != '0px' is true
-FAIL getHeight('input-button-75') should be 16px. Was 10.5px.
+PASS getHeight('input-button-75') is getHeight('input-button-100')
 PASS getWidth('input-checkbox-75') is getWidth('input-checkbox-100')
-PASS getHeight('input-checkbox-75') != '0px' is true
-PASS getHeight('input-checkbox-75') is 75% of getHeight('input-checkbox-100').
+PASS getHeight('input-checkbox-75') == '0px' is true
+PASS getHeight('input-checkbox-75') is getHeight('input-checkbox-100')
 PASS getWidth('input-file-75') is getWidth('input-file-100')
 PASS getHeight('input-file-75') != '0px' is true
-PASS getHeight('input-file-75') is 75% of getHeight('input-file-100').
+PASS getHeight('input-file-75') is getHeight('input-file-100')
 PASS getWidth('input-image-75') is '75px'
 PASS getHeight('input-image-75') is '75px'
 PASS getWidth('input-image-100') is '100px'
 PASS getHeight('input-image-100') is '100px'
 PASS getWidth('input-radio-75') is getWidth('input-radio-100')
-PASS getHeight('input-radio-75') != '0px' is true
-PASS getHeight('input-radio-75') is 75% of getHeight('input-radio-100').
+PASS getHeight('input-radio-75') == '0px' is true
+PASS getHeight('input-radio-75') is getHeight('input-radio-100')
 PASS getWidth('input-reset-75') is getWidth('input-reset-100')
 PASS getHeight('input-reset-75') != '0px' is true
-FAIL getHeight('input-reset-75') should be 16px. Was 10.5px.
+PASS getHeight('input-reset-75') is getHeight('input-reset-100')
 PASS getWidth('input-submit-75') is getWidth('input-submit-100')
 PASS getHeight('input-submit-75') != '0px' is true
-FAIL getHeight('input-submit-75') should be 16px. Was 10.5px.
+PASS getHeight('input-submit-75') is getHeight('input-submit-100')
 PASS getWidth('select-75') is getWidth('select-100')
 PASS getHeight('select-75') != '0px' is true
-FAIL getHeight('select-75') should be 18px. Was 13px.
+PASS getHeight('select-75') is getHeight('select-100')
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-text-controls-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-text-controls-expected.txt
index 6a84efd..9a2f2e32 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-text-controls-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/table-percent-height-text-controls-expected.txt
@@ -4,7 +4,7 @@
 
 
 
-This test checks that text controls with percentage heights within table cells have the correct height.Text controls are in a different test than other replaced elements because their metrics are platform-specific.
+This test checks that text controls with percentage heights within table cells have the correct height.Text controls are in a different test than other replaced elements because their metrics are platform-specific.The reason a 75% control is the same height as a 100% control is because a replaced element that depends on theheight of its parent cell is treated as auto. So by itself it will set the height of the row. See https://drafts.csswg.org/css-tables-3/#row-layout
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
@@ -14,13 +14,13 @@
 TEST COMPLETE
 PASS getWidth('input-password-75') is getWidth('input-password-100')
 PASS getFullHeight('input-password-75') != '0px' is true
-FAIL getFullHeight('input-password-75') [16.5px] is not 75% of getFullHeight('input-password-100') [22px].
+PASS getFullHeight('input-password-75') is getFullHeight('input-password-100')
 PASS getWidth('input-text-75') is getWidth('input-text-100')
 PASS getFullHeight('input-text-75') != '0px' is true
-FAIL getFullHeight('input-text-75') [16.5px] is not 75% of getFullHeight('input-text-100') [22px].
+PASS getFullHeight('input-text-75') is getFullHeight('input-text-100')
 PASS getWidth('textarea-75') is getWidth('textarea-100')
 PASS getFullHeight('textarea-75') != '0px' is true
-PASS getFullHeight('textarea-75') is 75% of getFullHeight('textarea-100').
+PASS getFullHeight('textarea-75') is getFullHeight('textarea-100')
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index dd615e7..d9673fa2 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -3185,19 +3185,16 @@
       // always make ourselves be a percentage of the cell's current content
       // height.
       if (!cb->hasOverrideLogicalContentHeight()) {
-        // Normally we would let the cell size intrinsically, but scrolling
-        // overflow has to be treated differently, since WinIE lets scrolled
-        // overflow regions shrink as needed.
-        // While we can't get all cases right, we can at least detect when the
-        // cell has a specified height or when the table has a specified height.
-        // In these cases we want to initially have no size and allow the
-        // flexing of the table or the cell to its specified height to cause us
-        // to grow to fill the space. This could end up being wrong in some
-        // cases, but it is preferable to the alternative (sizing intrinsically
-        // and making the row end up too big).
+        // https://drafts.csswg.org/css-tables-3/#row-layout:
+        // For the purpose of calculating [the minimum height of a row],
+        // descendants of table cells whose height depends on percentages
+        // of their parent cell's height are considered to have an auto
+        // height if they have overflow set to visible or hidden or if
+        // they are replaced elements, and a 0px height if they have not.
         LayoutTableCell* cell = toLayoutTableCell(cb);
         if (style()->overflowY() != EOverflow::Visible &&
             style()->overflowY() != EOverflow::Hidden &&
+            !shouldBeConsideredAsReplaced() &&
             (!cell->style()->logicalHeight().isAuto() ||
              !cell->table()->style()->logicalHeight().isAuto()))
           return LayoutUnit();
@@ -3218,16 +3215,17 @@
     availableHeight += cb->paddingLogicalHeight();
 
   LayoutUnit result = valueForLength(height, availableHeight);
-  bool includeBorderPadding =
+  // |overrideLogicalContentHeight| is the maximum height made available by the
+  // cell to its percent height children when we decide they can determine the
+  // height of the cell. If the percent height child is box-sizing:content-box
+  // then we must subtract the border and padding from the cell's
+  // |availableHeight| (given by |overrideLogicalContentHeight|) to arrive
+  // at the child's computed height.
+  bool subtractBorderAndPadding =
       isTable() || (cb->isTableCell() && !skippedAutoHeightContainingBlock &&
-                    cb->hasOverrideLogicalContentHeight());
-
-  if (includeBorderPadding) {
-    // FIXME: Table cells should default to box-sizing: border-box so we can
-    // avoid this hack.
-    // It is necessary to use the border-box to match WinIE's broken
-    // box model. This is essential for sizing inside
-    // table cells using percentage heights.
+                    cb->hasOverrideLogicalContentHeight() &&
+                    style()->boxSizing() == BoxSizingContentBox);
+  if (subtractBorderAndPadding) {
     result -= borderAndPaddingLogicalHeight();
     return std::max(LayoutUnit(), result);
   }
@@ -4672,9 +4670,12 @@
 }
 
 DISABLE_CFI_PERF
-static bool shouldBeConsideredAsReplaced(Node* node) {
+bool LayoutBox::shouldBeConsideredAsReplaced() const {
   // Checkboxes and radioboxes are not isAtomicInlineLevel() nor do they have
   // their own layoutObject in which to override avoidFloats().
+  if (isAtomicInlineLevel())
+    return true;
+  Node* node = this->node();
   return node && node->isElementNode() &&
          (toElement(node)->isFormControlElement() ||
           isHTMLImageElement(toElement(node)));
@@ -4682,10 +4683,9 @@
 
 DISABLE_CFI_PERF
 bool LayoutBox::avoidsFloats() const {
-  return isAtomicInlineLevel() || shouldBeConsideredAsReplaced(node()) ||
-         hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() ||
-         isFlexItemIncludingDeprecated() || style()->containsPaint() ||
-         style()->containsLayout();
+  return shouldBeConsideredAsReplaced() || hasOverflowClip() || isHR() ||
+         isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated() ||
+         style()->containsPaint() || style()->containsLayout();
 }
 
 bool LayoutBox::hasNonCompositedScrollbars() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h
index d4a1ae1..795abf62 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -1097,6 +1097,7 @@
 
   bool shrinkToAvoidFloats() const;
   virtual bool avoidsFloats() const;
+  bool shouldBeConsideredAsReplaced() const;
 
   void updateFragmentationInfoForChild(LayoutBox&);
   bool childNeedsRelayoutForPagination(const LayoutBox&) const;
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 759b828..4bb1a926 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -1727,15 +1727,18 @@
 
   // Text nodes share style with their parents but the paint properties don't
   // apply to them, hence the !isText() check.
-  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() &&
-      diff.needsPaintPropertyUpdate() && !isText()) {
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && !isText() &&
+      (diff.transformChanged() || diff.opacityChanged() ||
+       diff.zIndexChanged() || diff.filterChanged() ||
+       diff.backdropFilterChanged() || diff.cssClipChanged())) {
     setNeedsPaintPropertyUpdate();
 
     // We don't need to invalidate paint of objects on SPv2 when only paint
     // property or paint order change. Mark the painting layer needing repaint
     // for changed paint property or paint order. Raster invalidation will be
     // issued if needed during paint.
-    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() &&
+        !shouldDoFullPaintInvalidation())
       ObjectPaintInvalidator(*this).slowSetPaintingLayerNeedsRepaint();
   }
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
index 8dcca0a..2efec3e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -1110,13 +1110,15 @@
   return extraLogicalHeight - remainingExtraLogicalHeight;
 }
 
-static bool shouldFlexCellChild(LayoutObject* cellDescendant) {
-  return cellDescendant->isAtomicInlineLevel() ||
-         (cellDescendant->isBox() &&
-          toLayoutBox(cellDescendant)->style()->overflowY() !=
-              EOverflow::Visible &&
-          toLayoutBox(cellDescendant)->style()->overflowY() !=
-              EOverflow::Hidden);
+static bool shouldFlexCellChild(const LayoutTableCell& cell,
+                                LayoutObject* cellDescendant) {
+  if (!cell.style()->logicalHeight().isSpecified())
+    return false;
+  if (cellDescendant->style()->overflowY() == EOverflow::Visible ||
+      cellDescendant->style()->overflowY() == EOverflow::Hidden)
+    return true;
+  return cellDescendant->isBox() &&
+         toLayoutBox(cellDescendant)->shouldBeConsideredAsReplaced();
 }
 
 void LayoutTableSection::layoutRows() {
@@ -1902,7 +1904,7 @@
   for (LayoutObject* child = cell.firstChild(); child;
        child = child->nextSibling()) {
     if (!child->isText() && child->style()->logicalHeight().isPercentOrCalc() &&
-        (flexAllChildren || shouldFlexCellChild(child)) &&
+        (flexAllChildren || shouldFlexCellChild(cell, child)) &&
         (!child->isTable() || toLayoutTable(child)->hasSections())) {
       cellChildrenFlex = true;
       break;
@@ -1913,7 +1915,7 @@
     if (TrackedLayoutBoxListHashSet* percentHeightDescendants =
             cell.percentHeightDescendants()) {
       for (auto* descendant : *percentHeightDescendants) {
-        if (flexAllChildren || shouldFlexCellChild(descendant)) {
+        if (flexAllChildren || shouldFlexCellChild(cell, descendant)) {
           cellChildrenFlex = true;
           break;
         }
diff --git a/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp b/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp
index e12eb52..0db07cc 100644
--- a/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp
@@ -21,6 +21,10 @@
                         PaintControllerPaintTestForSlimmingPaintV1AndV2,
                         ::testing::Bool());
 
+INSTANTIATE_TEST_CASE_P(All,
+                        PaintControllerPaintTestForSlimmingPaintV2,
+                        ::testing::Bool());
+
 TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2,
        FullDocumentPaintingWithCaret) {
   setBodyInnerHTML(
@@ -171,7 +175,7 @@
   }
 }
 
-TEST_F(PaintControllerPaintTestForSlimmingPaintV2, ChunkIdClientCacheFlag) {
+TEST_P(PaintControllerPaintTestForSlimmingPaintV2, ChunkIdClientCacheFlag) {
   setBodyInnerHTML(
       "<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>"
       "  <div style='width: 100px; height: 100px; background-color: "
@@ -185,29 +189,48 @@
   LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div"));
   LayoutObject& subDiv = *div.firstChild();
   LayoutObject& subDiv2 = *subDiv.nextSibling();
-  EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 11,
-      TestDisplayItem(layoutView(),
-                      DisplayItem::kClipFrameToVisibleContentRect),
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-      TestDisplayItem(layoutView(), documentBackgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      TestDisplayItem(div, DisplayItem::kBeginCompositing),
-      TestDisplayItem(subDiv, backgroundType),
-      TestDisplayItem(subDiv2, backgroundType),
-      TestDisplayItem(div, DisplayItem::kEndCompositing),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence),
-      TestDisplayItem(layoutView(),
-                      DisplayItem::clipTypeToEndClipType(
-                          DisplayItem::kClipFrameToVisibleContentRect)));
+  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
+    EXPECT_DISPLAY_LIST(
+        rootPaintController().getDisplayItemList(), 9,
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
+        TestDisplayItem(layoutView(), documentBackgroundType),
+        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+        TestDisplayItem(div, DisplayItem::kBeginCompositing),
+        TestDisplayItem(subDiv, backgroundType),
+        TestDisplayItem(subDiv2, backgroundType),
+        TestDisplayItem(div, DisplayItem::kEndCompositing),
+        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+  } else {
+    EXPECT_DISPLAY_LIST(
+        rootPaintController().getDisplayItemList(), 11,
+        TestDisplayItem(layoutView(),
+                        DisplayItem::kClipFrameToVisibleContentRect),
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
+        TestDisplayItem(layoutView(), documentBackgroundType),
+        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+        TestDisplayItem(div, DisplayItem::kBeginCompositing),
+        TestDisplayItem(subDiv, backgroundType),
+        TestDisplayItem(subDiv2, backgroundType),
+        TestDisplayItem(div, DisplayItem::kEndCompositing),
+        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence),
+        TestDisplayItem(layoutView(),
+                        DisplayItem::clipTypeToEndClipType(
+                            DisplayItem::kClipFrameToVisibleContentRect)));
+  }
 
   const PaintChunk& backgroundChunk = rootPaintController().paintChunks()[0];
   EXPECT_TRUE(backgroundChunk.properties.propertyTreeState.scroll()->isRoot());
 
   const EffectPaintPropertyNode* effectNode = div.paintProperties()->effect();
   EXPECT_EQ(0.5f, effectNode->opacity());
-  const PaintChunk& chunk = rootPaintController().paintChunks()[1];
+
+  // When RLS is enabled, an additional paint chunk will be created for the
+  // LayoutView's layer.
+  unsigned divChunkIndex =
+      RuntimeEnabledFeatures::rootLayerScrollingEnabled() ? 2 : 1;
+  const PaintChunk& chunk = rootPaintController().paintChunks()[divChunkIndex];
   EXPECT_EQ(*div.layer(), chunk.id->client);
   EXPECT_EQ(effectNode, chunk.properties.propertyTreeState.effect());
 
@@ -219,7 +242,7 @@
   EXPECT_TRUE(rootPaintController().clientCacheIsValid(subDiv));
 }
 
-TEST_F(PaintControllerPaintTestForSlimmingPaintV2, CompositingFold) {
+TEST_P(PaintControllerPaintTestForSlimmingPaintV2, CompositingFold) {
   setBodyInnerHTML(
       "<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>"
       "  <div style='width: 100px; height: 100px; background-color: "
@@ -231,21 +254,36 @@
   LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div"));
   LayoutObject& subDiv = *div.firstChild();
 
-  EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 8,
-      TestDisplayItem(layoutView(),
-                      DisplayItem::kClipFrameToVisibleContentRect),
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-      TestDisplayItem(layoutView(), documentBackgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      // The begin and end compositing display items have been folded into this
-      // one.
-      TestDisplayItem(subDiv, backgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence),
-      TestDisplayItem(layoutView(),
-                      DisplayItem::clipTypeToEndClipType(
-                          DisplayItem::kClipFrameToVisibleContentRect)));
+  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
+    EXPECT_DISPLAY_LIST(
+        rootPaintController().getDisplayItemList(), 6,
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
+        TestDisplayItem(layoutView(), documentBackgroundType),
+        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+        // The begin and end compositing display items have been folded into
+        // this
+        // one.
+        TestDisplayItem(subDiv, backgroundType),
+        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+  } else {
+    EXPECT_DISPLAY_LIST(
+        rootPaintController().getDisplayItemList(), 8,
+        TestDisplayItem(layoutView(),
+                        DisplayItem::kClipFrameToVisibleContentRect),
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
+        TestDisplayItem(layoutView(), documentBackgroundType),
+        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+        // The begin and end compositing display items have been folded into
+        // this
+        // one.
+        TestDisplayItem(subDiv, backgroundType),
+        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
+        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence),
+        TestDisplayItem(layoutView(),
+                        DisplayItem::clipTypeToEndClipType(
+                            DisplayItem::kClipFrameToVisibleContentRect)));
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.h b/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.h
index 8cda2f5..050195e 100644
--- a/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.h
+++ b/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.h
@@ -88,10 +88,13 @@
 };
 
 class PaintControllerPaintTestForSlimmingPaintV2
-    : public PaintControllerPaintTestBase {
+    : public PaintControllerPaintTestBase,
+      public testing::WithParamInterface<bool>,
+      private ScopedRootLayerScrollingForTest {
  public:
   PaintControllerPaintTestForSlimmingPaintV2()
-      : PaintControllerPaintTestBase(true) {}
+      : PaintControllerPaintTestBase(true),
+        ScopedRootLayerScrollingForTest(GetParam()) {}
 };
 
 class PaintControllerPaintTestForSlimmingPaintV1AndV2
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
index 65a57b66..8cacaeb8a2 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
@@ -27,6 +27,10 @@
   PaintLayer* targetPaintLayer =
       toLayoutBoxModelObject(target->layoutObject())->layer();
   ClipRectsContext context(document().layoutView()->layer(), UncachedClipRects);
+  // When RLS is enabled, the LayoutView will have a composited scrolling layer,
+  // so don't apply an overflow clip.
+  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled())
+    context.setIgnoreOverflowClip();
   LayoutRect layerBounds;
   ClipRect backgroundRect, foregroundRect;
   targetPaintLayer->clipper().calculateRects(
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index 4a7e199..d822d5bc 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -586,12 +586,10 @@
 
   updatePropertySpecificDifferences(other, diff);
 
-  // The following conditions need to be at last, because they may depend on
+  // The following condition needs to be at last, because it may depend on
   // conditions in diff computed above.
   if (scrollAnchorDisablingPropertyChanged(other, diff))
     diff.setScrollAnchorDisablingPropertyChanged();
-  if (diffNeedsPaintPropertyUpdate(other, diff))
-    diff.setNeedsPaintPropertyUpdate();
 
   // Cursors are not checked, since they will be set appropriately in response
   // to mouse events, so they don't need to cause any paint invalidation or
@@ -1132,17 +1130,6 @@
     diff.setCSSClipChanged();
 }
 
-bool ComputedStyle::diffNeedsPaintPropertyUpdate(
-    const ComputedStyle& other,
-    const StyleDifference& diff) const {
-  if (diff.transformChanged() || diff.opacityChanged() ||
-      diff.zIndexChanged() || diff.filterChanged() ||
-      diff.backdropFilterChanged() || diff.cssClipChanged())
-    return true;
-
-  return false;
-}
-
 void ComputedStyle::addPaintImage(StyleImage* image) {
   if (!m_rareNonInheritedData.access()->m_paintImages) {
     m_rareNonInheritedData.access()->m_paintImages =
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index fcbdce1..85a68d0 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -3918,8 +3918,6 @@
       const ComputedStyle& other) const;
   void updatePropertySpecificDifferences(const ComputedStyle& other,
                                          StyleDifference&) const;
-  bool diffNeedsPaintPropertyUpdate(const ComputedStyle& other,
-                                    const StyleDifference&) const;
 
   bool requireTransformOrigin(ApplyTransformOrigin applyOrigin,
                               ApplyMotionPath) const;
diff --git a/third_party/WebKit/Source/core/style/StyleDifference.h b/third_party/WebKit/Source/core/style/StyleDifference.h
index ed12f68..3003e61 100644
--- a/third_party/WebKit/Source/core/style/StyleDifference.h
+++ b/third_party/WebKit/Source/core/style/StyleDifference.h
@@ -33,17 +33,15 @@
         m_layoutType(NoLayout),
         m_recomputeOverflow(false),
         m_propertySpecificDifferences(0),
-        m_scrollAnchorDisablingPropertyChanged(false),
-        m_needsPaintPropertyUpdate(false) {}
+        m_scrollAnchorDisablingPropertyChanged(false) {}
 
   bool hasDifference() const {
     bool result = m_paintInvalidationType || m_layoutType ||
                   m_propertySpecificDifferences;
     // m_recomputeOverflow, m_scrollAnchorDisablingPropertyChanged and
-    // m_needsPaintPropertyUpdate are never set without other flags set.
+    // are never set without other flags set.
     DCHECK(result ||
-           (!m_recomputeOverflow && !m_scrollAnchorDisablingPropertyChanged &&
-            !m_needsPaintPropertyUpdate));
+           (!m_recomputeOverflow && !m_scrollAnchorDisablingPropertyChanged));
     return result;
   }
 
@@ -140,9 +138,6 @@
     m_scrollAnchorDisablingPropertyChanged = true;
   }
 
-  bool needsPaintPropertyUpdate() const { return m_needsPaintPropertyUpdate; }
-  void setNeedsPaintPropertyUpdate() { m_needsPaintPropertyUpdate = true; }
-
  private:
   enum PaintInvalidationType {
     NoPaintInvalidation = 0,
@@ -156,7 +151,6 @@
   unsigned m_recomputeOverflow : 1;
   unsigned m_propertySpecificDifferences : 7;
   unsigned m_scrollAnchorDisablingPropertyChanged : 1;
-  unsigned m_needsPaintPropertyUpdate : 1;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
index cb7fe59..437a4ed 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
@@ -312,7 +312,10 @@
     var contentElement = toggleElement.createChild('div', 'console-message-stack-trace-wrapper');
 
     var messageElement = this._buildMessage(consoleMessage);
+    var icon = UI.Icon.create('smallicon-triangle-right', 'stack-trace-expand-icon');
     var clickableElement = contentElement.createChild('div');
+    clickableElement.appendChild(icon);
+
     clickableElement.appendChild(messageElement);
     var stackTraceElement = contentElement.createChild('div');
     var stackTracePreview =
@@ -324,8 +327,8 @@
      * @param {boolean} expand
      */
     function expandStackTrace(expand) {
+      icon.setIconType(expand ? 'smallicon-triangle-bottom' : 'smallicon-triangle-right');
       stackTraceElement.classList.toggle('hidden', !expand);
-      toggleElement.classList.toggle('expanded', expand);
     }
 
     /**
diff --git a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
index 69646aa..6d7e9f8b 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
+++ b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
@@ -367,27 +367,6 @@
     flex: none;
 }
 
-.console-message-stack-trace-toggle .console-message-text::before {
-    content: " ";
-    -webkit-user-select: none;
-    -webkit-mask-image: url(Images/toolbarButtonGlyphs.png);
-    -webkit-mask-size: 352px 168px;
-    color: transparent;
-    text-shadow: none;
-    padding-right: 8px;
-    height: 12px;
+.stack-trace-expand-icon {
     background-color: rgb(110, 110, 110);
-    width: 12px;
-    flex: none;
-    -webkit-mask-position: -4px -96px;
-}
-
-@media (-webkit-min-device-pixel-ratio: 1.1) {
-.console-message-stack-trace-toggle .console-message-text::before {
-    -webkit-mask-image: url(Images/toolbarButtonGlyphs_2x.png);
-}
-} /* media */
-
-.console-message-stack-trace-toggle.expanded .console-message-text::before {
-    -webkit-mask-position: -20px -96px;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index e934696..dd1b2fb 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -2109,10 +2109,30 @@
     }
   }
 
+  /**
+   * @override
+   */
+  onexpand() {
+    this._updateExpandElement();
+  }
+
+  /**
+   * @override
+   */
+  oncollapse() {
+    this._updateExpandElement();
+  }
+
+  _updateExpandElement() {
+    if (this.expanded)
+      this._expandElement.setIconType('smallicon-triangle-bottom');
+    else
+      this._expandElement.setIconType('smallicon-triangle-right');
+  }
+
   updateTitle() {
     this._updateState();
-    this._expandElement = createElement('span');
-    this._expandElement.className = 'expand-element';
+    this._expandElement = UI.Icon.create('smallicon-triangle-right', 'expand-icon');
 
     var propertyRenderer =
         new Elements.StylesSidebarPropertyRenderer(this._style.parentRule, this.node(), this.name, this.value);
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/stylesSectionTree.css b/third_party/WebKit/Source/devtools/front_end/elements/stylesSectionTree.css
index 090ef54..592cb40 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/stylesSectionTree.css
+++ b/third_party/WebKit/Source/devtools/front_end/elements/stylesSectionTree.css
@@ -179,30 +179,15 @@
     margin-left: 0 !important;
 }
 
-:host-context(.matched-styles) .tree-outline li.parent .expand-element {
+.expand-icon {
     -webkit-user-select: none;
-    -webkit-mask-image: url(Images/toolbarButtonGlyphs.png);
-    -webkit-mask-size: 352px 168px;
-    margin-right: 2px;
-    margin-left: -6px;
     background-color: #777;
-    width: 8px;
-    height: 10px;
-    display: inline-block;
+    margin-left: -6px;
+    margin-right: 2px;
 }
 
-@media (-webkit-min-device-pixel-ratio: 1.1) {
-:host-context(.matched-styles) .tree-outline li.parent .expand-element {
-    -webkit-mask-image: url(Images/toolbarButtonGlyphs_2x.png);
-}
-} /* media */
-
-:host-context(.matched-styles) .tree-outline li.parent .expand-element {
-    -webkit-mask-position: -4px -96px;
-}
-
-:host-context(.matched-styles) .tree-outline li.parent.expanded .expand-element {
-    -webkit-mask-position: -20px -96px;
+.tree-outline li:not(.parent) .expand-icon {
+    display: none;
 }
 
 :host-context(.matched-styles:not(.read-only):hover) .enabled-button {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js b/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
index 200b2994..a7a57169 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/BlockedURLsPane.js
@@ -200,7 +200,8 @@
     countElement.title = Common.UIString(
         count === 1 ? '%d request blocked by this pattern' : '%d requests blocked by this pattern', count);
 
-    var removeButton = element.createChild('div', 'remove-button');
+    var removeButton = UI.Icon.create('smallicon-cross', 'remove-icon');
+    element.appendChild(removeButton);
     removeButton.title = Common.UIString('Remove');
     removeButton.addEventListener('click', this._removeBlockedURL.bind(this, index), false);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css b/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css
index 280be41..0e20e389 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css
+++ b/third_party/WebKit/Source/devtools/front_end/network/blockedURLsPane.css
@@ -63,28 +63,15 @@
     margin-right: 5px;
 }
 
-.blocked-url .remove-button {
-    width: 13px;
-    height: 13px;
-    background-image: url(Images/toolbarButtonGlyphs.png);
-    background-size: 352px 168px;
-    background-position: -175px -96px;
-    visibility: hidden;
-    flex: none;
+.remove-icon {
     opacity: 0.7;
-    cursor: default;
+    visibility: hidden;
 }
 
-@media (-webkit-min-device-pixel-ratio: 1.1) {
-.blocked-url .remove-button {
-    background-image: url(Images/toolbarButtonGlyphs_2x.png);
+.blocked-url .remove-icon:hover {
+    opacity: 1.0;
 }
-} /* media */
 
-.blocked-url:hover .remove-button {
+.blocked-url:hover .remove-icon {
     visibility: visible;
 }
-
-.blocked-url .remove-button:hover {
-    opacity: 1.0;
-}
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
index 41232ae..45cd812 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
@@ -113,6 +113,7 @@
   'smallicon-triangle-right': {x: -4, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true},
   'smallicon-triangle-bottom': {x: -20, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true},
   'smallicon-arrow-in-circle': {x: -10, y: -127, width: 11, height: 11, spritesheet: 'largeicons', isMask: true},
+  'smallicon-cross': {x: -177, y: -98, width: 10, height: 10, spritesheet: 'largeicons'},
   'smallicon-inline-breakpoint': {x: -140, y: -20, width: 10, height: 10, spritesheet: 'smallicons'},
   'smallicon-inline-breakpoint-conditional': {x: -160, y: -20, width: 10, height: 10, spritesheet: 'smallicons'},
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css b/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
index 46c5ad6..cedc661 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
@@ -337,6 +337,11 @@
     -webkit-mask-size: 190px 30px;
 }
 
+.spritesheet-largeicons {
+    background-image: -webkit-image-set(url(Images/toolbarButtonGlyphs.png) 1x, url(Images/toolbarButtonGlyphs_2x.png) 2x);
+    background-size: 352px 168px;
+}
+
 .spritesheet-largeicons.icon-mask {
     -webkit-mask-image: -webkit-image-set(url(Images/toolbarButtonGlyphs.png) 1x, url(Images/toolbarButtonGlyphs_2x.png) 2x);
     -webkit-mask-size: 352px 168px;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
index 9c141b51..bc0a87f 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -904,33 +904,28 @@
   return AccessibilityOrientationUndefined;
 }
 
-static String queryString(WebLocalizedString::Name name) {
-  return Locale::defaultLocale().queryString(name);
-}
-
-String AXObject::actionVerb() const {
+AXSupportedAction AXObject::action() const {
   if (!actionElement())
-    return emptyString();
+    return AXSupportedAction::None;
 
   switch (roleValue()) {
     case ButtonRole:
     case ToggleButtonRole:
-      return queryString(WebLocalizedString::AXButtonActionVerb);
+      return AXSupportedAction::Press;
     case TextFieldRole:
-      return queryString(WebLocalizedString::AXTextFieldActionVerb);
+      return AXSupportedAction::Activate;
     case RadioButtonRole:
-      return queryString(WebLocalizedString::AXRadioButtonActionVerb);
+      return AXSupportedAction::Select;
     case CheckBoxRole:
     case SwitchRole:
-      return queryString(
-          isChecked() ? WebLocalizedString::AXCheckedCheckBoxActionVerb
-                      : WebLocalizedString::AXUncheckedCheckBoxActionVerb);
+      return isChecked() ? AXSupportedAction::Check
+                         : AXSupportedAction::Uncheck;
     case LinkRole:
-      return queryString(WebLocalizedString::AXLinkActionVerb);
+      return AXSupportedAction::Jump;
     case PopUpButtonRole:
-      return queryString(WebLocalizedString::AXPopUpButtonActionVerb);
+      return AXSupportedAction::Open;
     default:
-      return queryString(WebLocalizedString::AXDefaultActionVerb);
+      return AXSupportedAction::Click;
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h
index 45b7a1a..4f65993d 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -245,6 +245,18 @@
   DefaultBehavior,
 };
 
+enum class AXSupportedAction {
+  None = 0,
+  Activate,
+  Check,
+  Click,
+  Jump,
+  Open,
+  Press,
+  Select,
+  Uncheck
+};
+
 enum AccessibilityButtonState {
   ButtonStateOff = 0,
   ButtonStateOn,
@@ -795,7 +807,7 @@
   virtual void wordBoundaries(Vector<AXRange>&) const {}
 
   // Properties of interactive elements.
-  String actionVerb() const;
+  AXSupportedAction action() const;
   virtual AccessibilityButtonState checkboxOrRadioValue() const;
   virtual AriaCurrentState ariaCurrentState() const {
     return AriaCurrentStateUndefined;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLCompressedTextureETC1.cpp b/third_party/WebKit/Source/modules/webgl/WebGLCompressedTextureETC1.cpp
index 9b708b75..f12d9ae 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLCompressedTextureETC1.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLCompressedTextureETC1.cpp
@@ -25,12 +25,8 @@
 
 bool WebGLCompressedTextureETC1::supported(WebGLRenderingContextBase* context) {
   Extensions3DUtil* extensionsUtil = context->extensionsUtil();
-  bool webgl1 = !context->isWebGL2OrHigher();
-  bool etc1 =
-      extensionsUtil->supportsExtension("GL_OES_compressed_ETC1_RGB8_texture");
-  bool etc =
-      extensionsUtil->supportsExtension("GL_CHROMIUM_compressed_texture_etc");
-  return (webgl1 || etc) && etc1;
+  return extensionsUtil->supportsExtension(
+      "GL_OES_compressed_ETC1_RGB8_texture");
 }
 
 const char* WebGLCompressedTextureETC1::extensionName() {
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 9685a9a..0a49563d 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1179,7 +1179,6 @@
     "scheduler/base/intrusive_heap.h",
     "scheduler/base/lazy_now.cc",
     "scheduler/base/lazy_now.h",
-    "scheduler/base/moveable_auto_lock.h",
     "scheduler/base/pollable_thread_safe_flag.cc",
     "scheduler/base/pollable_thread_safe_flag.h",
     "scheduler/base/queueing_time_estimator.cc",
diff --git a/third_party/WebKit/Source/platform/graphics/skia/SkiaUtils.cpp b/third_party/WebKit/Source/platform/graphics/skia/SkiaUtils.cpp
index d154e48c..a450200 100644
--- a/third_party/WebKit/Source/platform/graphics/skia/SkiaUtils.cpp
+++ b/third_party/WebKit/Source/platform/graphics/skia/SkiaUtils.cpp
@@ -55,7 +55,7 @@
 // Keep this array in sync with the WebBlendMode enum in
 // public/platform/WebBlendMode.h.
 static const SkBlendMode gMapBlendOpsToXfermodeModes[] = {
-    SkBlendMode::kClear,       // WebBlendModeNormal
+    SkBlendMode::kSrcOver,     // WebBlendModeNormal
     SkBlendMode::kMultiply,    // WebBlendModeMultiply
     SkBlendMode::kScreen,      // WebBlendModeScreen
     SkBlendMode::kOverlay,     // WebBlendModeOverlay
diff --git a/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h b/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h
deleted file mode 100644
index ac2b8a8..0000000
--- a/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h
+++ /dev/null
@@ -1,41 +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.
-
-#ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_MOVEABLE_AUTO_LOCK_H_
-#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_MOVEABLE_AUTO_LOCK_H_
-
-#include "base/synchronization/lock.h"
-
-namespace blink {
-namespace scheduler {
-
-class MoveableAutoLock {
- public:
-  explicit MoveableAutoLock(base::Lock& lock) : lock_(lock), moved_(false) {
-    lock_.Acquire();
-  }
-
-  explicit MoveableAutoLock(MoveableAutoLock&& other)
-      : lock_(other.lock_), moved_(other.moved_) {
-    lock_.AssertAcquired();
-    other.moved_ = true;
-  }
-
-  ~MoveableAutoLock() {
-    if (moved_)
-      return;
-    lock_.AssertAcquired();
-    lock_.Release();
-  }
-
- private:
-  base::Lock& lock_;
-  bool moved_;
-  DISALLOW_COPY_AND_ASSIGN(MoveableAutoLock);
-};
-
-}  // namespace scheduler
-}  // namespace blink
-
-#endif  // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_MOVEABLE_AUTO_LOCK_H_
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
index 8614d7a..80f18b99 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
@@ -46,23 +46,23 @@
   task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, now, delay);
 }
 
-base::Optional<base::TimeDelta> RealTimeDomain::DelayTillNextTask(
-    LazyNow* lazy_now) {
+bool RealTimeDomain::MaybeAdvanceTime() {
   base::TimeTicks next_run_time;
   if (!NextScheduledRunTime(&next_run_time))
-    return base::Optional<base::TimeDelta>();
+    return false;
 
-  base::TimeTicks now = lazy_now->Now();
+  base::TimeTicks now = Now();
   if (now >= next_run_time)
-    return base::TimeDelta();  // Makes DoWork post an immediate continuation.
+    return true;  // Causes DoWork to post a continuation.
 
   base::TimeDelta delay = next_run_time - now;
-  TRACE_EVENT1(tracing_category_, "RealTimeDomain::DelayTillNextTask",
+  TRACE_EVENT1(tracing_category_, "RealTimeDomain::MaybeAdvanceTime",
                "delay_ms", delay.InMillisecondsF());
 
-  // The next task is sometime in the future. DoWork will make sure it gets
-  // run at the right time..
-  return delay;
+  // The next task is sometime in the future, make sure we schedule a DoWork to
+  // run it.
+  task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, now, delay);
+  return false;
 }
 
 void RealTimeDomain::AsValueIntoInternal(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
index 6883599..94d8a59 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
@@ -23,7 +23,7 @@
   // TimeDomain implementation:
   LazyNow CreateLazyNow() const override;
   base::TimeTicks Now() const override;
-  base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+  bool MaybeAdvanceTime() override;
   const char* GetName() const override;
 
  protected:
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
index 2885ccd..8ad3792 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
@@ -154,11 +154,8 @@
     TimeDomain* time_domain)
     : task_queue_manager(task_queue_manager),
       time_domain(time_domain),
-      delayed_work_queue(
-          new WorkQueue(task_queue, "delayed", WorkQueue::QueueType::DELAYED)),
-      immediate_work_queue(new WorkQueue(task_queue,
-                                         "immediate",
-                                         WorkQueue::QueueType::IMMEDIATE)),
+      delayed_work_queue(new WorkQueue(task_queue, "delayed")),
+      immediate_work_queue(new WorkQueue(task_queue, "immediate")),
       set_index(0),
       is_enabled_refcount(0),
       voter_refcount(0),
@@ -186,6 +183,7 @@
 }
 
 bool TaskQueueImpl::RunsTasksOnCurrentThread() const {
+  base::AutoLock lock(any_thread_lock_);
   return base::PlatformThread::CurrentId() == thread_id_;
 }
 
@@ -327,49 +325,26 @@
     base::TimeTicks desired_run_time,
     EnqueueOrder sequence_number,
     bool nestable) {
+  if (any_thread().immediate_incoming_queue.empty())
+    any_thread().time_domain->RegisterAsUpdatableTaskQueue(this);
   // If the |immediate_incoming_queue| is empty we need a DoWork posted to make
   // it run.
   if (any_thread().immediate_incoming_queue.empty()) {
-    // There's no point posting a DoWork for a blocked or disabled queue,
-    // although we can only determine that on the main thread.
-    bool ensure_do_work_posted = !RunsTasksOnCurrentThread() ||
-                                 (IsQueueEnabled() && !BlockedByFenceLocked());
-    any_thread().task_queue_manager->OnQueueHasImmediateWork(
-        this, ensure_do_work_posted);
-    any_thread().time_domain->OnQueueHasImmediateWork(this);
+    // There's no point posting a DoWork for a disabled queue, however we can
+    // only tell if it's disabled from the main thread.
+    if (base::PlatformThread::CurrentId() == thread_id_) {
+      if (IsQueueEnabled() && !BlockedByFenceLocked())
+        any_thread().task_queue_manager->MaybeScheduleImmediateWork(FROM_HERE);
+    } else {
+      any_thread().task_queue_manager->MaybeScheduleImmediateWork(FROM_HERE);
+    }
   }
-
   any_thread().immediate_incoming_queue.emplace(
-      posted_from, task, desired_run_time, sequence_number, nestable,
-      sequence_number);
-  any_thread().task_queue_manager->DidQueueTask(
-      any_thread().immediate_incoming_queue.back());
+      posted_from, task, desired_run_time, sequence_number, nestable, sequence_number);
+  any_thread().task_queue_manager->DidQueueTask( any_thread().immediate_incoming_queue.back());
   TraceQueueSize(true);
 }
 
-void TaskQueueImpl::ReloadImmediateWorkQueueIfEmpty() {
-  if (!main_thread_only().immediate_work_queue->Empty())
-    return;
-
-  base::AutoLock lock(any_thread_lock_);
-  if (any_thread().immediate_incoming_queue.empty())
-    return;
-
-  main_thread_only().immediate_work_queue->SwapLocked(
-      any_thread().immediate_incoming_queue);
-}
-
-void TaskQueueImpl::OnImmediateWorkQueueHasBecomeEmpty(
-    std::queue<TaskQueueImpl::Task>* work_queue) {
-  base::AutoLock lock(any_thread_lock_);
-  DCHECK(work_queue->empty());
-
-  if (any_thread().immediate_incoming_queue.empty())
-    return;
-
-  std::swap(any_thread().immediate_incoming_queue, *work_queue);
-}
-
 bool TaskQueueImpl::IsEmpty() const {
   if (!main_thread_only().delayed_work_queue->Empty() ||
       !main_thread_only().delayed_incoming_queue.empty() ||
@@ -419,8 +394,7 @@
   return main_thread_only().delayed_incoming_queue.top().delayed_run_time;
 }
 
-base::Optional<base::TimeTicks> TaskQueueImpl::WakeUpForDelayedWork(
-    LazyNow* lazy_now) {
+void TaskQueueImpl::WakeUpForDelayedWork(LazyNow* lazy_now) {
   // Enqueue all delayed tasks that should be running now, skipping any that
   // have been canceled.
   while (!main_thread_only().delayed_incoming_queue.empty()) {
@@ -439,10 +413,25 @@
   }
 
   // Make sure the next wake up is scheduled.
-  if (!main_thread_only().delayed_incoming_queue.empty())
-    return main_thread_only().delayed_incoming_queue.top().delayed_run_time;
+  if (!main_thread_only().delayed_incoming_queue.empty()) {
+    main_thread_only().time_domain->ScheduleDelayedWork(
+        this, main_thread_only().delayed_incoming_queue.top().delayed_run_time,
+        lazy_now->Now());
+  }
+}
 
-  return base::Optional<base::TimeTicks>();
+bool TaskQueueImpl::MaybeUpdateImmediateWorkQueues() {
+  if (!main_thread_only().task_queue_manager)
+    return false;
+
+  if (!main_thread_only().immediate_work_queue->Empty())
+    return true;
+
+  base::AutoLock lock(any_thread_lock_);
+  main_thread_only().immediate_work_queue->SwapLocked(
+      any_thread().immediate_incoming_queue);
+  // |immediate_work_queue| is now empty so updates are no longer required.
+  return false;
 }
 
 void TaskQueueImpl::TraceQueueSize(bool is_locked) const {
@@ -585,7 +574,8 @@
 
     any_thread().time_domain = time_domain;
   }
-
+  // We rely here on TimeDomain::MigrateQueue being thread-safe to use with
+  // TimeDomain::Register/UnregisterAsUpdatableTaskQueue.
   main_thread_only().time_domain->MigrateQueue(this, time_domain);
   main_thread_only().time_domain = time_domain;
 }
@@ -681,8 +671,6 @@
 }
 
 bool TaskQueueImpl::BlockedByFenceLocked() const {
-  any_thread_lock_.AssertAcquired();
-
   if (!main_thread_only().current_fence)
     return false;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
index 814c3eb0..55bfa458 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
@@ -139,16 +139,9 @@
   const char* GetName() const override;
   QueueType GetQueueType() const override;
 
-  // As BlockedByFence but only safe to be called while |any_thread_| is locked.
-  // Must only be called from the thread this task queue was created on.
-  bool BlockedByFenceLocked() const;
-
-  // Must only be called from the thread this task queue was created on.
-  void OnImmediateWorkQueueHasBecomeEmpty(
-      std::queue<TaskQueueImpl::Task>* work_queue);
-
-  // Must only be called from the thread this task queue was created on.
-  void ReloadImmediateWorkQueueIfEmpty();
+  // If this returns false then future updates for this queue are not needed
+  // unless requested.
+  bool MaybeUpdateImmediateWorkQueues();
 
   void AsValueInto(base::trace_event::TracedValue* state) const;
 
@@ -179,9 +172,9 @@
   }
 
   // Enqueues any delayed tasks which should be run now on the
-  // |delayed_work_queue|. Returns the deadline if a subsequent wakeup is
-  // required. Must be called from the main thread.
-  base::Optional<base::TimeTicks> WakeUpForDelayedWork(LazyNow* lazy_now);
+  // |delayed_work_queue|. It also schedules the next wake up with the
+  // TimeDomain. Must be called from the main thread.
+  void WakeUpForDelayedWork(LazyNow* lazy_now);
 
   base::TimeTicks scheduled_time_domain_wakeup() const {
     return main_thread_only().scheduled_time_domain_wakeup;
@@ -297,6 +290,9 @@
       EnqueueOrder sequence_number,
       bool nestable);
 
+  // As BlockedByFence but safe to be called while locked.
+  bool BlockedByFenceLocked() const;
+
   void TraceQueueSize(bool is_locked) const;
   static void QueueAsValueInto(const std::queue<Task>& queue,
                                base::trace_event::TracedValue* state);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index 252f086..74a311b1 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -58,7 +58,7 @@
     : real_time_domain_(new RealTimeDomain(tracing_category)),
       delegate_(delegate),
       task_was_run_on_quiescence_monitored_queue_(false),
-      record_task_delay_histograms_(true),
+      other_thread_pending_wakeup_(false),
       work_batch_size_(1),
       task_count_(0),
       tracing_category_(tracing_category),
@@ -75,10 +75,12 @@
                                      "TaskQueueManager", this);
   selector_.SetTaskQueueSelectorObserver(this);
 
-  delayed_do_work_closure_ =
-      base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), true);
-  immediate_do_work_closure_ =
-      base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), false);
+  from_main_thread_immediate_do_work_closure_ =
+      base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(),
+                 base::TimeTicks(), true);
+  from_other_thread_immediate_do_work_closure_ =
+      base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(),
+                 base::TimeTicks(), false);
 
   // TODO(alexclarke): Change this to be a parameter that's passed in.
   RegisterTimeDomain(real_time_domain_.get());
@@ -98,11 +100,6 @@
   delegate_->RemoveNestingObserver(this);
 }
 
-TaskQueueManager::AnyThread::AnyThread()
-    : do_work_running_count(0),
-      immediate_do_work_posted_count(0),
-      is_nested(false) {}
-
 void TaskQueueManager::RegisterTimeDomain(TimeDomain* time_domain) {
   time_domains_.insert(time_domain);
   time_domain->OnRegisterWithTaskQueueManager(this);
@@ -148,72 +145,45 @@
   queues_.erase(task_queue);
 
   selector_.RemoveQueue(task_queue.get());
-
-  {
-    base::AutoLock lock(any_thread_lock_);
-    any_thread().has_incoming_immediate_work.erase(task_queue.get());
-  }
 }
 
-void TaskQueueManager::UpdateWorkQueues(LazyNow* lazy_now) {
+void TaskQueueManager::UpdateWorkQueues(LazyNow lazy_now) {
+  TRACE_EVENT0(disabled_by_default_tracing_category_,
+               "TaskQueueManager::UpdateWorkQueues");
+
   for (TimeDomain* time_domain : time_domains_) {
-    if (time_domain == real_time_domain_.get()) {
-      time_domain->WakeupReadyDelayedQueues(lazy_now);
-      continue;
-    }
-    LazyNow time_domain_lazy_now = time_domain->CreateLazyNow();
-    time_domain->WakeupReadyDelayedQueues(&time_domain_lazy_now);
+    LazyNow lazy_now_in_domain = time_domain == real_time_domain_.get()
+                                     ? lazy_now
+                                     : time_domain->CreateLazyNow();
+    time_domain->UpdateWorkQueues(lazy_now_in_domain);
   }
 }
 
 void TaskQueueManager::OnBeginNestedMessageLoop() {
   // We just entered a nested message loop, make sure there's a DoWork posted or
   // the system will grind to a halt.
-  {
-    base::AutoLock lock(any_thread_lock_);
-    any_thread().immediate_do_work_posted_count++;
-    any_thread().is_nested = true;
-  }
-  delegate_->PostTask(FROM_HERE, immediate_do_work_closure_);
-}
-
-void TaskQueueManager::OnQueueHasImmediateWork(internal::TaskQueueImpl* queue,
-                                               bool ensure_do_work_posted) {
-  MoveableAutoLock lock(any_thread_lock_);
-  any_thread().has_incoming_immediate_work.insert(queue);
-  if (ensure_do_work_posted)
-    MaybeScheduleImmediateWorkLocked(FROM_HERE, std::move(lock));
-}
-
-void TaskQueueManager::NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked() {
-  for (internal::TaskQueueImpl* queue :
-       any_thread().has_incoming_immediate_work) {
-    queue->ReloadImmediateWorkQueueIfEmpty();
-  }
-  any_thread().has_incoming_immediate_work.clear();
+  delegate_->PostTask(FROM_HERE, from_main_thread_immediate_do_work_closure_);
 }
 
 void TaskQueueManager::MaybeScheduleImmediateWork(
     const tracked_objects::Location& from_here) {
-  MoveableAutoLock lock(any_thread_lock_);
-  MaybeScheduleImmediateWorkLocked(from_here, std::move(lock));
-}
-
-void TaskQueueManager::MaybeScheduleImmediateWorkLocked(
-    const tracked_objects::Location& from_here,
-    MoveableAutoLock&& lock) {
-  {
-    MoveableAutoLock auto_lock(std::move(lock));
-    // Unless we're nested, try to avoid posting redundant DoWorks.
-    if (!any_thread().is_nested &&
-        (any_thread().do_work_running_count == 1 ||
-         any_thread().immediate_do_work_posted_count > 0)) {
+  bool on_main_thread = delegate_->BelongsToCurrentThread();
+  // De-duplicate DoWork posts.
+  if (on_main_thread) {
+    if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) {
       return;
     }
-
-    any_thread().immediate_do_work_posted_count++;
+    delegate_->PostTask(from_here, from_main_thread_immediate_do_work_closure_);
+  } else {
+    {
+      base::AutoLock lock(other_thread_lock_);
+      if (other_thread_pending_wakeup_)
+        return;
+      other_thread_pending_wakeup_ = true;
+    }
+    delegate_->PostTask(from_here,
+                        from_other_thread_immediate_do_work_closure_);
   }
-  delegate_->PostTask(from_here, immediate_do_work_closure_);
 }
 
 void TaskQueueManager::MaybeScheduleDelayedWork(
@@ -222,74 +192,54 @@
     base::TimeDelta delay) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   DCHECK_GE(delay, base::TimeDelta());
-  {
-    base::AutoLock lock(any_thread_lock_);
 
-    // Unless we're nested, don't post a delayed DoWork if there's an immediate
-    // DoWork in flight or we're inside a DoWork. We can rely on DoWork posting
-    // a delayed continuation as needed.
-    if (!any_thread().is_nested &&
-        (any_thread().immediate_do_work_posted_count > 0 ||
-         any_thread().do_work_running_count == 1)) {
-      return;
-    }
+  // If there's a pending immediate DoWork then we rely on
+  // TryAdvanceTimeDomains getting the TimeDomain to call
+  // MaybeScheduleDelayedWork again when the immediate DoWork is complete.
+  if (main_thread_pending_wakeups_.find(base::TimeTicks()) !=
+      main_thread_pending_wakeups_.end()) {
+    return;
   }
-
   // De-duplicate DoWork posts.
   base::TimeTicks run_time = now + delay;
-  if (next_delayed_do_work_ <= run_time && !next_delayed_do_work_.is_null())
+  if (!main_thread_pending_wakeups_.empty() &&
+      *main_thread_pending_wakeups_.begin() <= run_time) {
     return;
-
-  TRACE_EVENT1(tracing_category_, "MaybeScheduleDelayedWorkInternal",
-               "delay_ms", delay.InMillisecondsF());
-
-  cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
-  next_delayed_do_work_ = run_time;
+  }
+  main_thread_pending_wakeups_.insert(run_time);
   delegate_->PostDelayedTask(
-      from_here, cancelable_delayed_do_work_closure_.callback(), delay);
+      from_here, base::Bind(&TaskQueueManager::DoWork,
+                            weak_factory_.GetWeakPtr(), run_time, true),
+      delay);
 }
 
-void TaskQueueManager::DoWork(bool delayed) {
+void TaskQueueManager::DoWork(base::TimeTicks run_time, bool from_main_thread) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
-  TRACE_EVENT1("tracing_category_", "TaskQueueManager::DoWork", "delayed",
-               delayed);
-  LazyNow lazy_now(real_time_domain()->CreateLazyNow());
+  TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork",
+               "from_main_thread", from_main_thread);
 
-  bool is_nested = delegate_->IsNested();
-  if (!is_nested)
+  if (from_main_thread) {
+    main_thread_pending_wakeups_.erase(run_time);
+  } else {
+    base::AutoLock lock(other_thread_lock_);
+    other_thread_pending_wakeup_ = false;
+  }
+
+  // Posting a DoWork while a DoWork is running leads to spurious DoWorks.
+  main_thread_pending_wakeups_.insert(base::TimeTicks());
+
+  if (!delegate_->IsNested())
     queues_to_delete_.clear();
 
-  // This must be done before running any tasks because they could invoke a
-  // nested message loop and we risk having a stale |next_delayed_do_work_|.
-  if (delayed)
-    next_delayed_do_work_ = base::TimeTicks();
+  LazyNow lazy_now(real_time_domain()->CreateLazyNow());
+  UpdateWorkQueues(lazy_now);
 
   for (int i = 0; i < work_batch_size_; i++) {
-    {
-      base::AutoLock lock(any_thread_lock_);
-      any_thread().is_nested = is_nested;
-      DCHECK_EQ(any_thread().is_nested, delegate_->IsNested());
-
-      if (i == 0) {
-        any_thread().do_work_running_count++;
-
-        if (!delayed) {
-          any_thread().immediate_do_work_posted_count--;
-          DCHECK_GE(any_thread().immediate_do_work_posted_count, 0);
-        }
-      }
-
-      NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked();
-    }
-
-    UpdateWorkQueues(&lazy_now);
-
-    internal::WorkQueue* work_queue = nullptr;
+    internal::WorkQueue* work_queue;
     if (!SelectWorkQueueToService(&work_queue))
       break;
 
-    // NB this may unregister the queue.
-    switch (ProcessTaskFromWorkQueue(work_queue, is_nested, &lazy_now)) {
+    switch (ProcessTaskFromWorkQueue(work_queue, &lazy_now)) {
       case ProcessTaskResult::DEFERRED:
         // If a task was deferred, try again with another task.
         continue;
@@ -299,111 +249,31 @@
         return;  // The TaskQueueManager got deleted, we must bail out.
     }
 
+    work_queue = nullptr;  // The queue may have been unregistered.
+
+    UpdateWorkQueues(lazy_now);
+
     // Only run a single task per batch in nested run loops so that we can
     // properly exit the nested loop when someone calls RunLoop::Quit().
-    if (is_nested)
+    if (delegate_->IsNested())
       break;
   }
 
+  main_thread_pending_wakeups_.erase(base::TimeTicks());
+
   // TODO(alexclarke): Consider refactoring the above loop to terminate only
   // when there's no more work left to be done, rather than posting a
   // continuation task.
-
-  {
-    MoveableAutoLock lock(any_thread_lock_);
-    base::Optional<base::TimeDelta> next_delay =
-        ComputeDelayTillNextTaskLocked(&lazy_now);
-
-    any_thread().do_work_running_count--;
-    DCHECK_GE(any_thread().do_work_running_count, 0);
-
-    any_thread().is_nested = is_nested;
-    DCHECK_EQ(any_thread().is_nested, delegate_->IsNested());
-
-    PostDoWorkContinuationLocked(next_delay, &lazy_now, std::move(lock));
-  }
+  if (!selector_.EnabledWorkQueuesEmpty() || TryAdvanceTimeDomains())
+    MaybeScheduleImmediateWork(FROM_HERE);
 }
 
-void TaskQueueManager::PostDoWorkContinuationLocked(
-    base::Optional<base::TimeDelta> next_delay,
-    LazyNow* lazy_now,
-    MoveableAutoLock&& lock) {
-  base::TimeDelta delay;
-
-  {
-    MoveableAutoLock auto_lock(std::move(lock));
-
-    // If there are no tasks left then we don't need to post a continuation.
-    if (!next_delay) {
-      // If there's a pending delayed DoWork, cancel it because it's not needed.
-      if (!next_delayed_do_work_.is_null()) {
-        next_delayed_do_work_ = base::TimeTicks();
-        cancelable_delayed_do_work_closure_.Cancel();
-      }
-      return;
-    }
-
-    // If an immediate DoWork is posted, we don't need to post a continuation.
-    if (any_thread().immediate_do_work_posted_count > 0)
-      return;
-
-    delay = next_delay.value();
-
-    // This isn't supposed to happen, but in case it does convert to
-    // non-delayed.
-    if (delay < base::TimeDelta())
-      delay = base::TimeDelta();
-
-    if (delay.is_zero()) {
-      // If a delayed DoWork is pending then we don't need to post a
-      // continuation because it should run immediately.
-      if (!next_delayed_do_work_.is_null() &&
-          next_delayed_do_work_ <= lazy_now->Now()) {
-        return;
-      }
-
-      any_thread().immediate_do_work_posted_count++;
-    } else {
-      base::TimeTicks run_time = lazy_now->Now() + delay;
-      if (next_delayed_do_work_ == run_time)
-        return;
-
-      next_delayed_do_work_ = run_time;
-    }
-  }
-
-  // We avoid holding |any_thread_lock_| while posting the task.
-  if (delay.is_zero()) {
-    delegate_->PostTask(FROM_HERE, immediate_do_work_closure_);
-  } else {
-    cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
-    delegate_->PostDelayedTask(
-        FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay);
-  }
-}
-
-base::Optional<base::TimeDelta>
-TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) {
-  NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked();
-
-  // If the selector has non-empty queues we trivially know there is immediate
-  // word to be done.
-  if (!selector_.EnabledWorkQueuesEmpty())
-    return base::TimeDelta();
-
-  UpdateWorkQueues(lazy_now);
-
-  // Otherwise we need to find the shortest delay, if any.
-  base::Optional<base::TimeDelta> next_continuation;
+bool TaskQueueManager::TryAdvanceTimeDomains() {
+  bool can_advance = false;
   for (TimeDomain* time_domain : time_domains_) {
-    base::Optional<base::TimeDelta> continuation =
-        time_domain->DelayTillNextTask(lazy_now);
-    if (!continuation)
-      continue;
-    if (!next_continuation || next_continuation.value() > continuation.value())
-      next_continuation = continuation;
+    can_advance |= time_domain->MaybeAdvanceTime();
   }
-  return next_continuation;
+  return can_advance;
 }
 
 bool TaskQueueManager::SelectWorkQueueToService(
@@ -422,7 +292,6 @@
 
 TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue(
     internal::WorkQueue* work_queue,
-    bool is_nested,
     LazyNow* lazy_now) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   scoped_refptr<DeletionSentinel> protect(deletion_sentinel_);
@@ -437,7 +306,7 @@
   if (queue->GetQuiescenceMonitored())
     task_was_run_on_quiescence_monitored_queue_ = true;
 
-  if (!pending_task.nestable && is_nested) {
+  if (!pending_task.nestable && delegate_->IsNested()) {
     // Defer non-nestable work to the main task runner.  NOTE these tasks can be
     // arbitrarily delayed so the additional delay should not be a problem.
     // TODO(skyostil): Figure out a way to not forget which task queue the
@@ -450,8 +319,7 @@
     return ProcessTaskResult::DEFERRED;
   }
 
-  if (record_task_delay_histograms_)
-    MaybeRecordTaskDelayHistograms(pending_task, queue);
+  MaybeRecordTaskDelayHistograms(pending_task, queue);
 
   double task_start_time = 0;
   TRACE_TASK_EXECUTION("TaskQueueManager::ProcessTaskFromWorkQueue",
@@ -602,22 +470,13 @@
   for (auto* time_domain : time_domains_)
     time_domain->AsValueInto(state.get());
   state->EndArray();
-
-  {
-    base::AutoLock lock(any_thread_lock_);
-    state->SetBoolean("is_nested", any_thread().is_nested);
-    state->SetInteger("do_work_running_count",
-                      any_thread().do_work_running_count);
-    state->SetInteger("immediate_do_work_posted_count",
-                      any_thread().immediate_do_work_posted_count);
-  }
   return std::move(state);
 }
 
 void TaskQueueManager::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   // Only schedule DoWork if there's something to do.
-  if (queue->HasPendingImmediateWork() && !queue->BlockedByFence())
+  if (queue->HasPendingImmediateWork())
     MaybeScheduleImmediateWork(FROM_HERE);
 }
 
@@ -635,11 +494,5 @@
   return !selector_.EnabledWorkQueuesEmpty();
 }
 
-void TaskQueueManager::SetRecordTaskDelayHistograms(
-    bool record_task_delay_histograms) {
-  DCHECK(main_thread_checker_.CalledOnValidThread());
-  record_task_delay_histograms_ = record_task_delay_histograms;
-}
-
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
index cd5df92..0fbfa7e 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -8,7 +8,6 @@
 #include <map>
 
 #include "base/atomic_sequence_num.h"
-#include "base/cancelable_callback.h"
 #include "base/debug/task_annotator.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -17,7 +16,6 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
 #include "platform/scheduler/base/enqueue_order.h"
-#include "platform/scheduler/base/moveable_auto_lock.h"
 #include "platform/scheduler/base/task_queue_impl.h"
 #include "platform/scheduler/base/task_queue_selector.h"
 
@@ -142,10 +140,6 @@
   // Returns true if there is a task that could be executed immediately.
   bool HasImmediateWorkForTesting() const;
 
-  // There is a small overhead to recording task delay histograms. If you don't
-  // need them, you can turn them off.
-  void SetRecordTaskDelayHistograms(bool record_task_delay_histograms);
-
  private:
   friend class LazyNow;
   friend class internal::TaskQueueImpl;
@@ -158,6 +152,9 @@
   };
 
   // Unregisters a TaskQueue previously created by |NewTaskQueue()|.
+  // NOTE we have to flush the queue from |newly_updatable_| which means as a
+  // side effect MoveNewlyUpdatableQueuesIntoUpdatableQueueSet is called by this
+  // function.
   void UnregisterTaskQueue(scoped_refptr<internal::TaskQueueImpl> task_queue);
 
   // TaskQueueSelector::Observer implementation:
@@ -172,16 +169,11 @@
   void DidQueueTask(const internal::TaskQueueImpl::Task& pending_task);
 
   // Use the selector to choose a pending task and run it.
-  void DoWork(bool delayed);
-
-  // Post a DoWork continuation if |next_delay| is not empty.
-  void PostDoWorkContinuationLocked(base::Optional<base::TimeDelta> next_delay,
-                                    LazyNow* lazy_now,
-                                    MoveableAutoLock&& lock);
+  void DoWork(base::TimeTicks run_time, bool from_main_thread);
 
   // Delayed Tasks with run_times <= Now() are enqueued onto the work queue and
   // reloads any empty work queues.
-  void UpdateWorkQueues(LazyNow* lazy_now);
+  void UpdateWorkQueues(LazyNow lazy_now);
 
   // Chooses the next work queue to service. Returns true if |out_queue|
   // indicates the queue from which the next task should be run, false to
@@ -197,8 +189,7 @@
     TASK_QUEUE_MANAGER_DELETED
   };
   ProcessTaskResult ProcessTaskFromWorkQueue(internal::WorkQueue* work_queue,
-                                             bool is_nested,
-                                             LazyNow* lazy_now);
+                                             LazyNow*);
 
   bool RunsTasksOnCurrentThread() const;
   bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
@@ -207,10 +198,9 @@
 
   internal::EnqueueOrder GetNextSequenceNumber();
 
-  // Calls DelayTillNextTask on all time domains and returns the smallest delay
-  // requested if any.
-  base::Optional<base::TimeDelta> ComputeDelayTillNextTaskLocked(
-      LazyNow* lazy_now);
+  // Calls MaybeAdvanceTime on all time domains and returns true if one of them
+  // was able to advance.
+  bool TryAdvanceTimeDomains();
 
   void MaybeRecordTaskDelayHistograms(
       const internal::TaskQueueImpl::Task& pending_task,
@@ -220,17 +210,6 @@
   AsValueWithSelectorResult(bool should_run,
                             internal::WorkQueue* selected_work_queue) const;
 
-  void MaybeScheduleImmediateWorkLocked(
-      const tracked_objects::Location& from_here,
-      MoveableAutoLock&& lock);
-
-  // Adds |queue| to |has_incoming_immediate_work_| and if
-  // |ensure_do_work_posted| is true it calls MaybeScheduleImmediateWorkLocked.
-  void OnQueueHasImmediateWork(internal::TaskQueueImpl* queue,
-                               bool ensure_do_work_posted);
-
-  void NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked();
-
   std::set<TimeDomain*> time_domains_;
   std::unique_ptr<RealTimeDomain> real_time_domain_;
 
@@ -247,35 +226,18 @@
   scoped_refptr<TaskQueueManagerDelegate> delegate_;
   internal::TaskQueueSelector selector_;
 
-  base::Closure immediate_do_work_closure_;
-  base::Closure delayed_do_work_closure_;
+  base::Closure from_main_thread_immediate_do_work_closure_;
+  base::Closure from_other_thread_immediate_do_work_closure_;
 
   bool task_was_run_on_quiescence_monitored_queue_;
 
-  struct AnyThread {
-    AnyThread();
+  // To reduce locking overhead we track pending calls to DoWork separately for
+  // the main thread and other threads.
+  std::set<base::TimeTicks> main_thread_pending_wakeups_;
 
-    int do_work_running_count;
-    int immediate_do_work_posted_count;
-    std::set<internal::TaskQueueImpl*> has_incoming_immediate_work;
-    bool is_nested;  // Whether or not the message loop is currently nested.
-  };
-
-  mutable base::Lock any_thread_lock_;
-  AnyThread any_thread_;
-  struct AnyThread& any_thread() {
-    any_thread_lock_.AssertAcquired();
-    return any_thread_;
-  }
-  const struct AnyThread& any_thread() const {
-    any_thread_lock_.AssertAcquired();
-    return any_thread_;
-  }
-
-  bool record_task_delay_histograms_;
-
-  base::TimeTicks next_delayed_do_work_;
-  base::CancelableClosure cancelable_delayed_do_work_closure_;
+  // Protects |other_thread_pending_wakeup_|.
+  mutable base::Lock other_thread_lock_;
+  bool other_thread_pending_wakeup_;
 
   int work_batch_size_;
   size_t task_count_;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
index a7f7454..b0f03a6 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -32,14 +32,13 @@
   PerfTestTimeDomain() : VirtualTimeDomain(nullptr, base::TimeTicks::Now()) {}
   ~PerfTestTimeDomain() override {}
 
-  base::Optional<base::TimeDelta> DelayTillNextTask(
-      LazyNow* lazy_now) override {
+  bool MaybeAdvanceTime() override {
     base::TimeTicks run_time;
     if (!NextScheduledRunTime(&run_time))
-      return base::Optional<base::TimeDelta>();
+      return false;
 
     AdvanceTo(run_time);
-    return base::TimeDelta();  // Makes DoWork post an immediate continuation.
+    return true;
   }
 
   void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
index 88d9331..97ef5e95 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -117,34 +117,7 @@
   }
 
   void UpdateWorkQueues(LazyNow lazy_now) {
-    manager_->UpdateWorkQueues(&lazy_now);
-  }
-
-  base::Optional<base::TimeDelta> ComputeDelayTillNextTask(LazyNow* lazy_now) {
-    base::AutoLock lock(manager_->any_thread_lock_);
-    return manager_->ComputeDelayTillNextTaskLocked(lazy_now);
-  }
-
-  void PostDoWorkContinuation(base::Optional<base::TimeDelta> next_delay,
-                              LazyNow* lazy_now) {
-    MoveableAutoLock lock(manager_->any_thread_lock_);
-    return manager_->PostDoWorkContinuationLocked(next_delay, lazy_now,
-                                                  std::move(lock));
-  }
-
-  int immediate_do_work_posted_count() const {
-    base::AutoLock lock(manager_->any_thread_lock_);
-    return manager_->any_thread().immediate_do_work_posted_count;
-  }
-
-  base::TimeTicks next_delayed_do_work() const {
-    return manager_->next_delayed_do_work_;
-  }
-
-  void MaybeScheduleImmediateWorkLocked(
-      const tracked_objects::Location& from_here) {
-    MoveableAutoLock lock(manager_->any_thread_lock_);
-    manager_->MaybeScheduleImmediateWorkLocked(from_here, std::move(lock));
+    manager_->UpdateWorkQueues(lazy_now);
   }
 
   // Runs all immediate tasks until there is no more work to do and advances
@@ -516,34 +489,6 @@
   EXPECT_THAT(run_order, ElementsAre(1));
 }
 
-TEST_F(TaskQueueManagerTest, RemovingFenceForDisabledQueueDoesNotPostDoWork) {
-  Initialize(1u);
-
-  std::vector<EnqueueOrder> run_order;
-  std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
-      runners_[0]->CreateQueueEnabledVoter();
-  voter->SetQueueEnabled(false);
-  runners_[0]->InsertFence(TaskQueue::InsertFencePosition::NOW);
-  runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order));
-
-  runners_[0]->RemoveFence();
-  EXPECT_FALSE(test_task_runner_->HasPendingTasks());
-}
-
-TEST_F(TaskQueueManagerTest, EnablingFencedQueueDoesNotPostDoWork) {
-  Initialize(1u);
-
-  std::vector<EnqueueOrder> run_order;
-  std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
-      runners_[0]->CreateQueueEnabledVoter();
-  voter->SetQueueEnabled(false);
-  runners_[0]->InsertFence(TaskQueue::InsertFencePosition::NOW);
-  runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order));
-
-  voter->SetQueueEnabled(true);
-  EXPECT_FALSE(test_task_runner_->HasPendingTasks());
-}
-
 TEST_F(TaskQueueManagerTest, DenyRunning_BeforePosting) {
   Initialize(1u);
 
@@ -2217,199 +2162,5 @@
   voter.reset();
 }
 
-TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask) {
-  Initialize(2u);
-
-  std::unique_ptr<RealTimeDomain> domain2(new RealTimeDomain("test"));
-  manager_->RegisterTimeDomain(domain2.get());
-  runners_[1]->SetTimeDomain(domain2.get());
-
-  LazyNow lazy_now(now_src_.get());
-  EXPECT_FALSE(static_cast<bool>(ComputeDelayTillNextTask(&lazy_now)));
-
-  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(10));
-
-  EXPECT_EQ(base::TimeDelta::FromSeconds(10),
-            ComputeDelayTillNextTask(&lazy_now).value());
-
-  runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(15));
-
-  EXPECT_EQ(base::TimeDelta::FromSeconds(10),
-            ComputeDelayTillNextTask(&lazy_now).value());
-
-  runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(5));
-
-  EXPECT_EQ(base::TimeDelta::FromSeconds(5),
-            ComputeDelayTillNextTask(&lazy_now).value());
-
-  runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask));
-
-  EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now).value());
-
-  // Tidy up.
-  runners_[1]->UnregisterTaskQueue();
-  manager_->UnregisterTimeDomain(domain2.get());
-}
-
-TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_NoMoreWork) {
-  Initialize(1u);
-
-  LazyNow lazy_now(now_src_.get());
-  PostDoWorkContinuation(base::Optional<base::TimeDelta>(), &lazy_now);
-
-  EXPECT_EQ(0u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(0, immediate_do_work_posted_count());
-  EXPECT_TRUE(next_delayed_do_work().is_null());
-}
-
-TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_ImmediateWork) {
-  Initialize(1u);
-
-  LazyNow lazy_now(now_src_.get());
-  PostDoWorkContinuation(base::TimeDelta(), &lazy_now);
-
-  EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(1, immediate_do_work_posted_count());
-  EXPECT_TRUE(next_delayed_do_work().is_null());
-}
-
-TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWork) {
-  Initialize(1u);
-
-  LazyNow lazy_now(now_src_.get());
-  PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now);
-
-  EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(base::TimeDelta::FromSeconds(1),
-            test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(0, immediate_do_work_posted_count());
-  EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(1),
-            next_delayed_do_work());
-}
-
-TEST_F(TaskQueueManagerTest,
-       PostDoWorkContinuation_DelayedWorkButImmediateDoWorkAlreadyPosted) {
-  Initialize(1u);
-
-  MaybeScheduleImmediateWorkLocked(FROM_HERE);
-  EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(1, immediate_do_work_posted_count());
-
-  LazyNow lazy_now(now_src_.get());
-  PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now);
-
-  // Test that a delayed task didn't get posted.
-  EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(1, immediate_do_work_posted_count());
-  EXPECT_TRUE(next_delayed_do_work().is_null());
-}
-
-TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWorkTimeChanges) {
-  Initialize(1u);
-
-  LazyNow lazy_now(now_src_.get());
-  PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now);
-
-  EXPECT_TRUE(test_task_runner_->HasPendingTasks());
-  EXPECT_EQ(0, immediate_do_work_posted_count());
-  EXPECT_EQ(base::TimeDelta::FromSeconds(1),
-            test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(1),
-            next_delayed_do_work());
-
-  PostDoWorkContinuation(base::TimeDelta::FromSeconds(10), &lazy_now);
-
-  // This should have resulted in the previous task getting canceled and a new
-  // one getting posted.
-  EXPECT_EQ(2u, test_task_runner_->NumPendingTasks());
-  test_task_runner_->RemoveCancelledTasks();
-  EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(base::TimeDelta::FromSeconds(10),
-            test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(0, immediate_do_work_posted_count());
-  EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(10),
-            next_delayed_do_work());
-}
-
-TEST_F(TaskQueueManagerTest,
-       PostDoWorkContinuation_ImmediateWorkButDelayedDoWorkPending) {
-  Initialize(1u);
-
-  LazyNow lazy_now(now_src_.get());
-  PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now);
-
-  now_src_->Advance(base::TimeDelta::FromSeconds(1));
-  lazy_now = LazyNow(now_src_.get());
-  PostDoWorkContinuation(base::TimeDelta(), &lazy_now);
-
-  // Because the delayed DoWork was pending we don't expect an immediate DoWork
-  // to get posted.
-  EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
-  EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(0, immediate_do_work_posted_count());
-  EXPECT_EQ(lazy_now.Now(), next_delayed_do_work());
-}
-
-namespace {
-void MessageLoopTaskWithDelayedQuit(
-    base::MessageLoop* message_loop,
-    base::SimpleTestTickClock* now_src,
-    scoped_refptr<internal::TaskQueueImpl> task_queue) {
-  base::MessageLoop::ScopedNestableTaskAllower allow(message_loop);
-  base::RunLoop run_loop;
-  task_queue->PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
-                              base::TimeDelta::FromMilliseconds(100));
-  now_src->Advance(base::TimeDelta::FromMilliseconds(200));
-  run_loop.Run();
-}
-}  // namespace
-
-TEST_F(TaskQueueManagerTest, DelayedTaskRunsInNestedMessageLoop) {
-  InitializeWithRealMessageLoop(1u);
-  base::RunLoop run_loop;
-  runners_[0]->PostTask(
-      FROM_HERE,
-      base::Bind(&MessageLoopTaskWithDelayedQuit, message_loop_.get(),
-                 now_src_.get(), base::RetainedRef(runners_[0])));
-  run_loop.RunUntilIdle();
-}
-
-namespace {
-void MessageLoopTaskWithImmediateQuit(
-    base::MessageLoop* message_loop,
-    base::Closure non_nested_quit_closure,
-    scoped_refptr<internal::TaskQueueImpl> task_queue) {
-  base::MessageLoop::ScopedNestableTaskAllower allow(message_loop);
-
-  base::RunLoop run_loop;
-  // Needed because entering the nested message loop causes a DoWork to get
-  // posted.
-  task_queue->PostTask(FROM_HERE, base::Bind(&NopTask));
-  task_queue->PostTask(FROM_HERE, run_loop.QuitClosure());
-  run_loop.Run();
-  non_nested_quit_closure.Run();
-}
-}  // namespace
-
-TEST_F(TaskQueueManagerTest,
-       DelayedNestedMessageLoopDoesntPreventTasksRunning) {
-  InitializeWithRealMessageLoop(1u);
-  base::RunLoop run_loop;
-  runners_[0]->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&MessageLoopTaskWithImmediateQuit, message_loop_.get(),
-                 run_loop.QuitClosure(), base::RetainedRef(runners_[0])),
-      base::TimeDelta::FromMilliseconds(100));
-
-  now_src_->Advance(base::TimeDelta::FromMilliseconds(200));
-  run_loop.Run();
-}
-
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector.cc
index 0a24636c..d7c5c29 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector.cc
@@ -163,14 +163,12 @@
         WorkQueue** out_work_queue) const {
   WorkQueue* immediate_queue;
   DCHECK_EQ(*out_chose_delayed_over_immediate, false);
-  EnqueueOrder immediate_enqueue_order;
-  if (immediate_work_queue_sets_.GetOldestQueueAndEnqueueOrderInSet(
-          priority, &immediate_queue, &immediate_enqueue_order)) {
+  if (immediate_work_queue_sets_.GetOldestQueueInSet(priority,
+                                                     &immediate_queue)) {
     WorkQueue* delayed_queue;
-    EnqueueOrder delayed_enqueue_order;
-    if (delayed_work_queue_sets_.GetOldestQueueAndEnqueueOrderInSet(
-            priority, &delayed_queue, &delayed_enqueue_order)) {
-      if (immediate_enqueue_order < delayed_enqueue_order) {
+    if (delayed_work_queue_sets_.GetOldestQueueInSet(priority,
+                                                     &delayed_queue)) {
+      if (immediate_queue->ShouldRunBefore(delayed_queue)) {
         *out_work_queue = immediate_queue;
       } else {
         *out_chose_delayed_over_immediate = true;
@@ -191,9 +189,13 @@
   // Select an immediate work queue if we are starving immediate tasks.
   if (task_queue_selector_->immediate_starvation_count_ >=
       kMaxDelayedStarvationTasks) {
-    if (ChooseOldestImmediateTaskWithPriority(priority, out_work_queue))
+    if (ChooseOldestImmediateTaskWithPriority(priority, out_work_queue)) {
       return true;
-    return ChooseOldestDelayedTaskWithPriority(priority, out_work_queue);
+    }
+    if (ChooseOldestDelayedTaskWithPriority(priority, out_work_queue)) {
+      return true;
+    }
+    return false;
   }
   return ChooseOldestImmediateOrDelayedTaskWithPriority(
       priority, out_chose_delayed_over_immediate, out_work_queue);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
index d326318..59e3e34 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
@@ -27,6 +27,7 @@
 void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   DCHECK_EQ(queue->GetTimeDomain(), this);
+  UnregisterAsUpdatableTaskQueue(queue);
 
   // If no wakeup has been requested then bail out.
   if (!queue->heap_handle().IsValid())
@@ -44,6 +45,11 @@
   DCHECK_EQ(queue->GetTimeDomain(), this);
   DCHECK(destination_time_domain);
 
+  // Make sure we remember to update |queue| if it's got incoming immediate
+  // work.
+  if (UnregisterAsUpdatableTaskQueue(queue))
+    destination_time_domain->updatable_queue_set_.insert(queue);
+
   // If no wakeup has been requested then bail out.
   if (!queue->heap_handle().IsValid())
     return;
@@ -88,11 +94,66 @@
     observer_->OnTimeDomainHasDelayedWork(queue);
 }
 
-void TimeDomain::OnQueueHasImmediateWork(internal::TaskQueueImpl* queue) {
+void TimeDomain::RegisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue) {
+  {
+    base::AutoLock lock(newly_updatable_lock_);
+    newly_updatable_.push_back(queue);
+  }
   if (observer_)
     observer_->OnTimeDomainHasImmediateWork(queue);
 }
 
+bool TimeDomain::UnregisterAsUpdatableTaskQueue(
+    internal::TaskQueueImpl* queue) {
+  DCHECK(main_thread_checker_.CalledOnValidThread());
+
+  bool was_updatable = updatable_queue_set_.erase(queue) != 0;
+
+  base::AutoLock lock(newly_updatable_lock_);
+  // Remove all copies of |queue| from |newly_updatable_|.
+  for (size_t i = 0; i < newly_updatable_.size();) {
+    if (newly_updatable_[i] == queue) {
+      // Move last element into slot #i and then compact.
+      newly_updatable_[i] = newly_updatable_.back();
+      newly_updatable_.pop_back();
+      was_updatable = true;
+    } else {
+      i++;
+    }
+  }
+  return was_updatable;
+}
+
+void TimeDomain::UpdateWorkQueues(LazyNow lazy_now) {
+  DCHECK(main_thread_checker_.CalledOnValidThread());
+
+  // Move any ready delayed tasks into the Incoming queues.
+  WakeupReadyDelayedQueues(&lazy_now);
+
+  MoveNewlyUpdatableQueuesIntoUpdatableQueueSet();
+
+  std::set<internal::TaskQueueImpl*>::iterator iter =
+      updatable_queue_set_.begin();
+  while (iter != updatable_queue_set_.end()) {
+    std::set<internal::TaskQueueImpl*>::iterator queue_it = iter++;
+    internal::TaskQueueImpl* queue = *queue_it;
+
+    // Update the queue and remove from the set if subsequent updates are not
+    // required.
+    if (!queue->MaybeUpdateImmediateWorkQueues())
+      updatable_queue_set_.erase(queue_it);
+  }
+}
+
+void TimeDomain::MoveNewlyUpdatableQueuesIntoUpdatableQueueSet() {
+  DCHECK(main_thread_checker_.CalledOnValidThread());
+  base::AutoLock lock(newly_updatable_lock_);
+  while (!newly_updatable_.empty()) {
+    updatable_queue_set_.insert(newly_updatable_.back());
+    newly_updatable_.pop_back();
+  }
+}
+
 void TimeDomain::WakeupReadyDelayedQueues(LazyNow* lazy_now) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   // Wake up any queues with pending delayed work.  Note std::multipmap stores
@@ -101,18 +162,10 @@
   while (!delayed_wakeup_queue_.empty() &&
          delayed_wakeup_queue_.min().time <= lazy_now->Now()) {
     internal::TaskQueueImpl* queue = delayed_wakeup_queue_.min().queue;
-    base::Optional<base::TimeTicks> next_wakeup =
-        queue->WakeUpForDelayedWork(lazy_now);
+    // O(log n)
+    delayed_wakeup_queue_.pop();
 
-    if (next_wakeup) {
-      // O(log n)
-      delayed_wakeup_queue_.ChangeKey(queue->heap_handle(),
-                                      {next_wakeup.value(), queue});
-      queue->set_scheduled_time_domain_wakeup(next_wakeup.value());
-    } else {
-      // O(log n)
-      delayed_wakeup_queue_.pop();
-    }
+    queue->WakeUpForDelayedWork(lazy_now);
   }
 }
 
@@ -137,6 +190,10 @@
 void TimeDomain::AsValueInto(base::trace_event::TracedValue* state) const {
   state->BeginDictionary();
   state->SetString("name", GetName());
+  state->BeginArray("updatable_queue_set");
+  for (auto* queue : updatable_queue_set_)
+    state->AppendString(queue->GetName());
+  state->EndArray();
   state->SetInteger("registered_delay_count", delayed_wakeup_queue_.size());
   if (!delayed_wakeup_queue_.empty()) {
     base::TimeDelta delay = delayed_wakeup_queue_.min().time - Now();
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
index 4947fd5..639d174 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
@@ -62,11 +62,9 @@
   // Evaluate this TimeDomain's Now. Can be called from any thread.
   virtual base::TimeTicks Now() const = 0;
 
-  // Computes the delay until the next task the TimeDomain is aware of, if any.
-  // Note virtual time domains may return base::TimeDelta() if they have any
-  // tasks that are eligible to run.
-  virtual base::Optional<base::TimeDelta> DelayTillNextTask(
-      LazyNow* lazy_now) = 0;
+  // Some TimeDomains support virtual time, this method tells us to advance time
+  // if possible and return true if time was advanced.
+  virtual bool MaybeAdvanceTime() = 0;
 
   // Returns the name of this time domain for tracing.
   virtual const char* GetName() const = 0;
@@ -90,6 +88,10 @@
   // the next task was posted to and it returns true.  Returns false otherwise.
   bool NextScheduledTaskQueue(TaskQueue** out_task_queue) const;
 
+  // Adds |queue| to the set of task queues that UpdateWorkQueues calls
+  // UpdateWorkQueue on.
+  void RegisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue);
+
   // Schedules a call to TaskQueueImpl::WakeUpForDelayedWork when this
   // TimeDomain reaches |delayed_run_time|.  This supersedes any previously
   // registered wakeup for |queue|.
@@ -100,12 +102,15 @@
   // Registers the |queue|.
   void RegisterQueue(internal::TaskQueueImpl* queue);
 
+  // Removes |queue| from the set of task queues that UpdateWorkQueues calls
+  // UpdateWorkQueue on. Returns true if |queue| was updatable.
+  bool UnregisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue);
+
   // Removes |queue| from all internal data structures.
   void UnregisterQueue(internal::TaskQueueImpl* queue);
 
-  // Tells the time domain that |queue| went from having no immediate work to
-  // having some.
-  void OnQueueHasImmediateWork(internal::TaskQueueImpl* queue);
+  // Updates active queues associated with this TimeDomain.
+  void UpdateWorkQueues(LazyNow lazy_now);
 
   // Called by the TaskQueueManager when the TimeDomain is registered.
   virtual void OnRegisterWithTaskQueueManager(
@@ -131,6 +136,8 @@
   }
 
  private:
+  void MoveNewlyUpdatableQueuesIntoUpdatableQueueSet();
+
   struct DelayedWakeup {
     base::TimeTicks time;
     internal::TaskQueueImpl* queue;
@@ -157,6 +164,15 @@
 
   IntrusiveHeap<DelayedWakeup> delayed_wakeup_queue_;
 
+  // This lock guards only |newly_updatable_|.  It's not expected to be heavily
+  // contended.
+  base::Lock newly_updatable_lock_;
+  std::vector<internal::TaskQueueImpl*> newly_updatable_;
+
+  // Set of task queues with avaliable work on the incoming queue.  This should
+  // only be accessed from the main thread.
+  std::set<internal::TaskQueueImpl*> updatable_queue_set_;
+
   Observer* observer_;  // NOT OWNED.
 
   base::ThreadChecker main_thread_checker_;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
index 09a253c..69224757 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
@@ -34,8 +34,8 @@
   using TimeDomain::NextScheduledTaskQueue;
   using TimeDomain::ScheduleDelayedWork;
   using TimeDomain::UnregisterQueue;
-  using TimeDomain::OnQueueHasImmediateWork;
-  using TimeDomain::WakeupReadyDelayedQueues;
+  using TimeDomain::UpdateWorkQueues;
+  using TimeDomain::RegisterAsUpdatableTaskQueue;
 
   // TimeSource implementation:
   LazyNow CreateLazyNow() const override { return LazyNow(now_); }
@@ -44,10 +44,7 @@
   void AsValueIntoInternal(
       base::trace_event::TracedValue* state) const override {}
 
-  base::Optional<base::TimeDelta> DelayTillNextTask(
-      LazyNow* lazy_now) override {
-    return base::Optional<base::TimeDelta>();
-  }
+  bool MaybeAdvanceTime() override { return false; }
   const char* GetName() const override { return "Test"; }
   void OnRegisterWithTaskQueueManager(
       TaskQueueManager* task_queue_manager) override {}
@@ -194,7 +191,7 @@
   EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
 }
 
-TEST_F(TimeDomainTest, WakeupReadyDelayedQueues) {
+TEST_F(TimeDomainTest, UpdateWorkQueues) {
   base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50);
   EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay));
   base::TimeTicks now = time_domain_->Now();
@@ -206,13 +203,13 @@
   EXPECT_EQ(delayed_runtime, next_run_time);
 
   LazyNow lazy_now = time_domain_->CreateLazyNow();
-  time_domain_->WakeupReadyDelayedQueues(&lazy_now);
+  time_domain_->UpdateWorkQueues(lazy_now);
   ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
   EXPECT_EQ(delayed_runtime, next_run_time);
 
   time_domain_->SetNow(delayed_runtime);
   lazy_now = time_domain_->CreateLazyNow();
-  time_domain_->WakeupReadyDelayedQueues(&lazy_now);
+  time_domain_->UpdateWorkQueues(lazy_now);
   ASSERT_FALSE(time_domain_->NextScheduledRunTime(&next_run_time));
 }
 
@@ -238,7 +235,7 @@
 
 TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasImmediateWork) {
   EXPECT_CALL(*observer_, OnTimeDomainHasImmediateWork(task_queue_.get()));
-  time_domain_->OnQueueHasImmediateWork(task_queue_.get());
+  time_domain_->RegisterAsUpdatableTaskQueue(task_queue_.get());
 }
 
 TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasDelayedWork) {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
index f40c6307..a22796c5d 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
@@ -41,9 +41,8 @@
   // needed.
 }
 
-base::Optional<base::TimeDelta> VirtualTimeDomain::DelayTillNextTask(
-    LazyNow* lazy_now) {
-  return base::Optional<base::TimeDelta>();
+bool VirtualTimeDomain::MaybeAdvanceTime() {
+  return false;
 }
 
 void VirtualTimeDomain::AsValueIntoInternal(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
index a841226..715948d 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
@@ -21,7 +21,7 @@
   // TimeDomain implementation:
   LazyNow CreateLazyNow() const override;
   base::TimeTicks Now() const override;
-  base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+  bool MaybeAdvanceTime() override;
   const char* GetName() const override;
 
   // Advances this time domain to |now|. NOTE |now| is supposed to be
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue.cc b/third_party/WebKit/Source/platform/scheduler/base/work_queue.cc
index ac312f9..fe16858 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue.cc
@@ -10,15 +10,12 @@
 namespace scheduler {
 namespace internal {
 
-WorkQueue::WorkQueue(TaskQueueImpl* task_queue,
-                     const char* name,
-                     QueueType queue_type)
+WorkQueue::WorkQueue(TaskQueueImpl* task_queue, const char* name)
     : work_queue_sets_(nullptr),
       task_queue_(task_queue),
       work_queue_set_index_(0),
       name_(name),
-      fence_(0),
-      queue_type_(queue_type) {}
+      fence_(0) {}
 
 void WorkQueue::AsValueInto(base::trace_event::TracedValue* state) const {
   // Remove const to search |work_queue_| in the destructive manner. Restore the
@@ -119,10 +116,6 @@
   TaskQueueImpl::Task pending_task =
       std::move(const_cast<TaskQueueImpl::Task&>(work_queue_.front()));
   work_queue_.pop();
-  // NB immediate tasks have a different pipeline to delayed ones.
-  if (queue_type_ == QueueType::IMMEDIATE && work_queue_.empty())
-    task_queue_->OnImmediateWorkQueueHasBecomeEmpty(&work_queue_);
-  // OnPopQueue checks BlockedByFence() so we don't need to here.
   work_queue_sets_->OnPopQueue(this);
   task_queue_->TraceQueueSize(false);
   return pending_task;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue.h b/third_party/WebKit/Source/platform/scheduler/base/work_queue.h
index 9fedd55..998fc62d 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue.h
@@ -31,9 +31,7 @@
 // throttling mechanisms.
 class BLINK_PLATFORM_EXPORT WorkQueue {
  public:
-  enum class QueueType { DELAYED, IMMEDIATE };
-
-  WorkQueue(TaskQueueImpl* task_queue, const char* name, QueueType queue_type);
+  WorkQueue(TaskQueueImpl* task_queue, const char* name);
   ~WorkQueue();
 
   // Associates this work queue with the given work queue sets. This must be
@@ -122,7 +120,6 @@
   HeapHandle heap_handle_;
   const char* name_;
   EnqueueOrder fence_;
-  QueueType queue_type_;
 
   DISALLOW_COPY_AND_ASSIGN(WorkQueue);
 };
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.cc b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.cc
index 8c608b0..bec08d1 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.cc
@@ -63,8 +63,6 @@
   size_t set_index = work_queue->work_queue_set_index();
   DCHECK_LT(set_index, work_queue_heaps_.size()) << " set_index = "
                                                  << set_index;
-  // |work_queue| should not be in work_queue_heaps_[set_index].
-  DCHECK(!work_queue->heap_handle().IsValid());
   work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
 }
 
@@ -83,8 +81,6 @@
   } else {
     // O(log n)
     work_queue_heaps_[set_index].pop();
-    DCHECK(work_queue_heaps_[set_index].empty() ||
-           work_queue_heaps_[set_index].min().value != work_queue);
   }
 }
 
@@ -107,18 +103,6 @@
   return true;
 }
 
-bool WorkQueueSets::GetOldestQueueAndEnqueueOrderInSet(
-    size_t set_index,
-    WorkQueue** out_work_queue,
-    EnqueueOrder* out_enqueue_order) const {
-  DCHECK_LT(set_index, work_queue_heaps_.size());
-  if (work_queue_heaps_[set_index].empty())
-    return false;
-  *out_work_queue = work_queue_heaps_[set_index].min().value;
-  *out_enqueue_order = work_queue_heaps_[set_index].min().key;
-  return true;
-}
-
 bool WorkQueueSets::IsSetEmpty(size_t set_index) const {
   DCHECK_LT(set_index, work_queue_heaps_.size()) << " set_index = "
                                                  << set_index;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.h b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.h
index 23668ff..7f80725 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets.h
@@ -56,12 +56,6 @@
   bool GetOldestQueueInSet(size_t set_index, WorkQueue** out_work_queue) const;
 
   // O(1)
-  bool GetOldestQueueAndEnqueueOrderInSet(
-      size_t set_index,
-      WorkQueue** out_work_queue,
-      EnqueueOrder* out_enqueue_order) const;
-
-  // O(1)
   bool IsSetEmpty(size_t set_index) const;
 
 #if DCHECK_IS_ON() || !defined(NDEBUG)
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc
index b340d7a..4f8b081 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc
@@ -35,8 +35,7 @@
   };
 
   WorkQueue* NewTaskQueue(const char* queue_name) {
-    WorkQueue* queue =
-        new WorkQueue(nullptr, "test", WorkQueue::QueueType::IMMEDIATE);
+    WorkQueue* queue = new WorkQueue(nullptr, "test");
     work_queues_.push_back(base::WrapUnique(queue));
     work_queue_sets_->AddQueue(queue, TaskQueue::CONTROL_PRIORITY);
     return queue;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc
index 2c111e2..caff182 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc
@@ -28,8 +28,7 @@
         new TaskQueueImpl(nullptr, time_domain_.get(),
                           TaskQueue::Spec(TaskQueue::QueueType::TEST), "", ""));
 
-    work_queue_.reset(new WorkQueue(task_queue_.get(), "test",
-                                    WorkQueue::QueueType::IMMEDIATE));
+    work_queue_.reset(new WorkQueue(task_queue_.get(), "test"));
     work_queue_sets_.reset(new WorkQueueSets(1, "test"));
     work_queue_sets_->AddQueue(work_queue_.get(), 0);
 
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
index 0bbbcbc1..5870d2f 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
@@ -18,19 +18,6 @@
     const char* tracing_category,
     const char* disabled_by_default_tracing_category,
     const char* disabled_by_default_verbose_tracing_category)
-    : SchedulerHelper(task_queue_manager_delegate,
-                      tracing_category,
-                      disabled_by_default_tracing_category,
-                      disabled_by_default_verbose_tracing_category,
-                      TaskQueue::Spec(TaskQueue::QueueType::DEFAULT)
-                          .SetShouldMonitorQuiescence(true)) {}
-
-SchedulerHelper::SchedulerHelper(
-    scoped_refptr<SchedulerTqmDelegate> task_queue_manager_delegate,
-    const char* tracing_category,
-    const char* disabled_by_default_tracing_category,
-    const char* disabled_by_default_verbose_tracing_category,
-    TaskQueue::Spec default_task_queue_spec)
     : task_queue_manager_delegate_(task_queue_manager_delegate),
       task_queue_manager_(
           new TaskQueueManager(task_queue_manager_delegate,
@@ -40,7 +27,9 @@
       control_task_runner_(
           NewTaskQueue(TaskQueue::Spec(TaskQueue::QueueType::CONTROL)
                            .SetShouldNotifyObservers(false))),
-      default_task_runner_(NewTaskQueue(default_task_queue_spec)),
+      default_task_runner_(
+          NewTaskQueue(TaskQueue::Spec(TaskQueue::QueueType::DEFAULT)
+                           .SetShouldMonitorQuiescence(true))),
       observer_(nullptr),
       tracing_category_(tracing_category),
       disabled_by_default_tracing_category_(
@@ -66,15 +55,6 @@
   task_queue_manager_delegate_->RestoreDefaultTaskRunner();
 }
 
-void SchedulerHelper::SetRecordTaskDelayHistograms(
-    bool record_task_delay_histograms) {
-  if (!task_queue_manager_)
-    return;
-
-  task_queue_manager_->SetRecordTaskDelayHistograms(
-      record_task_delay_histograms);
-}
-
 scoped_refptr<TaskQueue> SchedulerHelper::NewTaskQueue(
     const TaskQueue::Spec& spec) {
   DCHECK(task_queue_manager_.get());
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
index 67f978b..220ab7a 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
@@ -28,18 +28,8 @@
       const char* tracing_category,
       const char* disabled_by_default_tracing_category,
       const char* disabled_by_default_verbose_tracing_category);
-  SchedulerHelper(
-      scoped_refptr<SchedulerTqmDelegate> task_queue_manager_delegate,
-      const char* tracing_category,
-      const char* disabled_by_default_tracing_category,
-      const char* disabled_by_default_verbose_tracing_category,
-      TaskQueue::Spec default_task_queue_spec);
   ~SchedulerHelper() override;
 
-  // There is a small overhead to recording task delay histograms, we may not
-  // wish to do this on all threads.
-  void SetRecordTaskDelayHistograms(bool record_task_delay_histograms);
-
   // TaskQueueManager::Observer implementation:
   void OnUnregisterTaskQueue(const scoped_refptr<TaskQueue>& queue) override;
   void OnTriedToExecuteBlockedTask(const TaskQueue& queue,
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
index 455a0c2d..a34edb215 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
@@ -14,14 +14,13 @@
 
 AutoAdvancingVirtualTimeDomain::~AutoAdvancingVirtualTimeDomain() {}
 
-base::Optional<base::TimeDelta>
-AutoAdvancingVirtualTimeDomain::DelayTillNextTask(LazyNow* lazy_now) {
+bool AutoAdvancingVirtualTimeDomain::MaybeAdvanceTime() {
   base::TimeTicks run_time;
-  if (!can_advance_virtual_time_ || !NextScheduledRunTime(&run_time))
-    return base::Optional<base::TimeDelta>();
-
+  if (!can_advance_virtual_time_ || !NextScheduledRunTime(&run_time)) {
+    return false;
+  }
   AdvanceTo(run_time);
-  return base::TimeDelta();  // Makes DoWork post an immediate continuation.
+  return true;
 }
 
 void AutoAdvancingVirtualTimeDomain::RequestWakeup(base::TimeTicks now,
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
index 0ca186d..ffd880e 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
@@ -27,7 +27,7 @@
   ~AutoAdvancingVirtualTimeDomain() override;
 
   // TimeDomain implementation:
-  base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+  bool MaybeAdvanceTime() override;
   void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override;
   const char* GetName() const override;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
index db1d088..8f6887c 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
@@ -23,20 +23,19 @@
   // behalf.
 }
 
-base::Optional<base::TimeDelta> ThrottledTimeDomain::DelayTillNextTask(
-    LazyNow* lazy_now) {
+bool ThrottledTimeDomain::MaybeAdvanceTime() {
   base::TimeTicks next_run_time;
   if (!NextScheduledRunTime(&next_run_time))
-    return base::Optional<base::TimeDelta>();
+    return false;
 
-  base::TimeTicks now = lazy_now->Now();
+  base::TimeTicks now = Now();
   if (now >= next_run_time)
-    return base::TimeDelta();  // Makes DoWork post an immediate continuation.
+    return true;  // Causes DoWork to post a continuation.
 
-  // Unlike RealTimeDomain::ContinuationNeeded we don't request a wake up here,
-  // we assume the owner (i.e. TaskQueueThrottler) will manage wakeups on our
+  // Unlike RealTimeDomain::MaybeAdvanceTime we don't request a wake up here, we
+  // assume the owner (i.e. TaskQueueThrottler) will manage wakeups on our
   // behalf.
-  return base::Optional<base::TimeDelta>();
+  return false;
 }
 
 }  // namespace scheduler
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
index c9d73fa..ba59a6c2 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
@@ -22,7 +22,7 @@
   // TimeDomain implementation:
   const char* GetName() const override;
   void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override;
-  base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+  bool MaybeAdvanceTime() override;
 
   using TimeDomain::WakeupReadyDelayedQueues;
 
diff --git a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
index 38f6beb1..fdc093e 100644
--- a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
+++ b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
@@ -334,6 +334,16 @@
 STATIC_ASSERT_ENUM(WebAXStateVertical, AXVerticalState);
 STATIC_ASSERT_ENUM(WebAXStateVisited, AXVisitedState);
 
+STATIC_ASSERT_ENUM(WebAXSupportedAction::None, AXSupportedAction::None);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Activate, AXSupportedAction::Activate);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Check, AXSupportedAction::Check);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Click, AXSupportedAction::Click);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Jump, AXSupportedAction::Jump);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Open, AXSupportedAction::Open);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Press, AXSupportedAction::Press);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Select, AXSupportedAction::Select);
+STATIC_ASSERT_ENUM(WebAXSupportedAction::Uncheck, AXSupportedAction::Uncheck);
+
 STATIC_ASSERT_ENUM(WebAXTextDirectionLR, AccessibilityTextDirectionLTR);
 STATIC_ASSERT_ENUM(WebAXTextDirectionRL, AccessibilityTextDirectionRTL);
 STATIC_ASSERT_ENUM(WebAXTextDirectionTB, AccessibilityTextDirectionTTB);
diff --git a/third_party/WebKit/Source/web/WebAXObject.cpp b/third_party/WebKit/Source/web/WebAXObject.cpp
index b9bb16cb..ebdf64a 100644
--- a/third_party/WebKit/Source/web/WebAXObject.cpp
+++ b/third_party/WebKit/Source/web/WebAXObject.cpp
@@ -133,11 +133,11 @@
   return !isDetached();
 }
 
-WebString WebAXObject::actionVerb() const {
+WebAXSupportedAction WebAXObject::action() const {
   if (isDetached())
-    return WebString();
+    return WebAXSupportedAction::None;
 
-  return m_private->actionVerb();
+  return static_cast<WebAXSupportedAction>(m_private->action());
 }
 
 bool WebAXObject::canDecrement() const {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
index 959f1fc..888ca82 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
@@ -55,8 +55,7 @@
     def _run(self,
              args,
              cwd=None,
-             # pylint: disable=W0622
-             # redefining built-in
+             # pylint: disable=redefined-builtin
              input=None,
              timeout_seconds=None,
              error_handler=None,
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/multiprocessing_bootstrap.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/multiprocessing_bootstrap.py
index 663b920..8e69516 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/multiprocessing_bootstrap.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/multiprocessing_bootstrap.py
@@ -39,7 +39,7 @@
 import subprocess
 import sys
 
-from webkitpy.common import version_check   # 'unused import' pylint: disable=W0611
+from webkitpy.common import version_check  # pylint: disable=unused-import
 
 
 def run(*parts):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py
index 492eb45..30c3a8c7 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py
@@ -292,8 +292,9 @@
 
     def interrupt(self, pid):
         interrupt_signal = signal.SIGINT
-        # FIXME: The python docs seem to imply that platform == 'win32' may need to use signal.CTRL_C_EVENT
-        # http://docs.python.org/2/library/signal.html
+        # Note: The python docs seem to suggest that on Windows, we may want to use
+        # signal.CTRL_C_EVENT (http://docs.python.org/2/library/signal.html), but
+        # it appears that signal.SIGINT also appears to work on Windows.
         try:
             os.kill(pid, interrupt_signal)
         except OSError:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py
index 82ea5018..d5c918a 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py
@@ -98,8 +98,7 @@
                     args,
                     cwd=None,
                     input=None,
-                    # pylint: disable=W0613
-                    # unused argument
+                    # pylint: disable=unused-argument
                     timeout_seconds=None,
                     error_handler=None,
                     return_exit_code=False,
@@ -206,7 +205,7 @@
         assert isinstance(args, list) or isinstance(args, tuple)
         assert all(isinstance(arg, basestring) for arg in args)
         if self._exception:
-            raise self._exception  # pylint: disable=E0702
+            raise self._exception  # pylint: disable=raising-bad-type
         if self._run_command_fn:
             return self._run_command_fn(args)
         if return_exit_code:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
index 7495623..4e0e96e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
@@ -42,7 +42,7 @@
 
 class GenericFileSystemTests(object):
     """Tests that should pass on either a real or mock filesystem."""
-    # pylint gets confused about this being a mixin: pylint: disable=E1101
+    # Pylint gets confused about this being a mixin: pylint: disable=no-member
 
     def setup_generic_test_dir(self):
         fs = self.fs
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations_unittest.py
index 7626442..718dd73e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/lint_test_expectations_unittest.py
@@ -78,10 +78,10 @@
         for port in ports:
             self.ports[port.name] = port
 
-    def get(self, port_name='a', *args, **kwargs):  # pylint: disable=W0613,E0202
+    def get(self, port_name='a', *args, **kwargs):  # pylint: disable=unused-argument,method-hidden
         return self.ports[port_name]
 
-    def all_port_names(self, platform=None):  # pylint: disable=W0613,E0202
+    def all_port_names(self, platform=None):  # pylint: disable=unused-argument,method-hidden
         return sorted(self.ports.keys())
 
 
@@ -109,8 +109,6 @@
         options = optparse.Values({'platform': 'test-mac-mac10.10'})
         host = MockHost()
 
-        # pylint appears to complain incorrectly about the method overrides pylint: disable=E0202,C0322
-        # FIXME: incorrect complaints about spacing pylint: disable=C0322
         host.port_factory.all_port_names = lambda platform=None: [platform]
 
         logger, handler = lint_test_expectations.set_up_logging(logging_stream)
@@ -124,7 +122,6 @@
         options = optparse.Values({'platform': 'test', 'debug_rwt_logging': False})
         host = MockHost()
 
-        # FIXME: incorrect complaints about spacing pylint: disable=C0322
         port = host.port_factory.get(options.platform, options=options)
         port.expectations_dict = lambda: {'foo': '-- syntax error1', 'bar': '-- syntax error2'}
 
@@ -146,7 +143,6 @@
         options = optparse.Values({'platform': 'test', 'debug_rwt_logging': False})
         host = MockHost()
 
-        # FIXME: incorrect complaints about spacing pylint: disable=C0322
         port = host.port_factory.get(options.platform, options=options)
         port.expectations_dict = lambda: {'flag-specific': 'does/not/exist', 'noproblem': ''}
 
@@ -187,7 +183,7 @@
 
 
 class MainTest(unittest.TestCase):
-    # unused args pylint: disable=W0613
+    # pylint: disable=unused-argument
 
     def setUp(self):
         self.orig_lint_fn = lint_test_expectations.lint
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/browser_test.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/browser_test.py
index 0ac9b69..2608f57c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/browser_test.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/browser_test.py
@@ -56,7 +56,7 @@
         """Overridden function from the base port class. Redirects everything
         to src/chrome/test/data/printing/layout_tests.
         """
-        return self.path_from_chromium_base('chrome', 'test', 'data', 'printing', 'layout_tests')  # pylint: disable=E1101
+        return self.path_from_chromium_base('chrome', 'test', 'data', 'printing', 'layout_tests')  # pylint: disable=no-member
 
     def check_sys_deps(self, needs_http):
         """This function is meant to be a no-op since we don't want to actually
@@ -69,7 +69,7 @@
 
     def default_timeout_ms(self):
         timeout_ms = 10 * 1000
-        if self.get_option('configuration') == 'Debug':  # pylint: disable=E1101
+        if self.get_option('configuration') == 'Debug':  # pylint: disable=no-member
             # Debug is usually 2x-3x slower than Release.
             return 3 * timeout_ms
         return timeout_ms
@@ -89,7 +89,7 @@
 
     def default_timeout_ms(self):
         timeout_ms = 20 * 1000
-        if self.get_option('configuration') == 'Debug':  # pylint: disable=E1101
+        if self.get_option('configuration') == 'Debug':  # pylint: disable=no-member
             # Debug is usually 2x-3x slower than Release.
             return 3 * timeout_ms
         return timeout_ms
@@ -99,7 +99,7 @@
 
     def default_timeout_ms(self):
         timeout_ms = 20 * 1000
-        if self.get_option('configuration') == 'Debug':  # pylint: disable=E1101
+        if self.get_option('configuration') == 'Debug':  # pylint: disable=no-member
             # Debug is usually 2x-3x slower than Release.
             return 3 * timeout_ms
         return timeout_ms
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
index 30e2a5a8..5cbd3e1 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
@@ -310,7 +310,7 @@
         port.host.filesystem.write_text_file('/tmp/foo', 'foo')
         port.host.filesystem.write_text_file('/tmp/bar', 'bar')
         ordered_dict = port.expectations_dict()
-        self.assertEqual(ordered_dict.keys()[-2:], options.additional_expectations)  # pylint: disable=E1101
+        self.assertEqual(ordered_dict.keys()[-2:], options.additional_expectations)
         self.assertEqual(ordered_dict.values()[-2:], ['foo', 'bar'])
 
     def test_path_to_apache_config_file(self):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
index 1bfd90e6..1b300d1 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
@@ -1165,7 +1165,7 @@
     def test_exception_handling(self):
         orig_run_fn = run_webkit_tests._run_tests
 
-        # unused args pylint: disable=W0613
+        # pylint: disable=unused-argument
         def interrupting_run(port, options, args, printer):
             raise KeyboardInterrupt
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/server_base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/server_base.py
index f56da73..988d650 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/server_base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/server_base.py
@@ -275,7 +275,7 @@
             except IOError as e:
                 if e.errno in (errno.EALREADY, errno.EADDRINUSE):
                     raise ServerError('Port %d is already in use.' % port)
-                elif self._platform.is_win() and e.errno in (errno.WSAEACCES,):  # pylint: disable=E1101
+                elif self._platform.is_win() and e.errno in (errno.WSAEACCES,):  # pylint: disable=no-member
                     raise ServerError('Port %d is already in use.' % port)
                 else:
                     raise
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/abstract_local_server_command.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/abstract_local_server_command.py
index 91adde1..7da1460 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/abstract_local_server_command.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/abstract_local_server_command.py
@@ -54,5 +54,5 @@
             # FIXME: This seems racy.
             threading.Timer(0.1, lambda: tool.user.open_url(server_url)).start()
 
-        httpd = self.server(httpd_port=options.httpd_port, config=config)  # pylint: disable=E1102
+        httpd = self.server(httpd_port=options.httpd_port, config=config)  # pylint: disable=not-callable
         httpd.serve_forever()
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
index 5439519..31830f7 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
@@ -25,7 +25,7 @@
 
     def setUp(self):
         self.tool = MockWebKitPatch()
-        # lint warns that command_constructor might not be set, but this is intentional; pylint: disable=E1102
+        # Lint warns that command_constructor might not be set, but this is intentional; pylint: disable=not-callable
         self.command = self.command_constructor()
         self.command._tool = self.tool
         self.tool.builders = BuilderList({
diff --git a/third_party/WebKit/public/platform/WebLocalizedString.h b/third_party/WebKit/public/platform/WebLocalizedString.h
index 13ffbbc..766bbdcd 100644
--- a/third_party/WebKit/public/platform/WebLocalizedString.h
+++ b/third_party/WebKit/public/platform/WebLocalizedString.h
@@ -36,18 +36,14 @@
 struct WebLocalizedString {
   enum Name {
     AXAMPMFieldText,
-    AXButtonActionVerb,
     AXCalendarShowMonthSelector,
     AXCalendarShowNextMonth,
     AXCalendarShowPreviousMonth,
     AXCalendarWeekDescription,
-    AXCheckedCheckBoxActionVerb,
     AXDayOfMonthFieldText,
-    AXDefaultActionVerb,
     AXHeadingText,  // Deprecated.
     AXHourFieldText,
     AXImageMapText,  // Deprecated.
-    AXLinkActionVerb,
     AXLinkText,        // Deprecated.
     AXListMarkerText,  // Deprecated.
     AXMediaAudioElement,
@@ -92,11 +88,7 @@
     AXMillisecondFieldText,
     AXMinuteFieldText,
     AXMonthFieldText,
-    AXPopUpButtonActionVerb,
-    AXRadioButtonActionVerb,
     AXSecondFieldText,
-    AXTextFieldActionVerb,
-    AXUncheckedCheckBoxActionVerb,
     AXWebAreaText,  // Deprecated.
     AXWeekOfYearFieldText,
     AXYearFieldText,
diff --git a/third_party/WebKit/public/web/WebAXEnums.h b/third_party/WebKit/public/web/WebAXEnums.h
index 2e326123..20f3e78 100644
--- a/third_party/WebKit/public/web/WebAXEnums.h
+++ b/third_party/WebKit/public/web/WebAXEnums.h
@@ -225,6 +225,18 @@
   WebAXStateVisited,
 };
 
+enum class WebAXSupportedAction {
+  None = 0,
+  Activate,
+  Check,
+  Click,
+  Jump,
+  Open,
+  Press,
+  Select,
+  Uncheck
+};
+
 enum WebAXTextDirection {
   WebAXTextDirectionLR,
   WebAXTextDirectionRL,
diff --git a/third_party/WebKit/public/web/WebAXObject.h b/third_party/WebKit/public/web/WebAXObject.h
index 3666068d..3530215 100644
--- a/third_party/WebKit/public/web/WebAXObject.h
+++ b/third_party/WebKit/public/web/WebAXObject.h
@@ -243,8 +243,7 @@
                             WebVector<int>& ends) const;
 
   // Actions
-  BLINK_EXPORT WebString
-  actionVerb() const;  // The verb corresponding to performDefaultAction.
+  BLINK_EXPORT WebAXSupportedAction action() const;
   BLINK_EXPORT bool canDecrement() const;
   BLINK_EXPORT bool canIncrement() const;
   BLINK_EXPORT bool canPress() const;
diff --git a/third_party/fips181/BUILD.gn b/third_party/fips181/BUILD.gn
index ad95212..9642a327 100644
--- a/third_party/fips181/BUILD.gn
+++ b/third_party/fips181/BUILD.gn
@@ -8,7 +8,6 @@
     "convert.h",
     "fips181.cc",
     "fips181.h",
-    "owntypes.h",
     "randpass.cc",
     "randpass.h",
     "smbl.h",
diff --git a/third_party/fips181/README.chromium b/third_party/fips181/README.chromium
index 93af1b44..83a354b 100644
--- a/third_party/fips181/README.chromium
+++ b/third_party/fips181/README.chromium
@@ -12,5 +12,14 @@
 
 Local Modifications:
 
-pronpass.c and pronpass.h were imported as files fips181.cc and fips181.h
+pronpass.c and pronpass.h were imported as files fips181.cc and fips181.h.
 
+owntypes.h was removed and the calling code changed to use standard C99/C++
+types. (At least 1 typedef (UINT32) conflicted with the same typedef in
+windows.h.) Although the upstream code is vanilla C, bool is available without
+stdbool.h here because we compile the code as C++. Vanilla C users must include
+that header.
+
+Additionally, some variables and a function (is_restricted_symbol) have had
+their types changed from int to bool (they were being used as bools). This
+allows us to build the code warning-free in MSVC (warning C4805).
diff --git a/third_party/fips181/convert.cc b/third_party/fips181/convert.cc
index 00af6a8e..a8e0a259 100644
--- a/third_party/fips181/convert.cc
+++ b/third_party/fips181/convert.cc
@@ -108,7 +108,7 @@
    (void)memcpy((void *)&tmp, (void *)syllable, sizeof(tmp));
    for(i=0; i < 26; i++)
      if ( let[i] == tmp )
-       if (is_restricted_symbol(clet[i]) != TRUE)
+       if (!is_restricted_symbol(clet[i]))
          (void)memcpy ((void *)syllable, (void *)&clet[i], 1);
   }
 }
@@ -225,7 +225,7 @@
    {126, "TILDE"}
   };
  int i = 0;
- int flag = FALSE;
+ bool flag = false;
  
  if (strlen(syllable) == 1)
     {
@@ -234,10 +234,10 @@
        if(*syllable == ssn[i].symbol)
         {
          (void)memcpy((void*)h_syllable, (void*)ssn[i].name, strlen(ssn[i].name));
-	 flag = TRUE;
+	 flag = true;
         }
       }
-     if (flag != TRUE)
+     if (flag != true)
        (void)memcpy((void*)h_syllable, (void*)syllable, strlen(syllable));
     }
 }
diff --git a/third_party/fips181/fips181.cc b/third_party/fips181/fips181.cc
index a7725cbf..826ca794 100644
--- a/third_party/fips181/fips181.cc
+++ b/third_party/fips181/fips181.cc
@@ -50,7 +50,7 @@
 struct unit
 {
     char    unit_code[5];
-    USHORT  flags;
+    unsigned short  flags;
 };
 
 static struct unit  rules[] =
@@ -1255,7 +1255,7 @@
 ** buffer word.  Also, the hyphenated word will be placed into
 ** the buffer hyphenated_word.  Both word and hyphenated_word must
 ** be pre-allocated.  The words generated will have sizes between
-** minlen and maxlen.  If restrict is TRUE, words will not be generated that
+** minlen and maxlen.  If restrict is true, words will not be generated that
 ** appear as login names or as entries in the on-line dictionary.
 ** This algorithm was initially worded out by Morrie Gasser in 1975.
 ** Any changes here are minimal so that as many word combinations
@@ -1266,8 +1266,8 @@
 ** could not be done.
 */
 int
-gen_pron_pass (char *word, char *hyphenated_word, USHORT minlen,
-               USHORT maxlen, unsigned int pass_mode)
+gen_pron_pass (char *word, char *hyphenated_word, unsigned short minlen,
+               unsigned short maxlen, unsigned int pass_mode)
 {
 
     int     pwlen;
@@ -1309,19 +1309,19 @@
  * will be found if the retry limit is adequately large enough.
  */
 int
-gen_word (char *word, char *hyphenated_word, USHORT pwlen, unsigned int pass_mode)
+gen_word (char *word, char *hyphenated_word, unsigned short pwlen, unsigned int pass_mode)
 {
-    USHORT word_length;
-    USHORT syllable_length;
+    unsigned short word_length;
+    unsigned short syllable_length;
     char   *new_syllable;
     char   *syllable_for_hyph;
-    USHORT *syllable_units;
-    USHORT word_size;
-    USHORT word_place;
-    USHORT *word_units;
-    USHORT syllable_size;
-    UINT   tries;
-    int ch_flag = FALSE;
+    unsigned short *syllable_units;
+    unsigned short word_size;
+    unsigned short word_place;
+    unsigned short *word_units;
+    unsigned short syllable_size;
+    unsigned int   tries;
+    bool ch_flag = false;
     int dsd = 0;
 
     /*
@@ -1350,9 +1350,9 @@
      * explicit rule limits the length of syllables, but digram rules and
      * heuristics do so indirectly.
      */
-    if ( (word_units     = (USHORT *) calloc (sizeof (USHORT), pwlen+1))==NULL ||
-         (syllable_units = (USHORT *) calloc (sizeof (USHORT), pwlen+1))==NULL ||
-         (new_syllable   = (char *) calloc (sizeof (USHORT), pwlen+1))  ==NULL ||
+    if ( (word_units     = (unsigned short *) calloc (sizeof (unsigned short), pwlen+1))==NULL ||
+         (syllable_units = (unsigned short *) calloc (sizeof (unsigned short), pwlen+1))==NULL ||
+         (new_syllable   = (char *) calloc (sizeof (unsigned short), pwlen+1))  ==NULL ||
 	 (syllable_for_hyph = (char *) calloc (sizeof(char), 20))==NULL)
 	   return(-1);
 
@@ -1365,7 +1365,7 @@
       * Get the syllable and find its length.
       */
      (void) gen_syllable (new_syllable, pwlen - word_length, syllable_units, &syllable_size);
-     syllable_length = (USHORT) strlen (new_syllable);
+     syllable_length = (unsigned short) strlen (new_syllable);
      
      /*
       * Append the syllable units to the word units.
@@ -1399,16 +1399,16 @@
           if ( ((pass_mode & S_NB) > 0) && (syllable_length == 1) && dsd == 0)
             {
              numerize(new_syllable);
-	     ch_flag = TRUE;
+	     ch_flag = true;
             }
           if ( ((pass_mode & S_SS) > 0) && (syllable_length == 1) && (dsd == 1))
             {
 	      specialize(new_syllable);
-	      ch_flag = TRUE;
+	      ch_flag = true;
 	    }
-          if ( ( (pass_mode & S_CL) > 0) && (ch_flag != TRUE))
+          if ( ( (pass_mode & S_CL) > 0) && (ch_flag != true))
              capitalize(new_syllable);
-          ch_flag = FALSE;
+          ch_flag = false;
           /**/
           (void) strcpy (word, new_syllable);
 	  if (syllable_length == 1)
@@ -1420,7 +1420,7 @@
 	     {
               (void) strcpy (hyphenated_word, new_syllable);
 	     }
-	  (void)memset ( (void *)new_syllable, 0, (size_t)(pwlen * sizeof(USHORT)+1));
+	  (void)memset ( (void *)new_syllable, 0, (size_t)(pwlen * sizeof(unsigned short)+1));
 	  (void)memset ( (void *)syllable_for_hyph, 0, 20);
          }
          else
@@ -1433,16 +1433,16 @@
           if ( ((pass_mode & S_NB) > 0) && (syllable_length == 1) && (dsd == 0))
             {
              numerize(new_syllable);
-	     ch_flag = TRUE;
+	     ch_flag = true;
             }
           if ( ( (pass_mode & S_SS) > 0) && (syllable_length == 1) && (dsd == 1))
             {
 	     specialize(new_syllable);
-	     ch_flag = TRUE;
+	     ch_flag = true;
 	    }
-          if ( ( (pass_mode & S_CL) > 0) && (ch_flag != TRUE))
+          if ( ( (pass_mode & S_CL) > 0) && (ch_flag != true))
              capitalize(new_syllable);
-          ch_flag = FALSE;
+          ch_flag = false;
           /**/
           (void) strcat (word, new_syllable);
           (void) strcat (hyphenated_word, "-");
@@ -1455,7 +1455,7 @@
 	     {
               (void) strcat (hyphenated_word, new_syllable);
 	     }
-	  (void)memset ( (void *)new_syllable, 0, (size_t)(pwlen * sizeof(USHORT)+1));
+	  (void)memset ( (void *)new_syllable, 0, (size_t)(pwlen * sizeof(unsigned short)+1));
 	  (void)memset ( (void *)syllable_for_hyph, 0, 20);
          }
          word_length += syllable_length;
@@ -1503,13 +1503,13 @@
  * the individual letters, so three consecutive units can have
  * the length of 6 at most.
  */
-boolean
-improper_word (USHORT *units, USHORT word_size)
+bool
+improper_word (unsigned short *units, unsigned short word_size)
 {
-    USHORT unit_count;
-    boolean failure;
+    unsigned short unit_count;
+    bool failure;
 
-    failure = FALSE;
+    failure = false;
 
     for (unit_count = 0; !failure && (unit_count < word_size);
          unit_count++)
@@ -1524,7 +1524,7 @@
      if ((unit_count != 0) &&
           (digram[units[unit_count - 1]][units[unit_count]] &
               ILLEGAL_PAIR))
-         failure = TRUE;
+         failure = true;
 
      /* 
       * Check for consecutive vowels or consonants.  Because
@@ -1551,7 +1551,7 @@
               (!(rules[units[unit_count - 2]].flags & VOWEL) &&
                !(rules[units[unit_count - 1]].flags & VOWEL) &&
                !(rules[units[unit_count]].flags & VOWEL)))
-          failure = TRUE;
+          failure = true;
      }
     }
 
@@ -1565,12 +1565,12 @@
  * y starts a word and is the only vowel in the first syllable.
  * The word ycl is one example.  We discard words like these.
  */
-boolean
-have_initial_y (USHORT *units, USHORT unit_size)
+bool
+have_initial_y (unsigned short *units, unsigned short unit_size)
 {
-    USHORT unit_count;
-    USHORT vowel_count;
-    USHORT normal_vowel_count;
+    unsigned short unit_count;
+    unsigned short vowel_count;
+    unsigned short normal_vowel_count;
 
     vowel_count = 0;
     normal_vowel_count = 0;
@@ -1603,11 +1603,11 @@
  * vowel at the end of the word or syllables like ble will
  * be generated.
  */
-boolean
-have_final_split (USHORT *units, USHORT unit_size)
+bool
+have_final_split (unsigned short *units, unsigned short unit_size)
 {
-    USHORT unit_count;
-    USHORT vowel_count;
+    unsigned short unit_count;
+    unsigned short vowel_count;
 
     vowel_count = 0;
 
@@ -1619,7 +1619,7 @@
          vowel_count++;
 
     /*
-     * Return TRUE iff the only vowel was e, found at the end if the
+     * Return true iff the only vowel was e, found at the end if the
      * word.
      */
     return ((vowel_count == 1) &&
@@ -1663,21 +1663,21 @@
  *           first unit is "alternate_vowel".
  */
 char *
-gen_syllable (char *syllable, USHORT pwlen, USHORT *units_in_syllable,
-              USHORT *syllable_length)
+gen_syllable (char *syllable, unsigned short pwlen, unsigned short *units_in_syllable,
+              unsigned short *syllable_length)
 {
-    USHORT  unit = 0;
-    SHORT   current_unit = 0;
-    USHORT  vowel_count = 0;
-    boolean rule_broken;
-    boolean want_vowel;
-    boolean want_another_unit;
-    UINT    tries = 0;
-    USHORT  last_unit = 0;
-    SHORT   length_left = 0;
-    USHORT  hold_saved_unit = 0;
-    static  USHORT saved_unit;
-    static  USHORT saved_pair[2];
+    unsigned short  unit = 0;
+    short   current_unit = 0;
+    unsigned short  vowel_count = 0;
+    bool rule_broken;
+    bool want_vowel;
+    bool want_another_unit;
+    unsigned int    tries = 0;
+    unsigned short  last_unit = 0;
+    short   length_left = 0;
+    unsigned short  hold_saved_unit = 0;
+    static  unsigned short saved_unit;
+    static  unsigned short saved_pair[2];
 
     /*
      * This is needed if the saved_unit is tries and the syllable then
@@ -1701,14 +1701,14 @@
      vowel_count = 0;
      current_unit = 0;
      length_left = (short int) pwlen;
-     want_another_unit = TRUE;
+     want_another_unit = true;
 
      /*
       * This loop finds all the units for the syllable.
       */
      do
      {
-         want_vowel = FALSE;
+         want_vowel = false;
 
          /*
           * This loop continues until a valid unit is found for the
@@ -1769,9 +1769,9 @@
            * Prevent having a word longer than expected.
            */
           if (length_left < 0)
-              rule_broken = TRUE;
+              rule_broken = true;
           else
-              rule_broken = FALSE;
+              rule_broken = false;
 
           /*
            * First unit of syllable.  This is special because the
@@ -1785,7 +1785,7 @@
                * use it.
                */
               if (rules[unit].flags & NOT_BEGIN_SYLLABLE)
-               rule_broken = TRUE;
+               rule_broken = true;
               else
                /* 
                 * If this is the last unit of a word,
@@ -1797,9 +1797,9 @@
                if (length_left == 0)
 	          {
                    if (rules[unit].flags & VOWEL)
-                    want_another_unit = FALSE;
+                    want_another_unit = false;
                    else
-                    rule_broken = TRUE;
+                    rule_broken = true;
 		  }
           }
           else
@@ -1826,7 +1826,7 @@
                */
                    (ALLOWED (END) && (vowel_count == 0) &&
                     !(rules[unit].flags & VOWEL)))
-               rule_broken = TRUE;
+               rule_broken = true;
 
               if (current_unit == 1)
               {
@@ -1835,7 +1835,7 @@
                 * a syllable and it does not fit.
                 */
                if (ALLOWED (NOT_BEGIN))
-                   rule_broken = TRUE;
+                   rule_broken = true;
               }
               else
               {
@@ -1885,7 +1885,7 @@
                         !(rules[units_in_syllable
                              [current_unit - 2]].flags &
                          VOWEL)))
-                   rule_broken = TRUE;
+                   rule_broken = true;
 
                /*
                 * The following checks occur when the current unit
@@ -1905,7 +1905,7 @@
                     */
                    if ((vowel_count > 1) &&
                         (rules[last_unit].flags & VOWEL))
-                    rule_broken = TRUE;
+                    rule_broken = true;
                    else
                     /*
                      * Check for the case of
@@ -1928,12 +1928,12 @@
                               [current_unit - 2]]
                              [last_unit] &
                              NOT_END)
-                         rule_broken = TRUE;
+                         rule_broken = true;
                         else
                         {
                          saved_unit = 1;
                          saved_pair[0] = unit;
-                         want_another_unit = FALSE;
+                         want_another_unit = false;
                         }
                     }
 		  }
@@ -1963,7 +1963,7 @@
                      */
                     (ALLOWED (END) || (length_left == 0)))
 		   {
-                   want_another_unit = FALSE;
+                   want_another_unit = false;
 		   }
 	       else
                    /*
@@ -1992,20 +1992,20 @@
                         saved_unit = 2;
                         saved_pair[0] = unit;
                         saved_pair[1] = last_unit;
-                        want_another_unit = FALSE;
+                        want_another_unit = false;
                     }
                     else
                         if (ALLOWED (BREAK))
                         {
                          saved_unit = 1;
                          saved_pair[0] = unit;
-                         want_another_unit = FALSE;
+                         want_another_unit = false;
                         }
                    }
                    else
                     if (ALLOWED (SUFFIX))
 		     {
-                        want_vowel = TRUE;
+                        want_vowel = true;
 		     }
               }
           }
@@ -2066,7 +2066,7 @@
           * Whoops!  Too many tries.  We set rule_broken so we can
           * loop in the outer loop and try another syllable.
           */
-          rule_broken = TRUE;
+          rule_broken = true;
 
          /*
           * ...and the syllable length grows.
@@ -2091,15 +2091,15 @@
  * consonants, or syllables with consonants between vowels (unless
  * one of them is the final silent e).
  */
-boolean
-illegal_placement (USHORT *units, USHORT pwlen)
+bool
+illegal_placement (unsigned short *units, unsigned short pwlen)
 {
-    USHORT vowel_count;
-    USHORT unit_count;
-    boolean failure;
+    unsigned short vowel_count;
+    unsigned short unit_count;
+    bool failure;
 
     vowel_count = 0;
-    failure = FALSE;
+    failure = false;
 
     for (unit_count = 0; !failure && (unit_count <= pwlen);
          unit_count++)
@@ -2143,7 +2143,7 @@
                         VOWEL) &&
                     (rules[units[unit_count]].flags &
                         VOWEL)))))
-          failure = TRUE;
+          failure = true;
      }
 
      /* 
@@ -2185,7 +2185,7 @@
  * in this procedure without affecting the digram table or any other
  * programs using the Random_word subroutine.
  */
-static USHORT numbers[] =
+static unsigned short numbers[] =
 {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     1, 1, 1, 1, 1, 1, 1, 1,
@@ -2234,7 +2234,7 @@
  * use vowel_numbers will adjust to the size difference
 automatically.
  */
-static USHORT vowel_numbers[] =
+static unsigned short vowel_numbers[] =
 {
     0, 0, 4, 4, 4, 8, 8, 14, 14, 19, 19, 23
 };
@@ -2245,10 +2245,10 @@
  * expected, use the vowel_numbers array rather than looping through
  * the numbers array until a vowel is found.
  */
-USHORT
-random_unit (USHORT type)
+unsigned short
+random_unit (unsigned short type)
 {
-     USHORT number;
+     unsigned short number;
 
     /*
      * Sometimes, we are asked to explicitly get a vowel (i.e., if
@@ -2257,12 +2257,12 @@
      */
     if (type & VOWEL)
       number = vowel_numbers[
-          base::RandInt(0, (sizeof (vowel_numbers) / sizeof (USHORT))-1)];
+          base::RandInt(0, (sizeof (vowel_numbers) / sizeof (unsigned short))-1)];
     else
      /*
       * Get any letter according to the English distribution.
       */
       number = numbers[
-          base::RandInt(0, (sizeof (numbers) / sizeof (USHORT))-1)];
+          base::RandInt(0, (sizeof (numbers) / sizeof (unsigned short))-1)];
     return (number);
 }
diff --git a/third_party/fips181/fips181.h b/third_party/fips181/fips181.h
index 524da59..952256de 100644
--- a/third_party/fips181/fips181.h
+++ b/third_party/fips181/fips181.h
@@ -34,12 +34,8 @@
 */
 
 
-#ifndef APG_PRONPASS_H
-#define APG_PRONPASS_H	1
-
-#ifndef APG_OWN_TYPES_H
-#include "owntypes.h"
-#endif /* APG_OWN_TYPES_H */
+#ifndef FIPS181_H
+#define FIPS181_H	1
 
 #define RULE_SIZE             (sizeof(rules)/sizeof(struct unit))
 #define ALLOWED(flag)         (digram[units_in_syllable[current_unit -1]][unit] & (flag))
@@ -69,16 +65,18 @@
 #define S_SL    0x08 /* Small   */
 #define S_RS    0x10 /* Restricted Symbol*/
 
-extern int gen_pron_pass (char *word, char* hypenated_word,
-                          USHORT minlen, USHORT maxlen, unsigned int pass_mode);
-USHORT  random_unit (USHORT type);
-USHORT  get_random (USHORT minlen, USHORT maxlen);
-boolean have_initial_y (USHORT *units, USHORT unit_size);
-boolean illegal_placement (USHORT *units, USHORT pwlen);
-boolean improper_word (USHORT *units, USHORT word_size);
-boolean have_final_split (USHORT *units, USHORT unit_size);
-int gen_word (char *word, char *hyphenated_word, USHORT pwlen, unsigned int pass_mode);
-char   	*gen_syllable(char *syllable, USHORT pwlen, USHORT *units_in_syllable,
-                      USHORT *syllable_length);
+#define APG_MAX_PASSWORD_LENGTH 255
 
-#endif /* APG_PRONPASS_H */
+extern int gen_pron_pass (char *word, char* hypenated_word,
+                          unsigned short minlen, unsigned short maxlen, unsigned int pass_mode);
+unsigned short  random_unit (unsigned short type);
+unsigned short  get_random (unsigned short minlen, unsigned short maxlen);
+bool have_initial_y (unsigned short *units, unsigned short unit_size);
+bool illegal_placement (unsigned short *units, unsigned short pwlen);
+bool improper_word (unsigned short *units, unsigned short word_size);
+bool have_final_split (unsigned short *units, unsigned short unit_size);
+int gen_word (char *word, char *hyphenated_word, unsigned short pwlen, unsigned int pass_mode);
+char   	*gen_syllable(char *syllable, unsigned short pwlen, unsigned short *units_in_syllable,
+                      unsigned short *syllable_length);
+
+#endif /* FIPS181_H */
diff --git a/third_party/fips181/owntypes.h b/third_party/fips181/owntypes.h
deleted file mode 100644
index 31a34f9..0000000
--- a/third_party/fips181/owntypes.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-** Copyright (c) 1999, 2000, 2001, 2002, 2003
-** Adel I. Mirzazhanov. All rights reserved
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions
-** are met:
-** 
-**     1.Redistributions of source code must retain the above copyright notice,
-**       this list of conditions and the following disclaimer. 
-**     2.Redistributions in binary form must reproduce the above copyright
-**       notice, this list of conditions and the following disclaimer in the
-**       documentation and/or other materials provided with the distribution. 
-**     3.The name of the author may not be used to endorse or promote products
-**       derived from this software without specific prior written permission. 
-** 		  
-** THIS SOFTWARE IS PROVIDED BY THE AUTHOR  ``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 AUTHOR 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 APG_OWN_TYPES_H
-#define APG_OWN_TYPES_H	1
-
-typedef unsigned int	      UINT;
-typedef unsigned short	      USHORT;
-typedef short int	      SHORT;
-typedef int   		      boolean;
-typedef unsigned long int     UINT32;
-
-#define TRUE	1
-#define FALSE	0
-
-#define APG_MAX_PASSWORD_LENGTH 255
-
-#endif /* APG_OWN_TYPES_H */
diff --git a/third_party/fips181/randpass.cc b/third_party/fips181/randpass.cc
index 8aded15..68dddeea 100644
--- a/third_party/fips181/randpass.cc
+++ b/third_party/fips181/randpass.cc
@@ -36,7 +36,6 @@
 #include <time.h>
 
 #include "base/rand_util.h"
-#include "owntypes.h"
 #include "randpass.h"
 #include "smbl.h"
 
@@ -143,18 +142,18 @@
 ** INPUT:
 **   char - symbol.
 ** OUTPUT:
-**   int - 0 - not restricted
-**         1 - restricted
+**   bool - false - not restricted
+**          true - restricted
 ** NOTES:
 **   none.
 */
-int
+bool
 is_restricted_symbol (char symbol)
 {
   int j = 0;
   for (j = 0; j <= 93 ; j++)
     if (symbol == smbl[j].ch)
       if ((S_RS & smbl[j].type) > 0)
-        return(1);
-  return(0);
+        return(true);
+  return(false);
 }
diff --git a/third_party/fips181/randpass.h b/third_party/fips181/randpass.h
index ee0df62..013c572 100644
--- a/third_party/fips181/randpass.h
+++ b/third_party/fips181/randpass.h
@@ -33,20 +33,16 @@
 #ifndef APG_RANDPASS_H
 #define APG_RANDPASS_H	1
 
-#ifndef APG_OWN_TYPES_H
-#include "owntypes.h"
-#endif
-
 struct sym
  {
   char   ch;
-  USHORT type;
+  unsigned short type;
  };
 
 /* char gen_symbol(unsigned short int symbol_class); */
 extern int gen_rand_pass(char* password_string, int minl,
                          int maxl, unsigned int pass_mode);
 extern int gen_rand_symbol (char *symbol, unsigned int mode);
-extern int is_restricted_symbol (char symbol);
+extern bool is_restricted_symbol (char symbol);
 
 #endif /* RANDPASS_H */
diff --git a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
index 8e1cb07..97d9ebd 100644
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -348,14 +348,19 @@
 
   // Given
   //   class Foo {};
+  //   class DerivedFoo : class Foo;
   //   using Bar = Foo;
   //   Bar f1();  // <- |Bar| would be matched by hasString("Bar") below.
   //   Bar f2();  // <- |Bar| would be matched by hasName("Foo") below.
+  //   DerivedFoo f3();  // <- |DerivedFoo| matched by isDerivedFrom(...) below.
   // |type_with_same_name_as_function| matcher matches Bar and Foo return types.
   auto type_with_same_name_as_function = qualType(anyOf(
-      hasString(name),  // hasString matches the type as spelled (Bar above).
-      hasDeclaration(namedDecl(hasName(name)))));  // hasDeclaration matches
-                                                   // resolved type (Foo above).
+      // hasString matches the type as spelled (Bar above).
+      hasString(name),
+      // hasDeclaration matches resolved type (Foo or DerivedFoo above).
+      hasDeclaration(namedDecl(hasName(name))),
+      hasDeclaration(cxxRecordDecl(isDerivedFrom(namedDecl(hasName(name)))))));
+
   // |type_containing_same_name_as_function| matcher will match all of the
   // return types below:
   // - Foo foo()  // Direct application of |type_with_same_name_as_function|.
diff --git a/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc b/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
index 1eff05b..f770362 100644
--- a/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
+++ b/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
@@ -189,6 +189,39 @@
   MyRefPtr<FooBar> foobar_;
 };
 
+namespace get_prefix_vs_inheritance {
+
+// Regression test for https://crbug.com/673031:
+// 1. |frame| accessor/method should be renamed in the same way for
+//    WebFrameImplBase and WebLocalFrameImpl.
+// 2. Need to rename |frame| to |GetFrame| (not to |Frame|) to avoid
+//    a conflict with the Frame type.
+
+class Frame {};
+class LocalFrame : public Frame {};
+
+class WebFrameImplBase {
+ public:
+  virtual Frame* GetFrame() const = 0;
+};
+
+class WebLocalFrameImpl : public WebFrameImplBase {
+ public:
+  LocalFrame* GetFrame() const override { return nullptr; }
+};
+
+// This is also a regression test for https://crbug.com/673031
+// (which can happen in a non-virtual-method case):
+class LayoutObject {};
+class LayoutBoxModelObject : public LayoutObject {};
+class PaintLayerStackingNode {
+ public:
+  // |layoutObject| should be renamed to |GetLayoutObject|.
+  LayoutBoxModelObject* GetLayoutObject() { return nullptr; }
+};
+
+}  // namespace get_prefix_vs_inheritance
+
 }  // namespace blink
 
 namespace WTF {
diff --git a/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc b/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
index 1f8f3a0..2802033 100644
--- a/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
+++ b/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
@@ -193,6 +193,39 @@
   MyRefPtr<FooBar> foobar_;
 };
 
+namespace get_prefix_vs_inheritance {
+
+// Regression test for https://crbug.com/673031:
+// 1. |frame| accessor/method should be renamed in the same way for
+//    WebFrameImplBase and WebLocalFrameImpl.
+// 2. Need to rename |frame| to |GetFrame| (not to |Frame|) to avoid
+//    a conflict with the Frame type.
+
+class Frame {};
+class LocalFrame : public Frame {};
+
+class WebFrameImplBase {
+ public:
+  virtual Frame* frame() const = 0;
+};
+
+class WebLocalFrameImpl : public WebFrameImplBase {
+ public:
+  LocalFrame* frame() const override { return nullptr; }
+};
+
+// This is also a regression test for https://crbug.com/673031
+// (which can happen in a non-virtual-method case):
+class LayoutObject {};
+class LayoutBoxModelObject : public LayoutObject {};
+class PaintLayerStackingNode {
+ public:
+  // |layoutObject| should be renamed to |GetLayoutObject|.
+  LayoutBoxModelObject* layoutObject() { return nullptr; }
+};
+
+}  // namespace get_prefix_vs_inheritance
+
 }  // namespace blink
 
 namespace WTF {
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index cfe94cb..dbe0fdf 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -461,6 +461,10 @@
   Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR)
   if sys.platform == 'win32' or use_head_revision:
     Checkout('LLD', LLVM_REPO_URL + '/lld/trunk', LLD_DIR)
+  elif os.path.exists(LLD_DIR):
+    # In case someone sends a tryjob that temporary adds lld to the checkout,
+    # make sure it's not around on future builds.
+    RmTree(LLD_DIR)
   Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk', COMPILER_RT_DIR)
   if sys.platform == 'darwin':
     # clang needs a libc++ checkout, else -stdlib=libc++ won't find includes
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8a58680..2234290 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -44819,6 +44819,7 @@
 <histogram name="Platform.CrOSEvent" enum="CrosEventEnum">
   <owner>dkrahn@chromium.org</owner>
   <owner>jwerner@chromium.org</owner>
+  <owner>vapier@gentoo.org</owner>
   <summary>
     Generic event of interest from Chrome OS.  Intended mainly to help assess
     the frequency of rare error conditions.
@@ -80505,6 +80506,7 @@
   <int value="17" label="VeyronEmmcUpgrade.FailedDiskAccess"/>
   <int value="18" label="VeyronEmmcUpgrade.FailedWPEnable"/>
   <int value="19" label="VeyronEmmcUpgrade.SignatureDetected"/>
+  <int value="20" label="Watchdog.StartupFailed"/>
 </enum>
 
 <enum name="CrosFirstRunTutorialCompletionType" type="int">
@@ -106339,6 +106341,8 @@
   <int value="3" label="Unknown (unused)"/>
   <int value="4" label="Online"/>
   <int value="5" label="Policy"/>
+  <int value="6" label="Third party"/>
+  <int value="7" label="Device policy"/>
 </enum>
 
 <enum name="WarmupStateOnLaunch" type="int">
@@ -114572,6 +114576,8 @@
   <suffix name="Original"/>
   <suffix name="Experimental"/>
   <affected-histogram name="SoftwareReporter.FoundUwSReadError"/>
+  <affected-histogram name="SoftwareReporter.LogsUploadResult"/>
+  <affected-histogram name="SoftwareReporter.LogsUploadResultRegistryError"/>
   <affected-histogram name="SoftwareReporter.MajorVersion"/>
   <affected-histogram name="SoftwareReporter.MemoryUsed"/>
   <affected-histogram name="SoftwareReporter.MinorVersion"/>
diff --git a/tools/perf/docs/perf_bot_sheriffing.md b/tools/perf/docs/perf_bot_sheriffing.md
index 0e64a48..419dea53f 100644
--- a/tools/perf/docs/perf_bot_sheriffing.md
+++ b/tools/perf/docs/perf_bot_sheriffing.md
@@ -190,6 +190,14 @@
     ensure that the bug title reflects something like "Fix and re-enable
     testname".
 4.  Investigate the failure. Some tips for investigating:
+    *   When viewing buildbot step logs, **use the **<font color="blue">[stdout]</font>** link to view logs!**.
+        This will link to logdog logs which do not expire. Do not use or link
+        to the logs found through the <font color="blue">stdio</font> link
+        whenever possible as these logs will expire.
+    *   When investigating Android, look for the logcat which is uploaded to
+        Google Storage at the end of the run. logcat will contain much more
+        detailed Android device and crash info than will be found in
+        Telemetry logs.
     *   If it's a non flaky failure, indentify the first failed
         build so you can narrow down the range of CLs that causes the failure.
         You can use the
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index e903feb..4910aaa1 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -61,6 +61,7 @@
     "//ui/base",
     "//ui/gfx",
     "//ui/gfx/geometry",
+    "//ui/strings",
   ]
 
   if (is_win) {
diff --git a/ui/accessibility/DEPS b/ui/accessibility/DEPS
index fd3b8f0..0198e2c 100644
--- a/ui/accessibility/DEPS
+++ b/ui/accessibility/DEPS
@@ -1,5 +1,7 @@
 include_rules = [
   "+third_party/iaccessible2",
+  "+ui/base/l10n",
   "+ui/base/win",
   "+ui/gfx",
+  "+ui/strings/grit/ui_strings.h",
 ]
diff --git a/ui/accessibility/ax_enums.idl b/ui/accessibility/ax_enums.idl
index ac8b0eb..6984a17 100644
--- a/ui/accessibility/ax_enums.idl
+++ b/ui/accessibility/ax_enums.idl
@@ -239,6 +239,8 @@
   };
 
   // An action to be taken on an accessibility node.
+  // In contrast to |AXSupportedAction|, these describe what happens to the
+  // object, e.g. "FOCUS".
   enum AXAction {
     blur,
 
@@ -294,6 +296,21 @@
     request_inline_text_boxes
   };
 
+  // Lists the actions that can be performed on a given node.
+  // In contrast to |AXAction|, these describe what the user can do on the
+  // object, e.g. "PRESS", not what happens to the object as a result.
+  // Only one action is supported for now.
+  enum AXSupportedAction {
+    activate,
+    check,
+    click,
+    jump,
+    open,
+    press,
+    select,
+    uncheck
+  };
+
   // A change to the accessibility tree.
   enum AXMutation {
     node_created,
@@ -304,7 +321,6 @@
 
   [cpp_enum_prefix_override="ax_attr"] enum AXStringAttribute {
     access_key,
-    action,
     // Only used when invalid_state == invalid_state_other.
     aria_invalid_value,
     auto_complete,
@@ -329,6 +345,7 @@
   };
 
   [cpp_enum_prefix_override="ax_attr"] enum AXIntAttribute {
+    action,
     // Scrollable container attributes.
     scroll_x,
     scroll_x_min,
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 5d14645..5a7d6c70 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/ax_text_utils.h"
 #include "ui/gfx/transform.h"
 
 using base::DoubleToString;
@@ -394,6 +395,12 @@
   for (size_t i = 0; i < int_attributes.size(); ++i) {
     std::string value = IntToString(int_attributes[i].second);
     switch (int_attributes[i].first) {
+      case AX_ATTR_ACTION:
+        result +=
+            " action=" +
+            base::UTF16ToUTF8(ActionToUnlocalizedString(
+                static_cast<AXSupportedAction>(int_attributes[i].second)));
+        break;
       case AX_ATTR_SCROLL_X:
         result += " scroll_x=" + value;
         break;
@@ -471,12 +478,14 @@
         }
         break;
       case AX_ATTR_NAME_FROM:
-        result += " name_from=" + ui::ToString(
-            static_cast<ui::AXNameFrom>(int_attributes[i].second));
+        result +=
+            " name_from=" +
+            ui::ToString(static_cast<AXNameFrom>(int_attributes[i].second));
         break;
       case AX_ATTR_DESCRIPTION_FROM:
-        result += " description_from=" + ui::ToString(
-            static_cast<ui::AXDescriptionFrom>(int_attributes[i].second));
+        result += " description_from=" +
+                  ui::ToString(
+                      static_cast<AXDescriptionFrom>(int_attributes[i].second));
         break;
       case AX_ATTR_ACTIVEDESCENDANT_ID:
         result += " activedescendant=" + value;
@@ -597,9 +606,6 @@
       case AX_ATTR_ACCESS_KEY:
         result += " access_key=" + value;
         break;
-      case AX_ATTR_ACTION:
-        result += " action=" + value;
-        break;
       case AX_ATTR_ARIA_INVALID_VALUE:
         result += " aria_invalid_value=" + value;
         break;
diff --git a/ui/accessibility/ax_text_utils.cc b/ui/accessibility/ax_text_utils.cc
index 7c97a989..af14f97 100644
--- a/ui/accessibility/ax_text_utils.cc
+++ b/ui/accessibility/ax_text_utils.cc
@@ -7,6 +7,9 @@
 #include "base/i18n/break_iterator.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
 
 namespace ui {
 
@@ -112,4 +115,56 @@
   }
 }
 
+base::string16 ActionToString(const AXSupportedAction supported_action) {
+  switch (supported_action) {
+    case AX_SUPPORTED_ACTION_NONE:
+      return base::string16();
+    case AX_SUPPORTED_ACTION_ACTIVATE:
+      return l10n_util::GetStringUTF16(IDS_AX_ACTIVATE_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_CHECK:
+      return l10n_util::GetStringUTF16(IDS_AX_CHECK_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_CLICK:
+      return l10n_util::GetStringUTF16(IDS_AX_CLICK_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_JUMP:
+      return l10n_util::GetStringUTF16(IDS_AX_JUMP_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_OPEN:
+      return l10n_util::GetStringUTF16(IDS_AX_OPEN_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_PRESS:
+      return l10n_util::GetStringUTF16(IDS_AX_PRESS_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_SELECT:
+      return l10n_util::GetStringUTF16(IDS_AX_SELECT_ACTION_VERB);
+    case AX_SUPPORTED_ACTION_UNCHECK:
+      return l10n_util::GetStringUTF16(IDS_AX_UNCHECK_ACTION_VERB);
+  }
+  NOTREACHED();
+  return base::string16();
+}
+
+// Some APIs on Linux and Windows need to return non-localized action names.
+base::string16 ActionToUnlocalizedString(
+    const AXSupportedAction supported_action) {
+  switch (supported_action) {
+    case ui::AX_SUPPORTED_ACTION_NONE:
+      return base::UTF8ToUTF16("none");
+    case ui::AX_SUPPORTED_ACTION_ACTIVATE:
+      return base::UTF8ToUTF16("activate");
+    case ui::AX_SUPPORTED_ACTION_CHECK:
+      return base::UTF8ToUTF16("check");
+    case ui::AX_SUPPORTED_ACTION_CLICK:
+      return base::UTF8ToUTF16("click");
+    case ui::AX_SUPPORTED_ACTION_JUMP:
+      return base::UTF8ToUTF16("jump");
+    case ui::AX_SUPPORTED_ACTION_OPEN:
+      return base::UTF8ToUTF16("open");
+    case ui::AX_SUPPORTED_ACTION_PRESS:
+      return base::UTF8ToUTF16("press");
+    case ui::AX_SUPPORTED_ACTION_SELECT:
+      return base::UTF8ToUTF16("select");
+    case ui::AX_SUPPORTED_ACTION_UNCHECK:
+      return base::UTF8ToUTF16("uncheck");
+  }
+  NOTREACHED();
+  return base::string16();
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_text_utils.h b/ui/accessibility/ax_text_utils.h
index a074cfe..f760660 100644
--- a/ui/accessibility/ax_text_utils.h
+++ b/ui/accessibility/ax_text_utils.h
@@ -52,6 +52,15 @@
                                TextBoundaryDirection direction,
                                AXTextAffinity affinity);
 
+// Returns a string ID that corresponds to the name of the given action.
+base::string16 AX_EXPORT
+ActionToString(const AXSupportedAction supported_action);
+
+// Returns the non-localized string representation of a supported action.
+// Some APIs on Linux and Windows need to return non-localized action names.
+base::string16 AX_EXPORT
+ActionToUnlocalizedString(const AXSupportedAction supported_action);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_TEXT_UTILS_H_
diff --git a/ui/accessibility/ax_tree_combiner.cc b/ui/accessibility/ax_tree_combiner.cc
index b983e995..1186acd2 100644
--- a/ui/accessibility/ax_tree_combiner.cc
+++ b/ui/accessibility/ax_tree_combiner.cc
@@ -27,6 +27,7 @@
     // add a new attribute without explicitly considering whether it's
     // a node id attribute or not.
     case AX_INT_ATTRIBUTE_NONE:
+    case AX_ATTR_ACTION:
     case AX_ATTR_SCROLL_X:
     case AX_ATTR_SCROLL_X_MIN:
     case AX_ATTR_SCROLL_X_MAX:
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 17439d9..5028f2da 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -374,7 +374,22 @@
 STDMETHODIMP AXPlatformNodeWin::get_accDefaultAction(
     VARIANT var_id, BSTR* def_action) {
   COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id, def_action);
-  return GetStringAttributeAsBstr(ui::AX_ATTR_ACTION, def_action);
+  int action;
+  if (!GetIntAttribute(AX_ATTR_ACTION, &action)) {
+    *def_action = nullptr;
+    return S_FALSE;
+  }
+
+  base::string16 action_verb =
+      ActionToString(static_cast<AXSupportedAction>(action));
+  if (action_verb.empty()) {
+    *def_action = nullptr;
+    return S_FALSE;
+  }
+
+  *def_action = SysAllocString(action_verb.c_str());
+  DCHECK(def_action);
+  return S_OK;
 }
 
 STDMETHODIMP AXPlatformNodeWin::get_accDescription(
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 03c6301c..a29bf600 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -32,6 +32,7 @@
 #include "ui/compositor/paint_context.h"
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -183,12 +184,9 @@
   // cc::Layer state.
   if (surface_layer_ && surface_layer_->surface_id().is_valid()) {
     clone->SetShowSurface(
-        surface_layer_->surface_id(),
-        surface_layer_->satisfy_callback(),
-        surface_layer_->require_callback(),
-        surface_layer_->surface_size(),
-        surface_layer_->surface_scale(),
-        frame_size_in_dip_);
+        surface_layer_->surface_id(), surface_layer_->satisfy_callback(),
+        surface_layer_->require_callback(), surface_layer_->surface_size(),
+        surface_layer_->surface_scale());
   } else if (type_ == LAYER_SOLID_COLOR) {
     clone->SetColor(GetTargetColor());
   }
@@ -661,24 +659,23 @@
     const cc::SurfaceId& surface_id,
     const cc::SurfaceLayer::SatisfyCallback& satisfy_callback,
     const cc::SurfaceLayer::RequireCallback& require_callback,
-    gfx::Size surface_size,
-    float scale,
-    gfx::Size frame_size_in_dip) {
+    const gfx::Size& surface_size_in_pixels,
+    float scale) {
   DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR);
 
   scoped_refptr<cc::SurfaceLayer> new_layer =
       cc::SurfaceLayer::Create(satisfy_callback, require_callback);
-  new_layer->SetSurfaceId(surface_id, scale, surface_size);
+  new_layer->SetSurfaceId(surface_id, scale, surface_size_in_pixels);
   SwitchToLayer(new_layer);
   surface_layer_ = new_layer;
 
-  frame_size_in_dip_ = frame_size_in_dip;
+  frame_size_in_dip_ = gfx::ConvertSizeToDIP(scale, surface_size_in_pixels);
   RecomputeDrawsContentAndUVRect();
 
   for (const auto& mirror : mirrors_) {
-    mirror->dest()->SetShowSurface(
-        surface_id, satisfy_callback, require_callback,
-        surface_size, scale, frame_size_in_dip);
+    mirror->dest()->SetShowSurface(surface_id, satisfy_callback,
+                                   require_callback, surface_size_in_pixels,
+                                   scale);
   }
 }
 
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 4bb77d31..6bce25e 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -296,9 +296,8 @@
   void SetShowSurface(const cc::SurfaceId& surface_id,
                       const cc::SurfaceLayer::SatisfyCallback& satisfy_callback,
                       const cc::SurfaceLayer::RequireCallback& require_callback,
-                      gfx::Size surface_size,
-                      float scale,
-                      gfx::Size frame_size_in_dip);
+                      const gfx::Size& surface_size_in_pixels,
+                      float scale);
 
   bool has_external_content() {
     return texture_layer_.get() || surface_layer_.get();
@@ -388,6 +387,10 @@
     return damaged_region_;
   }
 
+  const gfx::Size& frame_size_in_dip_for_testing() const {
+    return frame_size_in_dip_;
+  }
+
  private:
   friend class LayerOwner;
   class LayerMirror;
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 7638b3a0..9a55e398 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -1840,7 +1840,7 @@
   before = child->cc_layer_for_testing();
   child->SetShowSurface(cc::SurfaceId(), base::Bind(&FakeSatisfyCallback),
                         base::Bind(&FakeRequireCallback), gfx::Size(10, 10),
-                        1.0, gfx::Size(10, 10));
+                        1.0);
   EXPECT_TRUE(child->cc_layer_for_testing());
   EXPECT_NE(before.get(), child->cc_layer_for_testing());
 
@@ -1861,7 +1861,7 @@
       cc::FrameSinkId(0, 1),
       cc::LocalFrameId(2, base::UnguessableToken::Create()));
   layer->SetShowSurface(surface_id, satisfy_callback, require_callback,
-                        gfx::Size(10, 10), 1.0f, gfx::Size(10, 10));
+                        gfx::Size(10, 10), 1.0f);
 
   const auto mirror = layer->Mirror();
   auto* const cc_layer = mirror->cc_layer_for_testing();
@@ -1878,7 +1878,7 @@
       cc::SurfaceId(cc::FrameSinkId(1, 2),
                     cc::LocalFrameId(3, base::UnguessableToken::Create()));
   layer->SetShowSurface(surface_id, satisfy_callback, require_callback,
-                        gfx::Size(20, 20), 2.0f, gfx::Size(20, 20));
+                        gfx::Size(20, 20), 2.0f);
 
   // A new cc::Layer should be created for the mirror.
   EXPECT_NE(cc_layer, mirror->cc_layer_for_testing());
@@ -1890,6 +1890,23 @@
   EXPECT_EQ(2.0f, surface->surface_scale());
 }
 
+// Test if frame size in dip is properly calculated in SetShowSurface
+TEST_F(LayerWithDelegateTest, FrameSizeInDip) {
+  std::unique_ptr<Layer> layer(CreateLayer(LAYER_SOLID_COLOR));
+
+  const auto satisfy_callback = base::Bind(&FakeSatisfyCallback);
+  const auto require_callback = base::Bind(&FakeRequireCallback);
+
+  cc::SurfaceId surface_id(
+      cc::FrameSinkId(0, 1),
+      cc::LocalFrameId(2, base::UnguessableToken::Create()));
+
+  layer->SetShowSurface(surface_id, satisfy_callback, require_callback,
+                        gfx::Size(30, 40), 2.0f);
+
+  EXPECT_EQ(layer->frame_size_in_dip_for_testing(), gfx::Size(15, 20));
+}
+
 // Verifies that layer filters still attached after changing implementation
 // layer.
 TEST_F(LayerWithDelegateTest, LayerFiltersSurvival) {
@@ -1906,7 +1923,7 @@
   scoped_refptr<cc::Layer> before = layer->cc_layer_for_testing();
   layer->SetShowSurface(cc::SurfaceId(), base::Bind(&FakeSatisfyCallback),
                         base::Bind(&FakeRequireCallback), gfx::Size(10, 10),
-                        1.0, gfx::Size(10, 10));
+                        1.0);
   EXPECT_EQ(layer->layer_grayscale(), 0.5f);
   EXPECT_TRUE(layer->cc_layer_for_testing());
   EXPECT_NE(before.get(), layer->cc_layer_for_testing());
@@ -2229,7 +2246,7 @@
   layer->set_delegate(&delegate);
   layer->SetShowSurface(cc::SurfaceId(), base::Bind(&FakeSatisfyCallback),
                         base::Bind(&FakeRequireCallback), gfx::Size(10, 10),
-                        1.0, gfx::Size(10, 10));
+                        1.0);
 
   EXPECT_FALSE(delegate.delegated_frame_damage_called());
   layer->OnDelegatedFrameDamage(damage_rect);
diff --git a/ui/events/win/event_utils_win_unittest.cc b/ui/events/win/event_utils_win_unittest.cc
index a4efce4f..8b99008e 100644
--- a/ui/events/win/event_utils_win_unittest.cc
+++ b/ui/events/win/event_utils_win_unittest.cc
@@ -22,7 +22,10 @@
                             LPARAM l_param,
                             LRESULT& result,
                             DWORD msg_map_id = 0) override {
-    return true;
+    // Handle WM_NCCALCSIZE because the test below assumes there is no
+    // non-client area, affecting EventSystemLocationFromNative's client to
+    // screen coordinate transform.
+    return message == WM_NCCALCSIZE;
   }
 
  private:
diff --git a/ui/gfx/win/window_impl.cc b/ui/gfx/win/window_impl.cc
index f774468..bf6994d 100644
--- a/ui/gfx/win/window_impl.cc
+++ b/ui/gfx/win/window_impl.cc
@@ -283,19 +283,20 @@
                                      UINT message,
                                      WPARAM w_param,
                                      LPARAM l_param) {
+  WindowImpl* window = nullptr;
   if (message == WM_NCCREATE) {
     CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(l_param);
-    WindowImpl* window = reinterpret_cast<WindowImpl*>(cs->lpCreateParams);
+    window = reinterpret_cast<WindowImpl*>(cs->lpCreateParams);
     DCHECK(window);
     gfx::SetWindowUserData(hwnd, window);
     window->hwnd_ = hwnd;
     window->got_create_ = true;
     if (hwnd)
       window->got_valid_hwnd_ = true;
-    return TRUE;
+  } else {
+    window = reinterpret_cast<WindowImpl*>(GetWindowUserData(hwnd));
   }
 
-  WindowImpl* window = reinterpret_cast<WindowImpl*>(GetWindowUserData(hwnd));
   if (!window)
     return 0;
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
index a4802cd9..b9c694df 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
@@ -10,6 +10,10 @@
 #include <stdint.h>
 #include <xf86drmMode.h>
 
+namespace gfx {
+class Rect;
+}  // namespace gfx
+
 namespace ui {
 
 class CrtcController;
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 2d7ed310..1b7bceb 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -317,10 +317,33 @@
         Upload
       </message>
 
-      <!--Accessible name/action strings-->
-      <message name="IDS_APP_ACCACTION_PRESS" desc="The accessible default action for a button.">
-        Press
+      <!--Accessible action strings-->
+      <message name="IDS_AX_ACTIVATE_ACTION_VERB" desc="Verb stating the action that will occur when an element such as a text field is selected, as used by accessibility.">
+        activate
       </message>
+      <message name="IDS_AX_CHECK_ACTION_VERB" desc="Verb stating the action that will occur when an unchecked checkbox is clicked, as used by accessibility.">
+        check
+      </message>
+      <message name="IDS_AX_CLICK_ACTION_VERB" desc="Verb stating the action that will occur when clicking on a generic clickable object, when we don't know what that action is, as used by accessibility.">
+        click
+      </message>
+      <message name="IDS_AX_JUMP_ACTION_VERB" desc="Verb stating the action that will occur when an element such as an in-page link is activated, as used by accessibility.">
+        jump
+      </message>
+      <message name="IDS_AX_OPEN_ACTION_VERB" desc="Verb stating the action that will occur when an element such as a pop-up button is pressed, as used by accessibility.">
+        open
+      </message>
+<message name="IDS_AX_PRESS_ACTION_VERB" desc="Verb stating the action that will occur when an element such as a button is pressed, as used by accessibility.">
+        press
+      </message>
+      <message name="IDS_AX_SELECT_ACTION_VERB" desc="Verb stating the action that will occur when an element such as a radio button is selected, as used by accessibility.">
+        select
+      </message>
+      <message name="IDS_AX_UNCHECK_ACTION_VERB" desc="Verb stating the action that will occur when a checked checkbox is clicked, as used by accessibility.">
+        uncheck
+      </message>
+
+      <!--Accessible name strings-->
       <message name="IDS_APP_ACCNAME_CLOSE" desc="The accessible name for the Close button.">
         Close
       </message>
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 28383b0..25df35a2 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -1046,6 +1046,11 @@
 }
 
 void BridgedNativeWidget::ReorderChildViews() {
+  // Ignore layer manipulation during a Close(). This can be reached during the
+  // orderOut: in Close(), which notifies visibility changes to Views.
+  if (!bridged_view_)
+    return;
+
   RankMap rank;
   Widget* widget = native_widget_mac_->GetWidget();
   RankNSViews(widget->GetRootView(), associated_views_, &rank);
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc
index 416cdf3..8adf74d 100644
--- a/ui/views/controls/button/button.cc
+++ b/ui/views/controls/button/button.cc
@@ -65,6 +65,8 @@
 void Button::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ui::AX_ROLE_BUTTON;
   node_data->SetName(accessible_name_);
+  if (!enabled())
+    node_data->AddStateFlag(ui::AX_STATE_DISABLED);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index f07b7484..97637e3c 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -125,6 +125,15 @@
   node_data->role = ui::AX_ROLE_CHECK_BOX;
   if (checked())
     node_data->AddStateFlag(ui::AX_STATE_CHECKED);
+  if (enabled()) {
+    if (checked()) {
+      node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                                 ui::AX_SUPPORTED_ACTION_UNCHECK);
+    } else {
+      node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                                 ui::AX_SUPPORTED_ACTION_CHECK);
+    }
+  }
 }
 
 void Checkbox::OnPaint(gfx::Canvas* canvas) {
diff --git a/ui/views/controls/button/custom_button.cc b/ui/views/controls/button/custom_button.cc
index 4540c1b..21ebca3d 100644
--- a/ui/views/controls/button/custom_button.cc
+++ b/ui/views/controls/button/custom_button.cc
@@ -351,6 +351,10 @@
       // No additional accessibility node_data set for this button node_data.
       break;
   }
+  if (enabled()) {
+    node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                               ui::AX_SUPPORTED_ACTION_PRESS);
+  }
 }
 
 void CustomButton::VisibilityChanged(View* starting_from, bool visible) {
diff --git a/ui/views/controls/button/menu_button.cc b/ui/views/controls/button/menu_button.cc
index e98dda7..c4e986b 100644
--- a/ui/views/controls/button/menu_button.cc
+++ b/ui/views/controls/button/menu_button.cc
@@ -7,7 +7,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
@@ -17,7 +16,6 @@
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/text_constants.h"
 #include "ui/resources/grit/ui_resources.h"
-#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/menu_button_listener.h"
 #include "ui/views/mouse_constants.h"
@@ -300,9 +298,11 @@
 void MenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   CustomButton::GetAccessibleNodeData(node_data);
   node_data->role = ui::AX_ROLE_POP_UP_BUTTON;
-  node_data->AddStringAttribute(
-      ui::AX_ATTR_ACTION, l10n_util::GetStringUTF8(IDS_APP_ACCACTION_PRESS));
   node_data->AddStateFlag(ui::AX_STATE_HASPOPUP);
+  if (enabled()) {
+    node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                               ui::AX_SUPPORTED_ACTION_OPEN);
+  }
 }
 
 void MenuButton::PaintMenuMarker(gfx::Canvas* canvas) {
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 8b32edd..25304929 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -740,6 +740,10 @@
   node_data->role = ui::AX_ROLE_COMBO_BOX;
   node_data->SetName(accessible_name_);
   node_data->SetValue(model_->GetItemAt(selected_index_));
+  if (enabled()) {
+    node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                               ui::AX_SUPPORTED_ACTION_OPEN);
+  }
   node_data->AddIntAttribute(ui::AX_ATTR_POS_IN_SET, selected_index_);
   node_data->AddIntAttribute(ui::AX_ATTR_SET_SIZE, model_->GetItemCount());
 }
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 2bb3e82..1de38cc0 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -878,6 +878,10 @@
 void Textfield::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ui::AX_ROLE_TEXT_FIELD;
   node_data->SetName(accessible_name_);
+  if (enabled()) {
+    node_data->AddIntAttribute(ui::AX_ATTR_ACTION,
+                               ui::AX_SUPPORTED_ACTION_ACTIVATE);
+  }
   if (read_only())
     node_data->AddStateFlag(ui::AX_STATE_READ_ONLY);
   else
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 6013c84..7567394 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -4618,6 +4618,63 @@
   EXPECT_TRUE(v->on_native_theme_changed_called());
 }
 
+// A View that removes its Layer when hidden.
+class NoLayerWhenHiddenView : public View {
+ public:
+  NoLayerWhenHiddenView() {
+    SetPaintToLayer(true);
+    set_owned_by_client();
+    SetBounds(0, 0, 100, 100);
+  }
+
+  bool was_hidden() const { return was_hidden_; }
+
+  // View:
+  void VisibilityChanged(View* starting_from, bool is_visible) override {
+    if (!is_visible) {
+      was_hidden_ = true;
+      SetPaintToLayer(false);
+    }
+  }
+
+ private:
+  bool was_hidden_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(NoLayerWhenHiddenView);
+};
+
+// Test that Views can safely manipulate Layers during Widget closure.
+TEST_F(ViewTest, DestroyLayerInClose) {
+  NoLayerWhenHiddenView view;
+  Widget* widget = new Widget;
+  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+  widget->Init(params);
+  widget->SetBounds(gfx::Rect(0, 0, 100, 100));
+  widget->GetContentsView()->AddChildView(&view);
+  widget->Show();
+
+  EXPECT_TRUE(view.layer());
+  EXPECT_TRUE(view.GetWidget());
+  EXPECT_FALSE(view.was_hidden());
+
+  widget->Close();
+  if (IsAuraMusClient()) {
+    // Mus on Ozone doesn't send the visibility change during Close().
+    // See http://crbug.com/674003.
+    EXPECT_TRUE(view.layer());
+    EXPECT_FALSE(view.was_hidden());
+  } else {
+    EXPECT_FALSE(view.layer());
+    // Ensure the layer went away via VisibilityChanged().
+    EXPECT_TRUE(view.was_hidden());
+  }
+
+  // Not removed from Widget until Close() completes.
+  EXPECT_TRUE(view.GetWidget());
+  base::RunLoop().RunUntilIdle();  // Let the Close() complete.
+  EXPECT_FALSE(view.GetWidget());
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Observer tests.
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 73fad04..1c6b46a 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -197,10 +197,8 @@
 }
 
 void NativeWidgetMac::ReorderNativeViews() {
-  if (bridge_) {
-    bridge_->SetRootView(GetWidget()->GetRootView());
+  if (bridge_)
     bridge_->ReorderChildViews();
-  }
 }
 
 void NativeWidgetMac::ViewRemoved(View* view) {
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index c9eb444..54116a2f5 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -329,6 +329,7 @@
       current_cursor_(NULL),
       previous_cursor_(NULL),
       dpi_(0),
+      called_enable_non_client_dpi_scaling_(false),
       active_mouse_tracking_flags_(0),
       is_right_mouse_pressed_on_caption_(false),
       lock_updates_count_(0),
@@ -362,7 +363,9 @@
   // Create the window.
   WindowImpl::Init(parent, bounds);
 
-  if (delegate_->HasFrame() && base::win::IsProcessPerMonitorDpiAware()) {
+  if (!called_enable_non_client_dpi_scaling_ &&
+      delegate_->HasFrame() &&
+      base::win::IsProcessPerMonitorDpiAware()) {
     static auto enable_child_window_dpi_message_func = []() {
       // Derived signature; not available in headers.
       // This call gets Windows to scale the non-client area when WM_DPICHANGED
@@ -1795,6 +1798,24 @@
   return mode ? WVR_REDRAW : 0;
 }
 
+LRESULT HWNDMessageHandler::OnNCCreate(LPCREATESTRUCT lpCreateStruct) {
+  SetMsgHandled(FALSE);
+  if (delegate_->HasFrame() && base::win::IsProcessPerMonitorDpiAware()) {
+    static auto enable_non_client_dpi_scaling_func = []() {
+      // Signature only available in the 10.0.14393.0 API. As of this writing,
+      // Chrome built against 10.0.10586.0.
+      using EnableNonClientDpiScalingPtr = LRESULT (WINAPI*)(HWND);
+      return reinterpret_cast<EnableNonClientDpiScalingPtr>(
+                 GetProcAddress(GetModuleHandle(L"user32.dll"),
+                                "EnableNonClientDpiScaling"));
+    }();
+    called_enable_non_client_dpi_scaling_ =
+        !!(enable_non_client_dpi_scaling_func &&
+           enable_non_client_dpi_scaling_func(hwnd()));
+  }
+  return FALSE;
+}
+
 LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) {
   if (!delegate_->HasNonClientView()) {
     SetMsgHandled(FALSE);
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index 622696f..0ae7bbf 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -411,6 +411,7 @@
     CR_MSG_WM_MOVE(OnMove)
     CR_MSG_WM_MOVING(OnMoving)
     CR_MSG_WM_NCCALCSIZE(OnNCCalcSize)
+    CR_MSG_WM_NCCREATE(OnNCCreate)
     CR_MSG_WM_NCHITTEST(OnNCHitTest)
     CR_MSG_WM_NCPAINT(OnNCPaint)
     CR_MSG_WM_NOTIFY(OnNotify)
@@ -462,6 +463,7 @@
   void OnMoving(UINT param, const RECT* new_bounds);
   LRESULT OnNCActivate(UINT message, WPARAM w_param, LPARAM l_param);
   LRESULT OnNCCalcSize(BOOL mode, LPARAM l_param);
+  LRESULT OnNCCreate(LPCREATESTRUCT lpCreateStruct);
   LRESULT OnNCHitTest(const gfx::Point& point);
   void OnNCPaint(HRGN rgn);
   LRESULT OnNCUAHDrawCaption(UINT message, WPARAM w_param, LPARAM l_param);
@@ -577,6 +579,13 @@
   // The current DPI.
   int dpi_;
 
+  // Whether EnableNonClientDpiScaling was called successfully with this window.
+  // This flag exists because EnableNonClientDpiScaling must be called during
+  // WM_NCCREATE and EnableChildWindowDpiMessage is called after window
+  // creation. We don't want to call both, so this helps us determine if a call
+  // to EnableChildWindowDpiMessage is necessary.
+  bool called_enable_non_client_dpi_scaling_;
+
   // Event handling ------------------------------------------------------------
 
   // The flags currently being used with TrackMouseEvent to track mouse