diff --git a/BUILD.gn b/BUILD.gn
index 5728f427..8f84ca3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -547,6 +547,7 @@
       "//sandbox/win:sbox_validation_tests",
       "//testing/gtest:gtest_main",
       "//third_party/pdfium/samples:pdfium_diff",
+      "//third_party/tcmalloc:addr2line-pdb",
       "//tools/win/chromeexts:chromeexts",
     ]
     deps -= [
diff --git a/DEPS b/DEPS
index 41f80bc..c5e93403 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # 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': '0b83319b7f301145b7fc89d7096ddcea91d4a56b',
+  'skia_revision': 'b66fa526b882f2472d731b42cba65fe05cea4268',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # 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': '0029fb25b9b69f2e4465164282eb1040b60437dd',
+  'pdfium_revision': 'cfdb5fdd12d47136ad1db9a67fc598a71f6757ff',
   # 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': '76b86f523bce70142abb163469b0973c0b20e9fb',
+  'catapult_revision': 'faf60eb37f8b9828eddb30c8397b333eb1d89204',
   # 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/PRESUBMIT.py b/PRESUBMIT.py
index ee6ab23..699a62b 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -340,7 +340,6 @@
 
 _ANDROID_SPECIFIC_PYDEPS_FILES = [
     'build/android/test_runner.pydeps',
-    'build/android/test_wrapper/logdog_wrapper.pydeps',
     'net/tools/testserver/testserver.pydeps',
 ]
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/KeySystemTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/KeySystemTest.java
index d00a3da9..d9f61fa 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/KeySystemTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/KeySystemTest.java
@@ -10,6 +10,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.permission.AwPermissionRequest;
 import org.chromium.android_webview.permission.Resource;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 
 import java.util.concurrent.Callable;
@@ -104,7 +105,8 @@
     }
 
     @Feature({"AndroidWebView"})
-    @SmallTest
+    @DisabledTest
+    // crbug/701916
     public void testSupportWidevineKeySystem() throws Throwable {
         assertEquals(
                 getPlatformKeySystemExpectations(), isKeySystemSupported("com.widevine.alpha"));
@@ -117,7 +119,8 @@
     }
 
     @Feature({"AndroidWebView"})
-    @SmallTest
+    @DisabledTest
+    // crbug/701916
     public void testSupportPlatformKeySystem() throws Throwable {
         assertEquals(getPlatformKeySystemExpectations(),
                 isKeySystemSupported("x-com.oem.test-keysystem"));
diff --git a/ash/common/shelf/shelf_controller.cc b/ash/common/shelf/shelf_controller.cc
index 7137d23..fa47593 100644
--- a/ash/common/shelf/shelf_controller.cc
+++ b/ash/common/shelf/shelf_controller.cc
@@ -93,9 +93,6 @@
 
 void ShelfController::SetAlignment(ShelfAlignment alignment,
                                    int64_t display_id) {
-  if (!ash::WmShelf::CanChangeShelfAlignment())
-    return;
-
   WmShelf* shelf = GetShelfForDisplay(display_id);
   // TODO(jamescook): The initialization check should not be necessary, but
   // otherwise this wrongly tries to set the alignment on a secondary display
diff --git a/ash/sticky_keys/sticky_keys_controller.cc b/ash/sticky_keys/sticky_keys_controller.cc
index d3257a8a..29706f8f 100644
--- a/ash/sticky_keys/sticky_keys_controller.cc
+++ b/ash/sticky_keys/sticky_keys_controller.cc
@@ -295,7 +295,7 @@
   DCHECK(new_event);
   if (*new_event)
     return 1;
-  new_event->reset(modifier_up_event_.release());
+  *new_event = std::move(modifier_up_event_);
   return 0;
 }
 
diff --git a/ash/test/shelf_button_pressed_metric_tracker_test_api.cc b/ash/test/shelf_button_pressed_metric_tracker_test_api.cc
index 85afeb93..6deddd65 100644
--- a/ash/test/shelf_button_pressed_metric_tracker_test_api.cc
+++ b/ash/test/shelf_button_pressed_metric_tracker_test_api.cc
@@ -19,7 +19,7 @@
 
 void ShelfButtonPressedMetricTrackerTestAPI::SetTickClock(
     std::unique_ptr<base::TickClock> tick_clock) {
-  shelf_button_pressed_metric_tracker_->tick_clock_.reset(tick_clock.release());
+  shelf_button_pressed_metric_tracker_->tick_clock_ = std::move(tick_clock);
 }
 
 }  // namespace test
diff --git a/base/allocator/partition_allocator/partition_alloc.h b/base/allocator/partition_allocator/partition_alloc.h
index a3ff90e..c720a50 100644
--- a/base/allocator/partition_allocator/partition_alloc.h
+++ b/base/allocator/partition_allocator/partition_alloc.h
@@ -60,6 +60,7 @@
 // - Better freelist masking function to guarantee fault on 32-bit.
 
 #include <limits.h>
+#include <string.h>
 
 #include "base/allocator/partition_allocator/page_allocator.h"
 #include "base/allocator/partition_allocator/spin_lock.h"
diff --git a/base/native_library.h b/base/native_library.h
index 02eae1d..e2b9ca7e 100644
--- a/base/native_library.h
+++ b/base/native_library.h
@@ -91,16 +91,6 @@
     const NativeLibraryOptions& options,
     NativeLibraryLoadError* error);
 
-#if defined(OS_WIN)
-// Loads a native library from disk.  Release it with UnloadNativeLibrary when
-// you're done.
-// This function retrieves the LoadLibrary function exported from kernel32.dll
-// and calls it instead of directly calling the LoadLibrary function via the
-// import table.
-BASE_EXPORT NativeLibrary LoadNativeLibraryDynamically(
-    const FilePath& library_path);
-#endif  // OS_WIN
-
 // Unloads a native library.
 BASE_EXPORT void UnloadNativeLibrary(NativeLibrary library);
 
diff --git a/base/native_library_win.cc b/base/native_library_win.cc
index 64c7380..68ff3d1f 100644
--- a/base/native_library_win.cc
+++ b/base/native_library_win.cc
@@ -7,6 +7,7 @@
 #include <windows.h>
 
 #include "base/files/file_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -14,16 +15,108 @@
 
 namespace base {
 
-typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name);
+using AddDllDirectory = HMODULE (*)(PCWSTR new_directory);
 
 namespace {
+// This enum is used to back an UMA histogram, and should therefore be treated
+// as append-only.
+enum LoadLibraryResult {
+  // LoadLibraryExW API/flags are available and the call succeeds.
+  SUCCEED = 0,
+  // LoadLibraryExW API/flags are availabe to use but the call fails, then
+  // LoadLibraryW is used and succeeds.
+  FAIL_AND_SUCCEED,
+  // LoadLibraryExW API/flags are availabe to use but the call fails, then
+  // LoadLibraryW is used but fails as well.
+  FAIL_AND_FAIL,
+  // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
+  // and succeeds.
+  UNAVAILABLE_AND_SUCCEED,
+  // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
+  // but fails.
+  UNAVAILABLE_AND_FAIL,
+  // Add new items before this one, always keep this one at the end.
+  END
+};
+
+// A helper method to log library loading result to UMA.
+void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
+  UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
+                            LoadLibraryResult::END);
+}
+
+// A helper method to check if AddDllDirectory method is available, thus
+// LOAD_LIBRARY_SEARCH_* flags are available on systems.
+bool AreSearchFlagsAvailable() {
+  // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have
+  // KB2533623 installed. To determine whether the flags are available, use
+  // GetProcAddress to get the address of the AddDllDirectory,
+  // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress
+  // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx.
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
+  // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper
+  // method.
+  auto add_dll_dir_func = reinterpret_cast<AddDllDirectory>(
+      GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory"));
+  return !!add_dll_dir_func;
+}
+
+// A helper method to encode the library loading result to enum
+// LoadLibraryResult.
+LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available,
+                                       bool has_load_library_succeeded) {
+  LoadLibraryResult result;
+  if (are_search_flags_available) {
+    if (has_load_library_succeeded)
+      result = LoadLibraryResult::FAIL_AND_SUCCEED;
+    else
+      result = LoadLibraryResult::FAIL_AND_FAIL;
+  } else if (has_load_library_succeeded) {
+    result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED;
+  } else {
+    result = LoadLibraryResult::UNAVAILABLE_AND_FAIL;
+  }
+  return result;
+}
 
 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
-                                      LoadLibraryFunction load_library_api,
                                       NativeLibraryLoadError* error) {
   // LoadLibrary() opens the file off disk.
   ThreadRestrictions::AssertIOAllowed();
 
+  HMODULE module = nullptr;
+
+  // This variable records the library loading result.
+  LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
+
+  bool are_search_flags_available = AreSearchFlagsAvailable();
+  if (are_search_flags_available) {
+    // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
+    // directory as the library may have dependencies on DLLs in this
+    // directory.
+    module = ::LoadLibraryExW(
+        library_path.value().c_str(), nullptr,
+        LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+    // If LoadLibraryExW succeeds, log this metric and return.
+    if (module) {
+      LogLibrarayLoadResultToUMA(load_library_result);
+      return module;
+    }
+    // GetLastError() needs to be called immediately after
+    // LoadLibraryExW call.
+    if (error)
+      error->code = GetLastError();
+  }
+
+  // If LoadLibraryExW API/flags are unavailable or API call fails, try
+  // LoadLibraryW API.
+  // TODO(chengx): Currently, if LoadLibraryExW API call fails, LoadLibraryW is
+  // still tried. We should strictly prefer the LoadLibraryExW over the
+  // LoadLibraryW if LoadLibraryW is statistically showing no extra benefits. If
+  // UMA metric shows that FAIL_AND_FAIL is the primary failure mode and/or
+  // FAIL_AND_SUCCESS is close to zero, we should remove this fallback.
+  // (http://crbug.com/701944)
+
   // Switch the current directory to the library directory as the library
   // may have dependencies on DLLs in this directory.
   bool restore_directory = false;
@@ -36,18 +129,21 @@
     }
   }
 
-  HMODULE module = (*load_library_api)(library_path.value().c_str());
-  if (!module && error) {
-    // GetLastError() needs to be called immediately after |load_library_api|.
+  module = ::LoadLibraryW(library_path.value().c_str());
+
+  // GetLastError() needs to be called immediately after LoadLibraryW call.
+  if (!module && error)
     error->code = GetLastError();
-  }
 
   if (restore_directory)
     SetCurrentDirectory(current_directory);
 
+  // Get the library loading result and log it to UMA.
+  LogLibrarayLoadResultToUMA(
+      GetLoadLibraryResult(are_search_flags_available, !!module));
+
   return module;
 }
-
 }  // namespace
 
 std::string NativeLibraryLoadError::ToString() const {
@@ -58,16 +154,7 @@
 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
                                            const NativeLibraryOptions& options,
                                            NativeLibraryLoadError* error) {
-  return LoadNativeLibraryHelper(library_path, LoadLibraryW, error);
-}
-
-NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) {
-  typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name);
-
-  LoadLibraryFunction load_library = reinterpret_cast<LoadLibraryFunction>(
-      GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"));
-
-  return LoadNativeLibraryHelper(library_path, load_library, NULL);
+  return LoadNativeLibraryHelper(library_path, error);
 }
 
 // static
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 71d6042e..aa26b902 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -188,6 +188,10 @@
   // Returns the number of file descriptors currently open by the process, or
   // -1 on error.
   int GetOpenFdCount() const;
+
+  // Returns the soft limit of file descriptors that can be opened by the
+  // process, or -1 on error.
+  int GetOpenFdSoftLimit() const;
 #endif  // defined(OS_LINUX)
 
  private:
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 5d542cc..e507d16 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -311,6 +311,32 @@
 
   return total_count;
 }
+
+int ProcessMetrics::GetOpenFdSoftLimit() const {
+  // Use /proc/<pid>/limits to read the open fd limit.
+  FilePath fd_path = internal::GetProcPidDir(process_).Append("limits");
+
+  std::string limits_contents;
+  if (!ReadFileToString(fd_path, &limits_contents))
+    return -1;
+
+  for (const auto& line :
+       base::SplitStringPiece(limits_contents, "\n", base::KEEP_WHITESPACE,
+                              base::SPLIT_WANT_NONEMPTY)) {
+    if (line.starts_with("Max open files")) {
+      auto tokens = base::SplitStringPiece(line, " ", base::TRIM_WHITESPACE,
+                                           base::SPLIT_WANT_NONEMPTY);
+      if (tokens.size() > 3) {
+        int limit = -1;
+        if (StringToInt(tokens[3], &limit))
+          return limit;
+        return -1;
+      }
+    }
+  }
+  return -1;
+}
+
 #endif  // defined(OS_LINUX)
 
 ProcessMetrics::ProcessMetrics(ProcessHandle process)
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
index aaa5c660..922536b 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
@@ -6,9 +6,10 @@
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import android.support.test.internal.util.AndroidRunnerParams;
 
 import org.junit.runner.notification.RunNotifier;
-import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.InitializationError;
 
@@ -25,8 +26,13 @@
 
 /**
  *  A custom runner for JUnit4 tests that checks requirements to conditionally ignore tests.
+ *
+ *  This ClassRunner imports from AndroidJUnit4ClassRunner which is a hidden but accessible
+ *  class. The reason is that default JUnit4 runner for Android is a final class,
+ *  {@link AndroidJUnit4}. We need to extends an inheritable class to change {@link runChild}
+ *  and {@link isIgnored} to add SkipChecks and PreTesthook.
  */
-public class BaseJUnit4ClassRunner extends BlockJUnit4ClassRunner {
+public class BaseJUnit4ClassRunner extends AndroidJUnit4ClassRunner {
     private final List<SkipCheck> mSkipChecks;
     private final List<PreTestHook> mPreTestHooks;
 
@@ -74,7 +80,9 @@
     public BaseJUnit4ClassRunner(
             final Class<?> klass, List<SkipCheck> checks, List<PreTestHook> hooks)
             throws InitializationError {
-        super(klass);
+        super(klass,
+                new AndroidRunnerParams(InstrumentationRegistry.getInstrumentation(),
+                        InstrumentationRegistry.getArguments(), false, 0L, false));
         mSkipChecks = mergeList(checks, defaultSkipChecks());
         mPreTestHooks = defaultPreTestHooks();
     }
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn
index 406f3f60..53366db 100644
--- a/build/android/BUILD.gn
+++ b/build/android/BUILD.gn
@@ -125,16 +125,6 @@
          ]
 }
 
-group("logdog_wrapper_py") {
-  _py_files = read_file("test_wrapper/logdog_wrapper.pydeps", "list lines")
-
-  # Filter out comments.
-  set_sources_assignment_filter([ "#*" ])
-  sources = _py_files
-
-  data = sources
-}
-
 # Create wrapper scripts in out/bin that takes care of setting the
 # --output-directory.
 _scripts_to_wrap = [
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 2c7fe8e..b3c1b97 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -12,7 +12,6 @@
 import itertools
 import logging
 import os
-import shutil
 import signal
 import sys
 import threading
@@ -35,7 +34,6 @@
 from pylib.base import test_run_factory
 from pylib.results import json_results
 from pylib.results import report_results
-from pylib.utils import logdog_helper
 
 from py_utils import contextlib_ext
 
@@ -218,12 +216,6 @@
       help='Run the test under a tool '
            '(use --tool help to list them)')
 
-  parser.add_argument(
-      '--upload-logcats-file',
-      action='store_true',
-      dest='upload_logcats_file',
-      help='Whether to upload logcat file to logdog.')
-
   logcat_output_group = parser.add_mutually_exclusive_group()
   logcat_output_group.add_argument(
       '--logcat-output-dir', type=os.path.realpath,
@@ -741,24 +733,6 @@
       write_json_file(),
       args.json_results_file)
 
-  @contextlib.contextmanager
-  def upload_logcats_file():
-    try:
-      yield
-    finally:
-      if not args.logcat_output_file:
-        logging.critical('Cannot upload logcats file. '
-                        'File to save logcat is not specified.')
-      else:
-        with open(args.logcat_output_file) as src:
-          dst = logdog_helper.open_text('unified_logcats')
-          if dst:
-            shutil.copyfileobj(src, dst)
-
-  logcats_uploader = contextlib_ext.Optional(
-      upload_logcats_file(),
-      'upload_logcats_file' in args and args.upload_logcats_file)
-
   ### Set up test objects.
 
   env = environment_factory.CreateEnvironment(args, infra_error)
@@ -768,7 +742,7 @@
 
   ### Run.
 
-  with json_writer, logcats_uploader, env, test_instance, test_run:
+  with json_writer, env, test_instance, test_run:
 
     repetitions = (xrange(args.repeat + 1) if args.repeat >= 0
                    else itertools.count())
diff --git a/build/android/test_wrapper/logdog_wrapper.py b/build/android/test_wrapper/logdog_wrapper.py
index 0296650..14ed7d13 100755
--- a/build/android/test_wrapper/logdog_wrapper.py
+++ b/build/android/test_wrapper/logdog_wrapper.py
@@ -11,76 +11,81 @@
 import signal
 import subprocess
 import sys
+import urllib
 
-_SRC_PATH = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '..', '..', '..'))
-sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'catapult', 'devil'))
-sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'catapult', 'common',
-                             'py_utils'))
-
-from devil.utils import signal_handler
-from py_utils import tempfile_ext
-
-PROJECT = 'chromium'
-OUTPUT = 'logdog'
-COORDINATOR_HOST = 'luci-logdog.appspot.com'
-SERVICE_ACCOUNT_JSON = ('/creds/service_accounts'
-                        '/service-account-luci-logdog-publisher.json')
 
 def CommandParser():
   # Parses the command line arguments being passed in
   parser = argparse.ArgumentParser()
-  parser.add_argument('--target', required=True,
-                      help='The test target to be run.')
   parser.add_argument('--logdog-bin-cmd', required=True,
-                      help='The logdog bin cmd.')
+                      help='Command for running logdog butler binary')
+  parser.add_argument('--project', required=True,
+                      help='Name of logdog project')
+  parser.add_argument('--logdog-server',
+                      default='services-dot-luci-logdog.appspot.com',
+                      help='URL of logdog server, https:// is assumed.')
+  parser.add_argument('--service-account-json', required=True,
+                      help='Location of authentication json')
+  parser.add_argument('--prefix', required=True,
+                      help='Prefix to be used for logdog stream')
+  parser.add_argument('--source', required=True,
+                      help='Location of file for logdog to stream')
+  parser.add_argument('--name', required=True,
+                      help='Name to be used for logdog stream')
   return parser
 
-def CreateStopTestsMethod(proc):
-  def StopTests(signum, _frame):
+
+def CreateUrl(server, project, prefix, name):
+  stream_name = '%s/%s/+/%s' % (project, prefix, name)
+  return 'https://%s/v/?s=%s' % (server, urllib.quote_plus(stream_name))
+
+
+def CreateSignalForwarder(proc):
+  def handler(signum, _frame):
     logging.error('Forwarding signal %s to test process', str(signum))
     proc.send_signal(signum)
-  return StopTests
+
+  return handler
+
 
 def main():
   parser = CommandParser()
-  args, extra_cmd_args = parser.parse_known_args(sys.argv[1:])
-
+  args, test_cmd = parser.parse_known_args(sys.argv[1:])
   logging.basicConfig(level=logging.INFO)
-  with tempfile_ext.NamedTemporaryDirectory() as logcat_output_dir:
-    test_cmd = [
-        os.path.join('bin', 'run_%s' % args.target),
-        '--logcat-output-file', os.path.join(logcat_output_dir, 'logcats'),
-        '--upload-logcats-file',
-        '--target-devices-file', '${SWARMING_BOT_FILE}',
-        '-v'] + extra_cmd_args
+  if not test_cmd:
+    parser.error('Must specify command to run after the logdog flags')
+  test_proc = subprocess.Popen(test_cmd)
+  original_sigterm_handler = signal.signal(
+      signal.SIGTERM, CreateSignalForwarder(test_proc))
+  try:
+    result = test_proc.wait()
+  finally:
+    signal.signal(signal.SIGTERM, original_sigterm_handler)
+  if '${SWARMING_TASK_ID}' in args.prefix:
+    args.prefix = args.prefix.replace('${SWARMING_TASK_ID}',
+                                      os.environ.get('SWARMING_TASK_ID'))
+  url = CreateUrl('luci-logdog.appspot.com', args.project, args.prefix,
+                  args.name)
+  logdog_cmd = [args.logdog_bin_cmd, '-project', args.project,
+                '-output', 'logdog,host=%s' % args.logdog_server,
+                '-prefix', args.prefix,
+                '-service-account-json', args.service_account_json,
+                'stream', '-source', args.source,
+                '-stream', '-name=%s' % args.name]
 
-    with tempfile_ext.NamedTemporaryDirectory(
-        prefix='tmp_android_logdog_wrapper') as temp_directory:
-      if not os.path.exists(args.logdog_bin_cmd):
-        logging.error(
-            'Logdog binary %s unavailable. Unable to create logdog client',
-            args.logdog_bin_cmd)
-      else:
-        streamserver_uri = 'unix:%s' % os.path.join(temp_directory,
-                                                    'butler.sock')
-        prefix = os.path.join('android', 'swarming', 'logcats',
-                              os.environ.get('SWARMING_TASK_ID'))
+  if not os.path.exists(args.logdog_bin_cmd):
+    logging.error(
+        'Logdog binary %s unavailable. Unable to upload logcats.',
+        args.logdog_bin_cmd)
+  elif not os.path.exists(args.source):
+    logging.error(
+        'Logcat sources not found at %s. Unable to upload logcats.',
+        args.source)
+  else:
+    subprocess.call(logdog_cmd)
+    logging.info('Logcats are located at: %s', url)
+  return result
 
-        # Call test_cmdline through logdog butler subcommand.
-        test_cmd = [
-            args.logdog_bin_cmd, '-project', PROJECT,
-            '-output', OUTPUT,
-            '-prefix', prefix,
-            '--service-account-json', SERVICE_ACCOUNT_JSON,
-            '-coordinator-host', COORDINATOR_HOST,
-            'run', '-streamserver-uri', streamserver_uri, '--'] + test_cmd
-
-      test_proc = subprocess.Popen(test_cmd)
-      with signal_handler.SignalHandler(signal.SIGTERM,
-                                        CreateStopTestsMethod(test_proc)):
-        result = test_proc.wait()
-    return result
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/build/android/test_wrapper/logdog_wrapper.pydeps b/build/android/test_wrapper/logdog_wrapper.pydeps
deleted file mode 100644
index cd57f2fd..0000000
--- a/build/android/test_wrapper/logdog_wrapper.pydeps
+++ /dev/null
@@ -1,11 +0,0 @@
-# Generated by running:
-#   build/print_python_deps.py --root build/android --output build/android/test_wrapper/logdog_wrapper.pydeps build/android/test_wrapper/logdog_wrapper.py
-../../third_party/catapult/common/py_utils/py_utils/__init__.py
-../../third_party/catapult/common/py_utils/py_utils/tempfile_ext.py
-../../third_party/catapult/devil/devil/__init__.py
-../../third_party/catapult/devil/devil/utils/__init__.py
-../../third_party/catapult/devil/devil/utils/reraiser_thread.py
-../../third_party/catapult/devil/devil/utils/signal_handler.py
-../../third_party/catapult/devil/devil/utils/timeout_retry.py
-../../third_party/catapult/devil/devil/utils/watchdog_timer.py
-test_wrapper/logdog_wrapper.py
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 9ef8d27..d2d9f99 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -483,11 +483,7 @@
     script = "//build/android/gyp/create_test_runner_script.py"
     depfile = "$target_gen_dir/$target_name.d"
 
-    data_deps += [
-      "//build/android:test_runner_py",
-      "//build/android:logdog_wrapper_py",
-    ]
-
+    data_deps += [ "//build/android:test_runner_py" ]
     data = []
 
     test_runner_args = [
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index 845d992..2a6c578 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -385,6 +385,10 @@
       ]
     }
 
+    if (use_cfi_icall) {
+      cflags += [ "-fsanitize=cfi-icall" ]
+    }
+
     if (use_cfi_diag) {
       cflags += [
         "-fno-sanitize-trap=cfi",
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 2a91c3d..6feef783 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -62,6 +62,11 @@
   # https://crbug.com/626794
   use_cfi_cast = false
 
+  # Enable checks for indirect function calls via a function pointer.
+  # TODO(pcc): remove this when we're ready to add these checks by default.
+  # https://crbug.com/701919
+  use_cfi_icall = false
+
   # By default, Control Flow Integrity will crash the program if it detects a
   # violation. Set this to true to print detailed diagnostics instead.
   use_cfi_diag = false
diff --git a/cc/input/scrollbar_animation_controller.cc b/cc/input/scrollbar_animation_controller.cc
index 576ec2a..08bb917 100644
--- a/cc/input/scrollbar_animation_controller.cc
+++ b/cc/input/scrollbar_animation_controller.cc
@@ -53,7 +53,6 @@
       scroll_gesture_has_scrolled_(false),
       opacity_(0.0f),
       fade_out_duration_(fade_out_duration),
-      show_scrollbars_on_scroll_gesture_(false),
       need_thinning_animation_(false),
       weak_factory_(this) {
   ApplyOpacityToScrollbars(0.0f);
@@ -78,7 +77,6 @@
       scroll_gesture_has_scrolled_(false),
       opacity_(0.0f),
       fade_out_duration_(fade_out_duration),
-      show_scrollbars_on_scroll_gesture_(true),
       need_thinning_animation_(true),
       weak_factory_(this) {
   vertical_controller_ = SingleScrollbarAnimationControllerThinning::Create(
@@ -168,14 +166,39 @@
   return std::max(std::min(progress, 1.f), 0.f);
 }
 
+void ScrollbarAnimationController::DidScrollBegin() {
+  currently_scrolling_ = true;
+}
+
 void ScrollbarAnimationController::RunAnimationFrame(float progress) {
   ApplyOpacityToScrollbars(1.f - progress);
   if (progress == 1.f)
     StopAnimation();
 }
 
-void ScrollbarAnimationController::DidScrollBegin() {
-  currently_scrolling_ = true;
+void ScrollbarAnimationController::DidScrollUpdate(bool on_resize) {
+  if (need_thinning_animation_ && Captured())
+    return;
+
+  StopAnimation();
+
+  // As an optimization, we avoid spamming fade delay tasks during active fast
+  // scrolls.  But if we're not within one, we need to post every scroll update.
+  if (!currently_scrolling_) {
+    // We don't fade out scrollbar if they need thinning animation and mouse is
+    // near.
+    if (!need_thinning_animation_ || !MouseIsNearAnyScrollbar())
+      PostDelayedFadeOut(on_resize);
+  } else {
+    scroll_gesture_has_scrolled_ = true;
+  }
+
+  Show();
+
+  if (need_thinning_animation_) {
+    vertical_controller_->UpdateThumbThicknessScale();
+    horizontal_controller_->UpdateThumbThicknessScale();
+  }
 }
 
 void ScrollbarAnimationController::DidScrollEnd() {
@@ -193,44 +216,6 @@
     PostDelayedFadeOut(false);
 }
 
-void ScrollbarAnimationController::DidScrollUpdate() {
-  if (need_thinning_animation_ && Captured())
-    return;
-
-  StopAnimation();
-
-  // As an optimization, we avoid spamming fade delay tasks during active fast
-  // scrolls.  But if we're not within one, we need to post every scroll update.
-  if (!currently_scrolling_) {
-    // We don't fade out scrollbar if they need thinning animation and mouse is
-    // near.
-    if (!need_thinning_animation_ || !MouseIsNearAnyScrollbar())
-      PostDelayedFadeOut(false);
-  } else {
-    scroll_gesture_has_scrolled_ = true;
-  }
-
-  Show();
-
-  if (need_thinning_animation_) {
-    vertical_controller_->UpdateThumbThicknessScale();
-    horizontal_controller_->UpdateThumbThicknessScale();
-  }
-}
-
-void ScrollbarAnimationController::WillUpdateScroll() {
-  if (show_scrollbars_on_scroll_gesture_)
-    DidScrollUpdate();
-}
-
-void ScrollbarAnimationController::DidResize() {
-  StopAnimation();
-  Show();
-  // We should use the gesture delay rather than the resize delay if we're in a
-  // gesture scroll, even if it is resizing.
-  PostDelayedFadeOut(!currently_scrolling_);
-}
-
 void ScrollbarAnimationController::DidMouseDown() {
   if (!need_thinning_animation_ || ScrollbarsHidden())
     return;
diff --git a/cc/input/scrollbar_animation_controller.h b/cc/input/scrollbar_animation_controller.h
index 2623543..02f8e97 100644
--- a/cc/input/scrollbar_animation_controller.h
+++ b/cc/input/scrollbar_animation_controller.h
@@ -65,19 +65,8 @@
 
   bool Animate(base::TimeTicks now);
 
-  // WillUpdateScroll expects to be called even if the scroll position won't
-  // change as a result of the scroll. Only effect Aura Overlay Scrollbar.
-  void WillUpdateScroll();
-
-  // DidScrollUpdate expects to be called only if the scroll position change.
-  // Effect both Android and Aura Overlay Scrollbar.
-  void DidScrollUpdate();
-
-  // DidResize expects to be called when clip layer size changed or scroll layer
-  // size changed.
-  void DidResize();
-
   void DidScrollBegin();
+  void DidScrollUpdate(bool on_resize);
   void DidScrollEnd();
 
   void DidMouseDown();
@@ -154,7 +143,6 @@
   float opacity_;
   base::TimeDelta fade_out_duration_;
 
-  const bool show_scrollbars_on_scroll_gesture_;
   const bool need_thinning_animation_;
   std::unique_ptr<SingleScrollbarAnimationControllerThinning>
       vertical_controller_;
diff --git a/cc/input/scrollbar_animation_controller_unittest.cc b/cc/input/scrollbar_animation_controller_unittest.cc
index c4470c14..3d0855e 100644
--- a/cc/input/scrollbar_animation_controller_unittest.cc
+++ b/cc/input/scrollbar_animation_controller_unittest.cc
@@ -145,18 +145,18 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
   ExpectScrollbarsOpacity(1);
 
   // Make the Layer non-scrollable, scrollbar disappears.
   clip_layer_->SetBounds(gfx::Size(200, 200));
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   ExpectScrollbarsOpacity(0);
 
   // Make the layer scrollable, scrollbar appears again.
   clip_layer_->SetBounds(gfx::Size(100, 100));
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   ExpectScrollbarsOpacity(1);
 }
 
@@ -175,7 +175,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(1, h_scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -187,7 +187,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(0.0f, h_scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -207,7 +207,7 @@
   ExpectScrollbarsOpacity(0);
   EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   ExpectScrollbarsOpacity(1);
   EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
 
@@ -229,35 +229,6 @@
   EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
 }
 
-// Confirm the scrollbar appears by WillUpdateScroll and fade out.
-TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
-       BasicAppearByWillUpdateScrollThenFadeOut) {
-  base::TimeTicks time;
-  time += base::TimeDelta::FromSeconds(1);
-
-  // Scrollbar should be invisible.
-  ExpectScrollbarsOpacity(0);
-  EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
-
-  // Scrollbar should appear when scroll will update.
-  scrollbar_controller_->WillUpdateScroll();
-  ExpectScrollbarsOpacity(1);
-  EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
-
-  // An fade out animation should have been enqueued.
-  EXPECT_EQ(kFadeOutDelay, client_.delay());
-  EXPECT_FALSE(client_.start_fade().is_null());
-  client_.start_fade().Run();
-
-  // Scrollbar should fade out over kFadeOutDuration.
-  scrollbar_controller_->Animate(time);
-  time += kFadeOutDuration;
-  scrollbar_controller_->Animate(time);
-
-  ExpectScrollbarsOpacity(0);
-  EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
-}
-
 // Scroll content. Move the mouse near the scrollbar and confirm it becomes
 // thick. Ensure it remains visible as long as the mouse is near the scrollbar.
 TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MoveNearAndDontFadeOut) {
@@ -265,7 +236,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -306,7 +277,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -348,7 +319,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -376,7 +347,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -412,7 +383,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -449,7 +420,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -500,7 +471,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // An fade out animation should have been enqueued.
@@ -539,7 +510,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // A fade out animation should have been enqueued. Start it.
@@ -579,7 +550,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   EXPECT_EQ(kFadeOutDelay, client_.delay());
@@ -643,7 +614,7 @@
                   h_scrollbar_layer_->thumb_thickness_scale_factor());
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
 
   // Now that we've received a scroll, we should be thick without an animation.
   ExpectScrollbarsOpacity(1);
@@ -678,7 +649,7 @@
   // A ScrollUpdate without a ScrollBegin indicates a main thread scroll update
   // so we should schedule a fade out animation without waiting for a ScrollEnd
   // (which will never come).
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(client_.start_fade().is_null());
   EXPECT_EQ(kFadeOutDelay, client_.delay());
 
@@ -687,7 +658,7 @@
   // If we got a ScrollBegin, we shouldn't schedule the fade out animation until
   // we get a corresponding ScrollEnd.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_TRUE(client_.start_fade().is_null());
   scrollbar_controller_->DidScrollEnd();
   EXPECT_FALSE(client_.start_fade().is_null());
@@ -699,16 +670,16 @@
 TEST_F(ScrollbarAnimationControllerAuraOverlayTest, ResizeFadeDuration) {
   ASSERT_TRUE(client_.delay().is_zero());
 
-  scrollbar_controller_->DidResize();
+  scrollbar_controller_->DidScrollUpdate(true);
   EXPECT_FALSE(client_.start_fade().is_null());
   EXPECT_EQ(kResizeFadeOutDelay, client_.delay());
 
   client_.delay() = base::TimeDelta();
 
   // We should use the gesture delay rather than the resize delay if we're in a
-  // gesture scroll, even if it is resizing.
+  // gesture scroll, even if the resize param is set.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidResize();
+  scrollbar_controller_->DidScrollUpdate(true);
   scrollbar_controller_->DidScrollEnd();
 
   EXPECT_FALSE(client_.start_fade().is_null());
@@ -722,7 +693,7 @@
 
   // Scroll to make the scrollbars visible.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // Appearance is instant.
@@ -754,7 +725,7 @@
   EXPECT_CALL(client_, DidChangeScrollbarVisibility()).Times(1);
   // Scroll to make the scrollbars visible.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
   Mock::VerifyAndClearExpectations(&client_);
 
@@ -789,7 +760,7 @@
   // Calling DidScrollUpdate without a begin (i.e. update from commit) should
   // also notify.
   EXPECT_CALL(client_, DidChangeScrollbarVisibility()).Times(1);
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
   Mock::VerifyAndClearExpectations(&client_);
 }
@@ -802,7 +773,7 @@
 
   // Scroll to make the scrollbars visible.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // Near vertical scrollbar
@@ -895,7 +866,7 @@
 
   // Scroll to make the scrollbars visible.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // Near both Scrollbar
@@ -925,7 +896,7 @@
 
   // Scroll to make the scrollbars visible.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // Near vertical scrollbar.
@@ -991,7 +962,7 @@
 
   // Scroll to make the scrollbars visible.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   // Should not have delay fadeout animation.
@@ -1177,10 +1148,8 @@
       ->effect_tree.OnOpacityAnimated(0.0f,
                                       scrollbar_layer_->effect_tree_index(),
                                       scrollbar_layer_->layer_tree_impl());
-  // We should use the gesture delay rather than the resize delay if we're in a
-  // gesture scroll, even if it is resizing.
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidResize();
+  scrollbar_controller_->DidScrollUpdate(true);
   scrollbar_controller_->DidScrollEnd();
   // Normal Animation delay of 2 seconds.
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
@@ -1191,7 +1160,7 @@
       ->effect_tree.OnOpacityAnimated(0.0f,
                                       scrollbar_layer_->effect_tree_index(),
                                       scrollbar_layer_->layer_tree_impl());
-  scrollbar_controller_->DidResize();
+  scrollbar_controller_->DidScrollUpdate(true);
   // Delay animation on resize to 5 seconds.
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
   EXPECT_EQ(delay_, base::TimeDelta::FromSeconds(5));
@@ -1229,25 +1198,6 @@
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
 }
 
-// Confirm the scrollbar does not appear on WillUpdateScroll on Android.
-TEST_F(ScrollbarAnimationControllerAndroidTest,
-       WillUpdateScrollNotAppearScrollbar) {
-  base::TimeTicks time;
-  time += base::TimeDelta::FromSeconds(1);
-
-  // Scrollbar should be invisible.
-  EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
-  EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
-
-  // Scrollbar should appear when scroll will update.
-  scrollbar_controller_->WillUpdateScroll();
-  EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
-  EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
-
-  // No fade out animation should have been enqueued.
-  EXPECT_TRUE(start_fade_.Equals(base::Closure()));
-}
-
 TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnResize) {
   LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1);
   ASSERT_TRUE(scroll_layer);
@@ -1261,7 +1211,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1273,7 +1223,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1292,7 +1242,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1303,7 +1253,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1318,7 +1268,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1333,7 +1283,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1349,7 +1299,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1365,7 +1315,7 @@
 
   scrollbar_controller_->DidScrollBegin();
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
 
   scrollbar_controller_->DidScrollEnd();
@@ -1377,7 +1327,7 @@
   scrollbar_controller_->DidScrollBegin();
   EXPECT_FALSE(did_request_animate_);
 
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
 
@@ -1415,7 +1365,7 @@
   time += base::TimeDelta::FromSeconds(1);
 
   scrollbar_controller_->DidScrollBegin();
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   scrollbar_controller_->DidScrollEnd();
 
   start_fade_.Run();
@@ -1449,7 +1399,7 @@
 TEST_F(ScrollbarAnimationControllerAndroidTest, AwakenByProgrammaticScroll) {
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(did_request_animate_);
 
   start_fade_.Run();
@@ -1465,7 +1415,7 @@
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->Opacity());
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(did_request_animate_);
 
   start_fade_.Run();
@@ -1490,7 +1440,7 @@
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->Opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   start_fade_.Run();
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->Animate(time);
@@ -1520,7 +1470,7 @@
        AnimationPreservedByNonScrollingGesture) {
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   start_fade_.Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
@@ -1559,7 +1509,7 @@
        AnimationOverriddenByScrollingGesture) {
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(did_request_animate_);
   start_fade_.Run();
   EXPECT_TRUE(did_request_animate_);
@@ -1585,7 +1535,7 @@
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->Opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  scrollbar_controller_->DidScrollUpdate();
+  scrollbar_controller_->DidScrollUpdate(false);
   EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1, scrollbar_layer_->Opacity());
 
diff --git a/cc/output/color_lut_cache.cc b/cc/output/color_lut_cache.cc
index 4cc7220..1a796db 100644
--- a/cc/output/color_lut_cache.cc
+++ b/cc/output/color_lut_cache.cc
@@ -103,11 +103,11 @@
   }
 
   LUT lut;
-  // If input is HDR, and the output is scRGB, we're going to need
+  // If input is HDR, and the output is full range, we're going to need
   // to produce values outside of 0-1, so we'll need to make a half-float
   // LUT. Also, we'll need to build a larger lut to maintain accuracy.
-  // All LUT sizes should be odd a some transforms hav a knee at 0.5.
-  if (transform->GetDstColorSpace() == gfx::ColorSpace::CreateSCRGBLinear() &&
+  // All LUT sizes should be odd as some transforms have a knee at 0.5.
+  if (transform->GetDstColorSpace().FullRangeEncodedValues() &&
       transform->GetSrcColorSpace().IsHDR() && texture_half_float_linear_) {
     lut.size = 37;
     lut.texture = MakeLUT<uint16_t>(transform, lut.size);
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index dfd32560..7c53b513 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -150,7 +150,7 @@
   current_canvas_->resetMatrix();
   // TODO(fmalita) stop using kReplace (see crbug.com/673851)
   current_canvas_->clipRect(gfx::RectToSkRect(rect),
-                            SkClipOp::kReplace_private_internal_do_not_use);
+                            SkClipOp::kReplace_deprecated);
   current_canvas_->setMatrix(current_matrix);
 }
 
diff --git a/cc/playback/discardable_image_map.cc b/cc/playback/discardable_image_map.cc
index cf424fe..c6129d1d 100644
--- a/cc/playback/discardable_image_map.cc
+++ b/cc/playback/discardable_image_map.cc
@@ -172,6 +172,7 @@
         return false;
       *paint_bounds = paint.computeFastBounds(*paint_bounds, paint_bounds);
     }
+
     return true;
   }
 
@@ -202,6 +203,18 @@
     src_rect.roundOut(&src_irect);
     gfx::Rect image_rect = SafeClampPaintRectToSize(paint_rect, canvas_size_);
 
+    // During raster, we use the device clip bounds on the canvas, which outsets
+    // the actual clip by 1 due to the possibility of antialiasing. Account for
+    // this here by outsetting the image rect by 1. Note that this only affects
+    // queries into the rtree, which will now return images that only touch the
+    // bounds of the query rect.
+    //
+    // Note that it's not sufficient for us to inset the device clip bounds at
+    // raster time, since we might be sending a larger-than-one-item display
+    // item to skia, which means that skia will internally determine whether to
+    // raster the picture (using device clip bounds that are outset).
+    image_rect.Inset(-1, -1);
+
     (*image_id_to_rect_)[image->uniqueID()].Union(image_rect);
     image_set_->push_back(std::make_pair(
         DrawImage(std::move(image), src_irect, filter_quality, matrix),
diff --git a/cc/playback/discardable_image_map_unittest.cc b/cc/playback/discardable_image_map_unittest.cc
index ff8441e..eee782e 100644
--- a/cc/playback/discardable_image_map_unittest.cc
+++ b/cc/playback/discardable_image_map_unittest.cc
@@ -60,6 +60,18 @@
       EXPECT_TRUE(draw_images[i].image() == position_draw_images[i].image);
     return position_draw_images;
   }
+
+  // Note that the image rtree outsets the images by 1, see the comment in
+  // DiscardableImagesMetadataCanvas::AddImage.
+  std::vector<gfx::Rect> InsetImageRects(
+      const std::vector<PositionScaleDrawImage>& images) {
+    std::vector<gfx::Rect> result;
+    for (auto& image : images) {
+      result.push_back(image.image_rect);
+      result.back().Inset(1, 1, 1, 1);
+    }
+    return result;
+  }
 };
 
 TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) {
@@ -105,12 +117,13 @@
     for (int x = 0; x < 4; ++x) {
       std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(x * 512, y * 512, 500, 500));
+      std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
         EXPECT_TRUE(images[0].image == discardable_image[y][x]) << x << " "
                                                                 << y;
         EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
-                  images[0].image_rect);
+                  inset_rects[0]);
         EXPECT_EQ(images[0].image_rect,
                   image_map.GetRectForImage(images[0].image->uniqueID()));
       } else {
@@ -122,27 +135,26 @@
   // Capture 4 pixel refs.
   std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048));
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
   EXPECT_EQ(4u, images.size());
 
   EXPECT_TRUE(images[0].image == discardable_image[1][2]);
-  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]);
   EXPECT_EQ(images[0].image_rect,
             image_map.GetRectForImage(images[0].image->uniqueID()));
 
   EXPECT_TRUE(images[1].image == discardable_image[2][1]);
-  EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), images[1].image_rect);
+  EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]);
   EXPECT_EQ(images[1].image_rect,
             image_map.GetRectForImage(images[1].image->uniqueID()));
 
   EXPECT_TRUE(images[2].image == discardable_image[2][3]);
-  EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500),
-            images[2].image_rect);
+  EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]);
   EXPECT_EQ(images[2].image_rect,
             image_map.GetRectForImage(images[2].image->uniqueID()));
 
   EXPECT_TRUE(images[3].image == discardable_image[3][2]);
-  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500),
-            images[3].image_rect);
+  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]);
   EXPECT_EQ(images[3].image_rect,
             image_map.GetRectForImage(images[3].image->uniqueID()));
 }
@@ -192,12 +204,13 @@
     for (int x = 0; x < 4; ++x) {
       std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(1024 + x * 512, y * 512, 500, 500));
+      std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
         EXPECT_TRUE(images[0].image == discardable_image[y][x]) << x << " "
                                                                 << y;
         EXPECT_EQ(gfx::Rect(1024 + x * 512 + 6, y * 512 + 6, 500, 500),
-                  images[0].image_rect);
+                  inset_rects[0]);
         EXPECT_EQ(images[0].image_rect,
                   image_map.GetRectForImage(images[0].image->uniqueID()));
       } else {
@@ -209,29 +222,28 @@
   {
     std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
         image_map, gfx::Rect(1024 + 512, 512, 2048, 2048));
+    std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
     EXPECT_EQ(4u, images.size());
 
     EXPECT_TRUE(images[0].image == discardable_image[1][2]);
-    EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 512 + 6, 500, 500),
-              images[0].image_rect);
+    EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]);
     EXPECT_EQ(images[0].image_rect,
               image_map.GetRectForImage(images[0].image->uniqueID()));
 
     EXPECT_TRUE(images[1].image == discardable_image[2][1]);
-    EXPECT_EQ(gfx::Rect(1024 + 512 + 6, 2 * 512 + 6, 500, 500),
-              images[1].image_rect);
+    EXPECT_EQ(gfx::Rect(1024 + 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]);
     EXPECT_EQ(images[1].image_rect,
               image_map.GetRectForImage(images[1].image->uniqueID()));
 
     EXPECT_TRUE(images[2].image == discardable_image[2][3]);
     EXPECT_EQ(gfx::Rect(1024 + 3 * 512 + 6, 2 * 512 + 6, 500, 500),
-              images[2].image_rect);
+              inset_rects[2]);
     EXPECT_EQ(images[2].image_rect,
               image_map.GetRectForImage(images[2].image->uniqueID()));
 
     EXPECT_TRUE(images[3].image == discardable_image[3][2]);
     EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 3 * 512 + 6, 500, 500),
-              images[3].image_rect);
+              inset_rects[3]);
     EXPECT_EQ(images[3].image_rect,
               image_map.GetRectForImage(images[3].image->uniqueID()));
   }
@@ -308,12 +320,13 @@
     for (int x = 0; x < 4; ++x) {
       std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(x * 512 + 256, y * 512 + 256, 1, 1));
+      std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
         EXPECT_TRUE(images[0].image == discardable_image[y][x]) << x << " "
                                                                 << y;
         EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
-                  images[0].image_rect);
+                  inset_rects[0]);
         EXPECT_EQ(images[0].image_rect,
                   image_map.GetRectForImage(images[0].image->uniqueID()));
       } else {
@@ -346,9 +359,10 @@
   }
   std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
-  EXPECT_EQ(gfx::Rect(0, 0, 2048, 2048), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(0, 0, 2048, 2048), inset_rects[0]);
   EXPECT_EQ(images[0].image_rect,
             image_map.GetRectForImage(images[0].image->uniqueID()));
 }
@@ -426,9 +440,10 @@
   }
   std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(42, 42, 1, 1));
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
-  EXPECT_EQ(gfx::Rect(42, 42, 2006, 2006), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(42, 42, 2006, 2006), inset_rects[0]);
   EXPECT_EQ(images[0].image_rect,
             image_map.GetRectForImage(images[0].image->uniqueID()));
 }
@@ -467,14 +482,15 @@
   }
   std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
   EXPECT_EQ(1u, images.size());
-  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]);
 
   images = GetDiscardableImagesInRect(image_map, gfx::Rect(10000, 0, 1, 1));
+  inset_rects = InsetImageRects(images);
   EXPECT_EQ(2u, images.size());
-  EXPECT_EQ(gfx::Rect(10000, 0, dimension - 10000, dimension),
-            images[1].image_rect);
-  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(10000, 0, dimension - 10000, dimension), inset_rects[1]);
+  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]);
 
   // Since we adjust negative offsets before using ToEnclosingRect, the expected
   // width will be converted to float, which means that we lose some precision.
@@ -482,12 +498,12 @@
   // back to int.
   int expected10k = static_cast<int>(static_cast<float>(dimension - 10000));
   images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 500, 1, 1));
+  inset_rects = InsetImageRects(images);
   EXPECT_EQ(2u, images.size());
-  EXPECT_EQ(gfx::Rect(0, 500, expected10k, dimension - 500),
-            images[1].image_rect);
-  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(0, 500, expected10k, dimension - 500), inset_rects[1]);
+  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]);
 
-  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension),
+  EXPECT_EQ(images[0].image_rect,
             image_map.GetRectForImage(discardable_image->uniqueID()));
 }
 
@@ -521,24 +537,28 @@
   }
   std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
   EXPECT_EQ(1u, images.size());
-  EXPECT_EQ(gfx::Rect(0, 0, 90, 89), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(0, 0, 90, 89), inset_rects[0]);
 
   images = GetDiscardableImagesInRect(image_map, gfx::Rect(999, 999, 1, 1));
+  inset_rects = InsetImageRects(images);
   EXPECT_EQ(1u, images.size());
-  EXPECT_EQ(gfx::Rect(950, 951, 50, 49), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(950, 951, 50, 49), inset_rects[0]);
 
   images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 500, 1, 1));
+  inset_rects = InsetImageRects(images);
   EXPECT_EQ(1u, images.size());
-  EXPECT_EQ(gfx::Rect(0, 500, 1000, 100), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(0, 500, 1000, 100), inset_rects[0]);
 
   gfx::Rect discardable_image_rect;
   discardable_image_rect.Union(gfx::Rect(0, 0, 90, 89));
   discardable_image_rect.Union(gfx::Rect(950, 951, 50, 49));
+  discardable_image_rect.Inset(-1, -1, -1, -1);
   EXPECT_EQ(discardable_image_rect,
             image_map.GetRectForImage(discardable_image->uniqueID()));
 
-  EXPECT_EQ(gfx::Rect(0, 500, 1000, 100),
+  EXPECT_EQ(gfx::Rect(-1, 499, 1002, 102),
             image_map.GetRectForImage(long_discardable_image->uniqueID()));
 }
 
@@ -593,12 +613,13 @@
     for (int x = 0; x < 4; ++x) {
       std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(x * 512, y * 512, 500, 500));
+      std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
         EXPECT_TRUE(images[0].image == discardable_image[y][x]) << x << " "
                                                                 << y;
         EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
-                  images[0].image_rect);
+                  inset_rects[0]);
         EXPECT_EQ(std::max(x * 0.5f, kMinScale), images[0].scale.fWidth);
         EXPECT_EQ(std::max(y * 0.5f, kMinScale), images[0].scale.fHeight);
       } else {
@@ -610,17 +631,16 @@
   // Capture 4 pixel refs.
   std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048));
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
   EXPECT_EQ(4u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image[1][2]);
-  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), images[0].image_rect);
+  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]);
   EXPECT_TRUE(images[1].image == discardable_image[2][1]);
-  EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), images[1].image_rect);
+  EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]);
   EXPECT_TRUE(images[2].image == discardable_image[2][3]);
-  EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500),
-            images[2].image_rect);
+  EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]);
   EXPECT_TRUE(images[3].image == discardable_image[3][2]);
-  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500),
-            images[3].image_rect);
+  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]);
 }
 
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 0051d95..c7238e8 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2904,15 +2904,7 @@
       MainThreadScrollingReason::kNotScrollingOnMain;
   ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
   ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
-
   if (scroll_node) {
-    // Flash the overlay scrollbar even if the scroll dalta is 0.
-    ScrollbarAnimationController* animation_controller =
-        ScrollbarAnimationControllerForId(scroll_node->owning_layer_id);
-
-    if (animation_controller)
-      animation_controller->WillUpdateScroll();
-
     gfx::Vector2dF delta = scroll_delta;
     if (!scroll_node->user_scrollable_horizontal)
       delta.set_x(0);
@@ -2951,13 +2943,6 @@
           viewport()->MainScrollLayer() &&
           viewport()->MainScrollLayer()->scroll_tree_index() == scroll_node->id;
       if (scrolls_main_viewport_scroll_layer) {
-        // Flash the overlay scrollbar even if the scroll dalta is 0.
-        ScrollbarAnimationController* animation_controller =
-            ScrollbarAnimationControllerForId(scroll_node->owning_layer_id);
-
-        if (animation_controller)
-          animation_controller->WillUpdateScroll();
-
         gfx::Vector2dF scrolled =
             viewport()->ScrollAnimated(pending_delta, delayed_by);
         // Viewport::ScrollAnimated returns pending_delta as long as it starts
@@ -3202,18 +3187,9 @@
   DCHECK(scroll_state);
 
   TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBy");
-  ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
-  ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
-
-  if (!scroll_node)
+  if (!CurrentlyScrollingNode())
     return InputHandlerScrollResult();
 
-  ScrollbarAnimationController* animation_controller =
-      ScrollbarAnimationControllerForId(scroll_node->owning_layer_id);
-
-  if (animation_controller)
-    animation_controller->WillUpdateScroll();
-
   float initial_top_controls_offset =
       browser_controls_offset_manager_->ControlsTopOffset();
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index f6e8f731..c74072b 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -2814,8 +2814,6 @@
     settings.scrollbar_animator = animator;
     settings.scrollbar_show_delay = base::TimeDelta::FromMilliseconds(20);
     settings.scrollbar_fade_out_delay = base::TimeDelta::FromMilliseconds(20);
-    settings.scrollbar_fade_out_resize_delay =
-        base::TimeDelta::FromMilliseconds(20);
     settings.scrollbar_fade_out_duration =
         base::TimeDelta::FromMilliseconds(20);
 
@@ -2849,25 +2847,6 @@
     EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
     EXPECT_TRUE(animation_task_.Equals(base::Closure()));
 
-    // For Aura Overlay Scrollbar, if no scroll happened during a scroll
-    // gesture, shows scrollbars and schedules a delay fade out.
-    host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                            InputHandler::WHEEL);
-    host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2dF(0, 0)).get());
-    host_impl_->ScrollEnd(EndState().get());
-    EXPECT_FALSE(did_request_next_frame_);
-    EXPECT_FALSE(did_request_redraw_);
-    if (animator == LayerTreeSettings::AURA_OVERLAY) {
-      EXPECT_EQ(base::TimeDelta::FromMilliseconds(20),
-                requested_animation_delay_);
-      EXPECT_FALSE(animation_task_.Equals(base::Closure()));
-      requested_animation_delay_ = base::TimeDelta();
-      animation_task_ = base::Closure();
-    } else {
-      EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
-      EXPECT_TRUE(animation_task_.Equals(base::Closure()));
-    }
-
     // Before the scrollbar animation exists, we should not get redraws.
     BeginFrameArgs begin_frame_args =
         CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, fake_now);
@@ -2975,9 +2954,7 @@
       host_impl_->DidFinishImplFrame();
     }
 
-    // For Andrdoid, scrollbar animation is not triggered unnecessarily.
-    // For Aura Overlay Scrollbar, scrollbar appears even if scroll offset did
-    // not change.
+    // Scrollbar animation is not triggered unnecessarily.
     host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
                             InputHandler::WHEEL);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2dF(5, 0)).get());
@@ -2990,16 +2967,8 @@
     host_impl_->ScrollEnd(EndState().get());
     EXPECT_FALSE(did_request_next_frame_);
     EXPECT_FALSE(did_request_redraw_);
-    if (animator == LayerTreeSettings::AURA_OVERLAY) {
-      EXPECT_EQ(base::TimeDelta::FromMilliseconds(20),
-                requested_animation_delay_);
-      EXPECT_FALSE(animation_task_.Equals(base::Closure()));
-      requested_animation_delay_ = base::TimeDelta();
-      animation_task_ = base::Closure();
-    } else {
-      EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
-      EXPECT_TRUE(animation_task_.Equals(base::Closure()));
-    }
+    EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
+    EXPECT_TRUE(animation_task_.Equals(base::Closure()));
 
     // Changing page scale triggers scrollbar animation.
     host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 4.f);
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index b069051..a42bd683 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -243,31 +243,27 @@
   }
 
   bool scrollbar_needs_animation = false;
-  bool clip_layer_size_did_change = false;
   bool scroll_layer_size_did_change = false;
   bool y_offset_did_change = false;
   for (ScrollbarLayerImplBase* scrollbar : ScrollbarsFor(scroll_layer_id)) {
     if (scrollbar->orientation() == HORIZONTAL) {
       scrollbar_needs_animation |= scrollbar->SetCurrentPos(current_offset.x());
-      clip_layer_size_did_change |=
+      scrollbar_needs_animation |=
           scrollbar->SetClipLayerLength(clip_size.width());
-      scroll_layer_size_did_change |=
+      scrollbar_needs_animation |= scroll_layer_size_did_change |=
           scrollbar->SetScrollLayerLength(scroll_size.width());
     } else {
       scrollbar_needs_animation |= y_offset_did_change |=
           scrollbar->SetCurrentPos(current_offset.y());
-      clip_layer_size_did_change |=
+      scrollbar_needs_animation |=
           scrollbar->SetClipLayerLength(clip_size.height());
-      scroll_layer_size_did_change |=
+      scrollbar_needs_animation |= scroll_layer_size_did_change |=
           scrollbar->SetScrollLayerLength(scroll_size.height());
     }
     scrollbar_needs_animation |=
         scrollbar->SetVerticalAdjust(clip_layer->bounds_delta().y());
   }
 
-  scrollbar_needs_animation |=
-      (clip_layer_size_did_change || scroll_layer_size_did_change);
-
   if (y_offset_did_change && IsViewportLayerId(scroll_layer_id))
     TRACE_COUNTER_ID1("cc", "scroll_offset_y", scroll_layer->id(),
                       current_offset.y());
@@ -276,14 +272,8 @@
     ScrollbarAnimationController* controller =
         layer_tree_host_impl_->ScrollbarAnimationControllerForId(
             scroll_layer_id);
-    if (!controller)
-      return;
-
-    if (clip_layer_size_did_change || scroll_layer_size_did_change) {
-      controller->DidResize();
-    } else {
-      controller->DidScrollUpdate();
-    }
+    if (controller)
+      controller->DidScrollUpdate(scroll_layer_size_did_change);
   }
 }
 
@@ -1281,6 +1271,10 @@
 // These manage ownership of the LayerImpl.
 void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) {
   DCHECK(std::find(layers_->begin(), layers_->end(), layer) == layers_->end());
+
+  // TODO(ajuma): Change this to a DCHECK once we've figured out what's causing
+  // crbug.com/701279.
+  CHECK(layer);
   layers_->push_back(std::move(layer));
   set_needs_update_draw_properties();
 }
diff --git a/cc/trees/tree_synchronizer.cc b/cc/trees/tree_synchronizer.cc
index b593a9d6..873a57bf 100644
--- a/cc/trees/tree_synchronizer.cc
+++ b/cc/trees/tree_synchronizer.cc
@@ -28,8 +28,12 @@
   std::unique_ptr<OwnedLayerImplList> old_layers(tree_impl->DetachLayers());
 
   OwnedLayerImplMap old_layer_map;
-  for (auto& it : *old_layers)
+  for (auto& it : *old_layers) {
+    // TODO(ajuma): Remove this once we've figured out what's causing
+    // crbug.com/701279.
+    CHECK(it);
     old_layer_map[it->id()] = std::move(it);
+  }
 
   PushLayerList(&old_layer_map, source_tree, tree_impl);
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 9db4e92..402b4a6 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=59
 MINOR=0
-BUILD=3043
+BUILD=3044
 PATCH=0
diff --git a/chrome/android/java/res/drawable-hdpi/chrome_sync_logo.png b/chrome/android/java/res/drawable-hdpi/chrome_sync_logo.png
index ed53451..22af647 100644
--- a/chrome/android/java/res/drawable-hdpi/chrome_sync_logo.png
+++ b/chrome/android/java/res/drawable-hdpi/chrome_sync_logo.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-ldrtl-hdpi-v17/ic_history_grey600_24dp.png b/chrome/android/java/res/drawable-ldrtl-hdpi-v17/ic_history_grey600_24dp.png
deleted file mode 100644
index 6571bf73..0000000
--- a/chrome/android/java/res/drawable-ldrtl-hdpi-v17/ic_history_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-ldrtl-mdpi-v17/ic_history_grey600_24dp.png b/chrome/android/java/res/drawable-ldrtl-mdpi-v17/ic_history_grey600_24dp.png
deleted file mode 100644
index 41f31a0..0000000
--- a/chrome/android/java/res/drawable-ldrtl-mdpi-v17/ic_history_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-ldrtl-xhdpi-v17/ic_history_grey600_24dp.png b/chrome/android/java/res/drawable-ldrtl-xhdpi-v17/ic_history_grey600_24dp.png
deleted file mode 100644
index f04d215..0000000
--- a/chrome/android/java/res/drawable-ldrtl-xhdpi-v17/ic_history_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-ldrtl-xxhdpi-v17/ic_history_grey600_24dp.png b/chrome/android/java/res/drawable-ldrtl-xxhdpi-v17/ic_history_grey600_24dp.png
deleted file mode 100644
index 03c7448..0000000
--- a/chrome/android/java/res/drawable-ldrtl-xxhdpi-v17/ic_history_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-ldrtl-xxxhdpi-v17/ic_history_grey600_24dp.png b/chrome/android/java/res/drawable-ldrtl-xxxhdpi-v17/ic_history_grey600_24dp.png
deleted file mode 100644
index 37803b6a..0000000
--- a/chrome/android/java/res/drawable-ldrtl-xxxhdpi-v17/ic_history_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/chrome_sync_logo.png b/chrome/android/java/res/drawable-mdpi/chrome_sync_logo.png
index 122de7b..0504718 100644
--- a/chrome/android/java/res/drawable-mdpi/chrome_sync_logo.png
+++ b/chrome/android/java/res/drawable-mdpi/chrome_sync_logo.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/chrome_sync_logo.png b/chrome/android/java/res/drawable-xhdpi/chrome_sync_logo.png
index d4af888..ce3a32b 100644
--- a/chrome/android/java/res/drawable-xhdpi/chrome_sync_logo.png
+++ b/chrome/android/java/res/drawable-xhdpi/chrome_sync_logo.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/chrome_sync_logo.png b/chrome/android/java/res/drawable-xxxhdpi/chrome_sync_logo.png
index 01760ec..bcc72c1 100644
--- a/chrome/android/java/res/drawable-xxxhdpi/chrome_sync_logo.png
+++ b/chrome/android/java/res/drawable-xxxhdpi/chrome_sync_logo.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/search_sogou.png b/chrome/android/java/res/drawable-xxxhdpi/search_sogou.png
index 170a5df5..96413939 100644
--- a/chrome/android/java/res/drawable-xxxhdpi/search_sogou.png
+++ b/chrome/android/java/res/drawable-xxxhdpi/search_sogou.png
Binary files differ
diff --git a/chrome/android/java/res/layout/bottom_control_container.xml b/chrome/android/java/res/layout/bottom_control_container.xml
index 9f45f2a4..2ff0eab 100644
--- a/chrome/android/java/res/layout/bottom_control_container.xml
+++ b/chrome/android/java/res/layout/bottom_control_container.xml
@@ -13,12 +13,13 @@
         android:id="@+id/control_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/control_container_height" >
+        android:minHeight="@dimen/bottom_control_container_height" >
+
         <view
             class="org.chromium.chrome.browser.toolbar.ToolbarControlContainer$ToolbarViewResourceFrameLayout"
             android:id="@+id/toolbar_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+            android:layout_height="wrap_content" >
             <ImageView
                 android:id="@+id/bottom_toolbar_shadow"
                 android:layout_width="match_parent"
@@ -30,16 +31,22 @@
             <ViewStub
                 android:id="@+id/toolbar_stub"
                 android:layout_width="match_parent"
-                android:layout_height="@dimen/toolbar_height_no_shadow"
+                android:layout_height="@dimen/bottom_control_container_height"
                 android:layout_marginTop="@dimen/toolbar_shadow_height" />
             <ViewStub
                 android:id="@+id/find_toolbar_stub"
                 android:inflatedId="@+id/find_toolbar"
                 android:layout_marginTop="@dimen/toolbar_shadow_height"
                 android:layout_width="match_parent"
-                android:layout_height="@dimen/toolbar_height_no_shadow"
-                android:layout="@layout/find_toolbar"
-                android:visibility="gone" />
+                android:layout_height="@dimen/bottom_control_container_height"
+                android:layout="@layout/find_toolbar" />
+            <ImageView
+                android:id="@+id/toolbar_handle"
+                android:layout_width="@dimen/toolbar_handle_width"
+                android:layout_height="@dimen/toolbar_handle_height"
+                android:layout_marginTop="@dimen/toolbar_handle_margin_top"
+                android:layout_gravity="center|top"
+                android:contentDescription="@null" />
         </view>
     </org.chromium.chrome.browser.toolbar.ToolbarControlContainer>
 
diff --git a/chrome/android/java/res/layout/bottom_toolbar_phone.xml b/chrome/android/java/res/layout/bottom_toolbar_phone.xml
index 00d1b0b..9dc4891 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_phone.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_phone.xml
@@ -10,7 +10,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/toolbar"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/toolbar_height_no_shadow" >
+    android:layout_height="match_parent" >
 
     <include layout="@layout/toolbar_phone_common"/>
 
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 0e3ebd21..d156c87 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -27,6 +27,7 @@
     <color name="google_grey_600">#757575</color>
     <color name="google_grey_700">#616161</color>
     <color name="toolbar_shadow_color">#1d000000</color>
+    <color name="semi_opaque_white">#80ffffff</color>
     <color name="toolbar_light_tint">#A3000000</color>
     <color name="light_grey">#ccc</color>
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index bd848cf..fb3f6e0 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -190,6 +190,8 @@
     <!-- Full Screen Dimensions -->
     <!-- Should match toolbar_height_no_shadow -->
     <dimen name="control_container_height">56dp</dimen>
+    <dimen name="bottom_control_container_height">64dp</dimen>
+    <dimen name="bottom_toolbar_top_margin">8dp</dimen>
     <dimen name="custom_tabs_control_container_height">56dp</dimen>
     <dimen name="webapp_control_container_height">22dp</dimen>
 
@@ -232,6 +234,9 @@
     <dimen name="toolbar_shadow_height">8dp</dimen>
     <dimen name="toolbar_progress_bar_height">2dp</dimen>
     <dimen name="toolbar_button_width">48dp</dimen>
+    <dimen name="toolbar_handle_height">4dp</dimen>
+    <dimen name="toolbar_handle_width">40dp</dimen>
+    <dimen name="toolbar_handle_margin_top">14dp</dimen>
 
     <dimen name="toolbar_edge_padding">8dp</dimen>
     <dimen name="location_bar_google_g_width">24dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index cabf212d..d5e46096 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1351,6 +1351,7 @@
      * @return The resource id that contains how large the browser controls are.
      */
     public int getControlContainerHeightResource() {
+        if (mBottomSheet != null) return R.dimen.bottom_control_container_height;
         return R.dimen.control_container_height;
     }
 
@@ -1668,6 +1669,7 @@
      * and visa-versa.
      * @param isInMultiWindowMode True if the activity is in multi-window mode.
      */
+    @Override
     public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
         recordMultiWindowModeChangedUserAction(isInMultiWindowMode);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ChromeAnimation.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ChromeAnimation.java
index 5933a66a8..be825b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ChromeAnimation.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ChromeAnimation.java
@@ -4,12 +4,17 @@
 
 package org.chromium.chrome.browser.compositor.layouts;
 
+import android.annotation.TargetApi;
+import android.os.Build;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 
+import org.chromium.base.ContextUtils;
+
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -31,9 +36,10 @@
     private static final int FIRST_FRAME_OFFSET_MS = 1000 / 60;
 
     /**
-     * Can be used to slow down created animations for debugging purposes.
+     * Multiplier for animation durations for debugging. Can be set in Developer Options and cached
+     * here.
      */
-    private static final int ANIMATION_MULTIPLIER = 1;
+    private static Float sAnimationMultiplier;
 
     private final AtomicBoolean mFinishCalled = new AtomicBoolean();
     private final ArrayList<Animation<T>> mAnimations = new ArrayList<Animation<T>>();
@@ -256,11 +262,26 @@
             mAnimatedObject = t;
             mStart = start;
             mEnd = end;
-            mDuration = duration * ANIMATION_MULTIPLIER;
-            mStartDelay = startTime * ANIMATION_MULTIPLIER;
+            float animationMultiplier = getAnimationMultiplier();
+            mDuration = (long) (duration * animationMultiplier);
+            mStartDelay = (long) (startTime * animationMultiplier);
             mCurrentTime = 0;
         }
 
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+        private float getAnimationMultiplier() {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return 1f;
+
+            synchronized (sLock) {
+                if (sAnimationMultiplier == null) {
+                    sAnimationMultiplier = Settings.Global.getFloat(
+                            ContextUtils.getApplicationContext().getContentResolver(),
+                            Settings.Global.ANIMATOR_DURATION_SCALE, 1f);
+                }
+                return sAnimationMultiplier;
+            }
+        }
+
         /**
          * Returns the object being animated.
          */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/GestureEventFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/GestureEventFilter.java
index 8b2a49c..62d7714 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/GestureEventFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/GestureEventFilter.java
@@ -84,6 +84,7 @@
     public GestureEventFilter(Context context, EventFilterHost host, GestureHandler handler,
             boolean autoOffset, boolean useDefaultLongPress) {
         super(context, host, autoOffset);
+        assert handler != null;
         mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mLongPressTimeoutMs = ViewConfiguration.getLongPressTimeout();
         mUseDefaultLongPress = useDefaultLongPress;
@@ -112,7 +113,7 @@
                         distanceY *= ratio;
                     }
                 }
-                if (mHandler != null && mSingleInput) {
+                if (mSingleInput) {
                     // distanceX/Y only represent the distance since the last event, not the total
                     // distance for the full scroll.  Calculate the total distance here.
                     float totalX = e2.getX() - mOnScrollBeginX;
@@ -130,7 +131,7 @@
                  * during long press, so we need to explicitly not call handler.click if a
                  * long press has been detected.
                  */
-                if (mHandler != null && mSingleInput && !mInLongPress) {
+                if (mSingleInput && !mInLongPress) {
                     mHandler.click(e.getX() * mPxToDp, e.getY() * mPxToDp,
                                    e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE,
                                    mButtons);
@@ -141,7 +142,7 @@
             @Override
             public boolean onFling(MotionEvent e1, MotionEvent e2,
                     float velocityX, float velocityY) {
-                if (mHandler != null && mSingleInput) {
+                if (mSingleInput) {
                     mHandler.fling(e1.getX() * mPxToDp, e1.getY() * mPxToDp,
                             velocityX * mPxToDp, velocityY * mPxToDp);
                 }
@@ -153,7 +154,7 @@
                 mButtons = e.getButtonState();
                 mInLongPress = false;
                 mSeenFirstScrollEvent = false;
-                if (mHandler != null && mSingleInput) {
+                if (mSingleInput) {
                     mHandler.onDown(e.getX() * mPxToDp,
                                     e.getY() * mPxToDp,
                                     e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE,
@@ -172,7 +173,7 @@
     }
 
     private void longPress(MotionEvent e) {
-        if (mHandler != null && mSingleInput) {
+        if (mSingleInput) {
             mInLongPress = true;
             mHandler.onLongPress(e.getX() * mPxToDp, e.getY() * mPxToDp);
         }
@@ -180,7 +181,7 @@
 
     @Override
     public boolean onInterceptTouchEventInternal(MotionEvent e, boolean isKeyboardShowing) {
-        return mHandler != null;
+        return true;
     }
 
     private void cancelLongPress() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 1c5364f..a406ac4d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -545,6 +545,7 @@
      * Open a link from the panel in a new tab.
      * @param url The URL to load.
      */
+    @Override
     public void createNewTab(String url) {
         if (mChromeActivity == null) return;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java
index ab4ac0f..0cb4dd0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java
@@ -81,6 +81,7 @@
         super.onButtonClicked(isPrimaryButton);
     }
 
+    @Override
     @CalledByNative
     protected boolean isPersistSwitchOn() {
         return super.isPersistSwitchOn();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java
index dcb5015..9b1e0e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java
@@ -173,6 +173,7 @@
         return this;
     }
 
+    @Override
     public ChromeNotificationBuilder setOnlyAlertOnce(boolean onlyAlertOnce) {
         mBuilder.setOnlyAlertOnce(onlyAlertOnce);
         return this;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index baf1a596..c833e89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -304,11 +304,7 @@
         final TextView searchBoxTextView = (TextView) mSearchBoxView
                 .findViewById(R.id.search_box_text);
 
-        boolean isTablet = DeviceFormFactor.isTablet(getContext());
-
-        // If the Google G should not be shown then clear it because it is shown by default in xml.
-        if (isTablet
-                || !ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_SHOW_GOOGLE_G_IN_OMNIBOX)) {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_SHOW_GOOGLE_G_IN_OMNIBOX)) {
             searchBoxTextView.setCompoundDrawablePadding(0);
 
             // Not using the relative version of this call because we only want to clear
@@ -317,7 +313,7 @@
         }
 
         String hintText = getResources().getString(R.string.search_or_type_url);
-        if (!isTablet || mManager.isFakeOmniboxTextEnabledTablet()) {
+        if (!DeviceFormFactor.isTablet(getContext()) || mManager.isFakeOmniboxTextEnabledTablet()) {
             searchBoxTextView.setHint(hintText);
         } else {
             searchBoxTextView.setContentDescription(hintText);
@@ -523,8 +519,7 @@
         if (hasLogo == mSearchProviderHasLogo && mInitialized) return;
         mSearchProviderHasLogo = hasLogo;
         boolean showLogo = mSearchProviderHasLogo
-                && (DeviceFormFactor.isTablet(getContext())
-                           || !ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_CONDENSED_LAYOUT));
+                && !ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_CONDENSED_LAYOUT);
 
         // Set a bit more top padding on the tile grid if there is no logo.
         int paddingTop = getResources().getDimensionPixelSize(showLogo
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index f718dad..2a95de7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -139,6 +139,9 @@
     private boolean mIgnoreAutocomplete = true;
     private boolean mLastUrlEditWasDelete;
 
+    /** This tracks whether or not the last ACTION_DOWN event was when the url bar had focus. */
+    boolean mDownEventHadFocus;
+
     /**
      * Implement this to get updates when the direction of the text in the URL bar changes.
      * E.g. If the user is typing a URL, then erases it and starts typing a query in Arabic,
@@ -555,6 +558,8 @@
             return true;
         }
 
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) mDownEventHadFocus = mFocused;
+
         Tab currentTab = mUrlBarDelegate.getCurrentTab();
         if (event.getAction() == MotionEvent.ACTION_DOWN && currentTab != null) {
             // Make sure to hide the current ContentView ActionBar.
@@ -566,6 +571,15 @@
     }
 
     @Override
+    public boolean performLongClick(float x, float y) {
+        // If the touch event that triggered this was when the url bar was in a different focus
+        // state, ignore the event.
+        if (mDownEventHadFocus != mFocused) return true;
+
+        return super.performLongClick(x, y);
+    }
+
+    @Override
     public boolean bringPointIntoView(int offset) {
         if (mDisableTextScrollingFromAutocomplete) return false;
         return super.bringPointIntoView(offset);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
index afe3d31..86ec7157 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
@@ -194,6 +194,7 @@
                         .setCustomTitle(titleView)
                         .setNegativeButton(R.string.cancel, this)
                         .setAdapter(mAdapter, new DialogInterface.OnClickListener() {
+                            @Override
                             public void onClick(DialogInterface dialog, int item) {
                                 mCredential = mCredentials[item];
                             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
index 973494c..0c7fcc02 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
@@ -47,6 +47,9 @@
     /** The action name for the Pay Intent. */
     public static final String ACTION_PAY = "org.chromium.intent.action.PAY";
 
+    /** The maximum number of milliseconds to wait for a response from a READY_TO_PAY service. */
+    private static final long READY_TO_PAY_TIMEOUT_MS = 400;
+
     private static final String EXTRA_METHOD_NAME = "methodName";
     private static final String EXTRA_DATA = "data";
     private static final String EXTRA_ORIGIN = "origin";
@@ -168,6 +171,7 @@
     }
 
     private void respondToGetInstrumentsQuery(final PaymentInstrument instrument) {
+        if (mInstrumentsCallback == null) return;
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -199,10 +203,17 @@
         try {
             mIsReadyToPayService.isReadyToPay(callback);
         } catch (Throwable e) {
-            /** Many undocument exceptions are not caught in the remote Service but passed on to
-                the Service caller, see writeException in Parcel.java. */
+            // Many undocumented exceptions are not caught in the remote Service but passed on to
+            // the Service caller, see writeException in Parcel.java.
             respondToGetInstrumentsQuery(null);
+            return;
         }
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                respondToGetInstrumentsQuery(null);
+            }
+        }, READY_TO_PAY_TIMEOUT_MS);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillContact.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillContact.java
index 8a91399..467db90 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillContact.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillContact.java
@@ -78,6 +78,7 @@
     }
 
     /** @return Whether the contact is complete and ready to be sent to the merchant as-is. */
+    @Override
     public boolean isComplete() {
         return mIsComplete;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
index f2cda58..6530082 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
@@ -217,6 +217,7 @@
      * @return Whether the card is complete and ready to be sent to the merchant as-is. If true,
      * this card has a valid card number, a non-empty name on card, and a complete billing address.
      */
+    @Override
     public boolean isComplete() {
         return mIsComplete;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 72a7bc0..3c06b63d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -376,6 +376,7 @@
         recordSuccessFunnelHistograms("Initiated");
     }
 
+    @Override
     protected void finalize() throws Throwable {
         super.finalize();
         if (mCurrencyFormatter != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java
index fdaccc7..6ed1ad3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java
@@ -42,6 +42,7 @@
      * Helper method to make an HTTP request.
      * @param urlConnection The HTTP connection.
      */
+    @Override
     public void writeToUrlConnection(HttpURLConnection urlConnection) throws IOException {}
 
     /**
@@ -49,6 +50,7 @@
      * @param is The InputStream.
      * @return The decoded image.
      */
+    @Override
     protected Bitmap readInputStream(InputStream is) throws IOException {
         ByteArrayOutputStream os = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/JsonObjectHttpRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/JsonObjectHttpRequest.java
index 8b14850..fae2427 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/JsonObjectHttpRequest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/JsonObjectHttpRequest.java
@@ -45,6 +45,7 @@
      * Helper method to make an HTTP request.
      * @param urlConnection The HTTP connection.
      */
+    @Override
     public void writeToUrlConnection(HttpURLConnection urlConnection) throws IOException {
         urlConnection.setDoOutput(true);
         urlConnection.setRequestProperty("Content-Type", "application/json");
@@ -60,6 +61,7 @@
      * @param is The InputStream.
      * @return An object representing the HTTP response.
      */
+    @Override
     protected JSONObject readInputStream(InputStream is) throws IOException {
         String jsonString = readStreamToString(is);
         JSONObject jsonObject;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java
index 969ff1c..0ef82a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java
@@ -143,6 +143,7 @@
 
                 if (mListener == null && sync != null) {
                     mListener = new ProfileSyncService.SyncStateChangedListener() {
+                        @Override
                         public void syncStateChanged() {
                             if (sync.isEngineInitialized()) {
                                 mSyncInitialized = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/CameraInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/CameraInfo.java
index 27e43643..61aa7aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/CameraInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/CameraInfo.java
@@ -12,11 +12,13 @@
         super(origin, embedder, isIncognito);
     }
 
+    @Override
     protected int getNativePreferenceValue(String origin, String embedder, boolean isIncognito) {
         return WebsitePreferenceBridge.nativeGetCameraSettingForOrigin(
                 origin, embedder, isIncognito);
     }
 
+    @Override
     protected void setNativePreferenceValue(
             String origin, String embedder, ContentSetting value, boolean isIncognito) {
         WebsitePreferenceBridge.nativeSetCameraSettingForOrigin(origin, value.toInt(), isIncognito);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MicrophoneInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MicrophoneInfo.java
index 51dd41c..b45498c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MicrophoneInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MicrophoneInfo.java
@@ -12,11 +12,13 @@
         super(origin, embedder, isIncognito);
     }
 
+    @Override
     protected int getNativePreferenceValue(String origin, String embedder, boolean isIncognito) {
         return WebsitePreferenceBridge.nativeGetMicrophoneSettingForOrigin(
                 origin, embedder, isIncognito);
     }
 
+    @Override
     protected void setNativePreferenceValue(
             String origin, String embedder, ContentSetting value, boolean isIncognito) {
         WebsitePreferenceBridge.nativeSetMicrophoneSettingForOrigin(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MidiInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MidiInfo.java
index 1a4eb14..7c059e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MidiInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/MidiInfo.java
@@ -12,10 +12,12 @@
         super(origin, embedder, isIncognito);
     }
 
+    @Override
     protected int getNativePreferenceValue(String origin, String embedder, boolean isIncognito) {
         return WebsitePreferenceBridge.nativeGetMidiSettingForOrigin(origin, embedder, isIncognito);
     }
 
+    @Override
     protected void setNativePreferenceValue(
             String origin, String embedder, ContentSetting value, boolean isIncognito) {
         WebsitePreferenceBridge.nativeSetMidiSettingForOrigin(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
index e94cd94..a04656e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
@@ -77,6 +77,7 @@
         mProfileSyncService.addSyncStateChangedListener(this);
         mProfileSyncService.setMasterSyncEnabledProvider(
                 new ProfileSyncService.MasterSyncEnabledProvider() {
+                    @Override
                     public boolean isMasterSyncEnabled() {
                         return AndroidSyncSettings.isMasterSyncEnabled(mContext);
                     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
index 494538d..5c1b6659 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -5,9 +5,17 @@
 package org.chromium.chrome.browser.toolbar;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.AttributeSet;
+import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.widget.BottomSheet;
 import org.chromium.chrome.browser.widget.BottomSheetObserver;
@@ -16,6 +24,12 @@
  * Phone specific toolbar that exists at the bottom of the screen.
  */
 public class BottomToolbarPhone extends ToolbarPhone implements BottomSheetObserver {
+    /** The white version of the toolbar handle; used for dark themes and incognito. */
+    private final Bitmap mHandleLight;
+
+    /** The dark version of the toolbar handle; this is the default handle to use. */
+    private final Bitmap mHandleDark;
+
     /** A handle to the bottom sheet. */
     private BottomSheet mBottomSheet;
 
@@ -30,6 +44,9 @@
      */
     private float mLastHeightFraction;
 
+    /** The toolbar handle view that indicates the toolbar can be pulled upward. */
+    private ImageView mToolbarHandleView;
+
     /**
      * Constructs a BottomToolbarPhone object.
      * @param context The Context in which this View object is created.
@@ -37,6 +54,14 @@
      */
     public BottomToolbarPhone(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        int defaultHandleColor =
+                ApiCompatibilityUtils.getColor(getResources(), R.color.google_grey_500);
+        mHandleDark = generateHandleBitmap(defaultHandleColor);
+
+        int lightHandleColor =
+                ApiCompatibilityUtils.getColor(getResources(), R.color.semi_opaque_white);
+        mHandleLight = generateHandleBitmap(lightHandleColor);
     }
 
     @Override
@@ -80,6 +105,90 @@
         mProgressBar.setProgressBarContainer(coordinator);
     }
 
+    /**
+     * @return The extra top margin that should be applied to the browser controls views to
+     *         correctly offset them from the handle that sits above them.
+     */
+    private int getExtraTopMargin() {
+        return getResources().getDimensionPixelSize(R.dimen.bottom_toolbar_top_margin);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+
+        // Programmatically apply a top margin to all the children of the toolbar container. This
+        // is done so the view hierarchy does not need to be changed.
+        int topMarginForControls = getExtraTopMargin();
+
+        View topShadow = findViewById(R.id.bottom_toolbar_shadow);
+
+        for (int i = 0; i < getChildCount(); i++) {
+            View curView = getChildAt(i);
+
+            // Skip the shadow that sits at the top of the toolbar since this needs to sit on top
+            // of the toolbar.
+            if (curView == topShadow) continue;
+
+            ((MarginLayoutParams) curView.getLayoutParams()).topMargin = topMarginForControls;
+        }
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        // The toolbar handle is part of the control container so it can draw on top of the other
+        // toolbar views. Get the root view and search for the handle.
+        mToolbarHandleView = (ImageView) getRootView().findViewById(R.id.toolbar_handle);
+        mToolbarHandleView.setImageBitmap(mHandleDark);
+    }
+
+    @Override
+    protected void updateVisualsForToolbarState() {
+        super.updateVisualsForToolbarState();
+
+        // The handle should not show in tab switcher mode.
+        mToolbarHandleView.setVisibility(
+                mTabSwitcherState != ToolbarPhone.STATIC_TAB ? View.INVISIBLE : View.VISIBLE);
+        mToolbarHandleView.setImageBitmap(mUseLightToolbarDrawables ? mHandleLight : mHandleDark);
+    }
+
+    @Override
+    protected void updateLocationBarBackgroundBounds(Rect out, VisualState visualState) {
+        super.updateLocationBarBackgroundBounds(out, visualState);
+
+        // Allow the location bar to expand to the full height of the control container.
+        out.top -= getExtraTopMargin() * mUrlExpansionPercent;
+    }
+
+    /**
+     * Generate the bitmap used as the handle on the toolbar. This indicates that the toolbar can
+     * be pulled up.
+     * @return The handle as a bitmap.
+     */
+    private Bitmap generateHandleBitmap(int handleColor) {
+        int handleWidth = getResources().getDimensionPixelSize(R.dimen.toolbar_handle_width);
+        int handleHeight = getResources().getDimensionPixelSize(R.dimen.toolbar_handle_height);
+
+        Bitmap handle = Bitmap.createBitmap(handleWidth, handleHeight, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(handle);
+
+        // Clear the canvas to be completely transparent.
+        canvas.drawARGB(0, 0, 0, 0);
+
+        Paint paint = new Paint();
+        paint.setColor(handleColor);
+        paint.setAntiAlias(true);
+
+        RectF rect = new RectF(0, 0, handleWidth, handleHeight);
+
+        // Use height / 2 for the corner radius so the handle always takes the shape of a pill.
+        canvas.drawRoundRect(rect, handleHeight / 2f, handleHeight / 2f, paint);
+
+        return handle;
+    }
+
     @Override
     protected boolean shouldHideEndToolbarButtons() {
         return mShouldHideEndToolbarButtons;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index e06bba2d..a27517aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -167,11 +167,6 @@
     }
 
     @Override
-    protected int getToolbarHeightWithoutShadowResId() {
-        return R.dimen.custom_tabs_control_container_height;
-    }
-
-    @Override
     public void initialize(ToolbarDataProvider toolbarDataProvider,
             ToolbarTabController tabController, AppMenuButtonHelper appMenuButtonHelper) {
         super.initialize(toolbarDataProvider, tabController, appMenuButtonHelper);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java
index a6c471fb..fa4c5743 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java
@@ -36,6 +36,12 @@
     void getPositionRelativeToContainer(View containerView, int[] position);
 
     /**
+     * Get the height of the toolbar in px.
+     * @return The height of the toolbar.
+     */
+    int getHeight();
+
+    /**
      * Sets whether or not the toolbar should draw as if it's being captured for a snapshot
      * texture.  In this mode it will only draw the toolbar in it's normal state (no TabSwitcher
      * or animations).
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
index 84a77aa..fa95604 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
@@ -163,7 +163,6 @@
         private final View mToolbarContainer;
 
         private Toolbar mToolbar;
-        private int mToolbarActualHeightPx;
         private int mTabStripHeightPx;
 
         /** Builds the resource adapter for the toolbar. */
@@ -178,12 +177,6 @@
          */
         public void setToolbar(Toolbar toolbar) {
             mToolbar = toolbar;
-            int containerHeightResId = R.dimen.control_container_height;
-            if (mToolbar instanceof CustomTabToolbar) {
-                containerHeightResId = R.dimen.custom_tabs_control_container_height;
-            }
-            mToolbarActualHeightPx = mToolbarContainer.getResources().getDimensionPixelSize(
-                    containerHeightResId);
             mTabStripHeightPx = mToolbarContainer.getResources().getDimensionPixelSize(
                     R.dimen.tab_strip_height);
         }
@@ -228,8 +221,8 @@
 
         @Override
         protected void computeContentPadding(Rect outContentPadding) {
-            outContentPadding.set(0, mTabStripHeightPx, mToolbarContainer.getWidth(),
-                    mToolbarActualHeightPx);
+            outContentPadding.set(
+                    0, mTabStripHeightPx, mToolbarContainer.getWidth(), mToolbar.getHeight());
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
index 9855702..20817c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
@@ -77,8 +77,6 @@
 
     private long mFirstDrawTimeMs;
 
-    protected final int mToolbarHeightWithoutShadow;
-
     private boolean mFindInPageToolbarShowing;
 
     protected boolean mShowMenuBadge;
@@ -90,12 +88,24 @@
      */
     public ToolbarLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mToolbarHeightWithoutShadow = getResources().getDimensionPixelOffset(
-                getToolbarHeightWithoutShadowResId());
         mDarkModeTint =
                 ApiCompatibilityUtils.getColorStateList(getResources(), R.color.dark_mode_tint);
         mLightModeTint =
                 ApiCompatibilityUtils.getColorStateList(getResources(), R.color.light_mode_tint);
+
+        addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View view, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                // Creation of the progress bar is done here so the toolbar height is available.
+                mProgressBar = new ToolbarProgressBar(getContext(), getProgressBarTopMargin());
+                if (isNativeLibraryReady()) mProgressBar.initializeAnimation();
+                addProgressBarToHierarchy();
+
+                // Since this only needs to happen once, remove this listener from the view.
+                removeOnLayoutChangeListener(this);
+            }
+        });
     }
 
     /**
@@ -104,7 +114,7 @@
      * @return The top margin of the progress bar.
      */
     protected int getProgressBarTopMargin() {
-        return mToolbarHeightWithoutShadow
+        return getHeight()
                 - getResources().getDimensionPixelSize(R.dimen.toolbar_progress_bar_height);
     }
 
@@ -112,10 +122,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mProgressBar = new ToolbarProgressBar(getContext(), getProgressBarTopMargin());
-
-        if (isNativeLibraryReady()) mProgressBar.initializeAnimation();
-
         mMenuButton = (TintedImageButton) findViewById(R.id.menu_button);
         mMenuBadge = (ImageView) findViewById(R.id.menu_badge);
         mMenuButtonWrapper = findViewById(R.id.menu_button_wrapper);
@@ -169,13 +175,6 @@
     }
 
     /**
-     * @return The resource id to be used while getting the toolbar height with no shadow.
-     */
-    protected int getToolbarHeightWithoutShadowResId() {
-        return R.dimen.toolbar_height_no_shadow;
-    }
-
-    /**
      * Initialize the external dependencies required for view interaction.
      * @param toolbarDataProvider The provider for toolbar data.
      * @param tabController       The controller that handles interactions with the tab.
@@ -263,12 +262,6 @@
         mProgressBar.setProgressBarContainer(controlContainer);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        addProgressBarToHierarchy();
-    }
-
     /**
      * Shows the content description toast for items on the toolbar.
      * @param view The view to anchor the toast.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index 328bb77..28b4d730 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -95,10 +95,10 @@
     private static final float UNINITIALIZED_PERCENT = -1f;
 
     /** States that the toolbar can be in regarding the tab switcher. */
-    private static final int STATIC_TAB = 0;
-    private static final int TAB_SWITCHER = 1;
-    private static final int ENTERING_TAB_SWITCHER = 2;
-    private static final int EXITING_TAB_SWITCHER = 3;
+    protected static final int STATIC_TAB = 0;
+    protected static final int TAB_SWITCHER = 1;
+    protected static final int ENTERING_TAB_SWITCHER = 2;
+    protected static final int EXITING_TAB_SWITCHER = 3;
 
     @ViewDebug.ExportedProperty(category = "chrome", mapping = {
             @ViewDebug.IntToString(from = STATIC_TAB, to = "STATIC_TAB"),
@@ -130,7 +130,7 @@
     private final List<View> mTabSwitcherModeViews = new ArrayList<>();
     private final Set<View> mBrowsingModeViews = new HashSet<>();
     @ViewDebug.ExportedProperty(category = "chrome")
-    private int mTabSwitcherState;
+    protected int mTabSwitcherState;
 
     // This determines whether or not the toolbar draws as expected (false) or whether it always
     // draws as if it's showing the non-tabswitcher, non-animating toolbar. This is used in grabbing
@@ -179,7 +179,7 @@
      * maximum of {@link #mUrlFocusChangePercent} and {@link #mNtpSearchBoxScrollPercent}.
      */
     @ViewDebug.ExportedProperty(category = "chrome")
-    private float mUrlExpansionPercent;
+    protected float mUrlExpansionPercent;
     private AnimatorSet mUrlFocusLayoutAnimator;
     private boolean mDisableLocationBarRelayout;
     private boolean mLayoutLocationBarInFocusedMode;
@@ -248,7 +248,7 @@
     /**
      * Used to specify the visual state of the toolbar.
      */
-    private enum VisualState {
+    protected enum VisualState {
         TAB_SWITCHER_INCOGNITO,
         TAB_SWITCHER_NORMAL,
         NORMAL,
@@ -259,7 +259,7 @@
 
     private VisualState mVisualState = VisualState.NORMAL;
     private VisualState mOverlayDrawablesVisualState;
-    private boolean mUseLightToolbarDrawables;
+    protected boolean mUseLightToolbarDrawables;
 
     private NewTabPage mVisibleNewTabPage;
     private float mPreTextureCaptureAlpha = 1f;
@@ -738,7 +738,7 @@
     /**
      * Calculate the bounds for the location bar background and set them to {@code out}.
      */
-    private void updateLocationBarBackgroundBounds(Rect out, VisualState visualState) {
+    protected void updateLocationBarBackgroundBounds(Rect out, VisualState visualState) {
         // Calculate the visible boundaries of the left and right most child views of the
         // location bar.
         float expansion = visualState == VisualState.NEW_TAB_NORMAL ? 1 : mUrlExpansionPercent;
@@ -1237,7 +1237,7 @@
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        mBackgroundOverlayBounds.set(0, 0, w, mToolbarHeightWithoutShadow);
+        mBackgroundOverlayBounds.set(0, 0, w, h);
         super.onSizeChanged(w, h, oldw, oldh);
     }
 
@@ -1539,7 +1539,8 @@
                     ApiCompatibilityUtils.getColor(getResources(), android.R.color.transparent));
         } else {
             TypedValue outValue = new TypedValue();
-            // the linked style here will have to be changed if it is updated in the XML.
+
+            // The linked style here will have to be changed if it is updated in the XML.
             getContext().getTheme().resolveAttribute(R.style.ToolbarButton, outValue, true);
             mToggleTabStackButton.setBackgroundResource(outValue.resourceId);
         }
@@ -2022,7 +2023,7 @@
         return VisualState.NORMAL;
     }
 
-    private void updateVisualsForToolbarState() {
+    protected void updateVisualsForToolbarState() {
         final boolean isIncognito = isIncognito();
 
         // These are important for setting visual state while the entering or leaving the tab
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
index 36745ee01..9c411d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
@@ -61,12 +61,14 @@
         }
 
         ShareHelper.TargetChosenCallback innerCallback = new ShareHelper.TargetChosenCallback() {
+            @Override
             public void onTargetChosen(ComponentName chosenComponent) {
                 RecordHistogram.recordEnumeratedHistogram("WebShare.ShareOutcome",
                         WEBSHARE_OUTCOME_SUCCESS, WEBSHARE_OUTCOME_COUNT);
                 callback.call(ShareError.OK);
             }
 
+            @Override
             public void onCancel() {
                 RecordHistogram.recordEnumeratedHistogram("WebShare.ShareOutcome",
                         WEBSHARE_OUTCOME_CANCELED, WEBSHARE_OUTCOME_COUNT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
index 5a235403..940914f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
@@ -352,6 +352,7 @@
 
         // Listen to height changes on the root.
         root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
                 // Make sure the size of the layout actually changed.
@@ -370,6 +371,7 @@
 
         // Listen to height changes on the toolbar.
         controlContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
                 // Make sure the size of the layout actually changed.
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index f751f02..99652c59 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -409,7 +409,7 @@
     Auto Sign-in
   </message>
   <message name="IDS_SETTINGS_PASSWORDS_AUTOSIGNIN_CHECKBOX_DESC" desc="Text that describes the 'Auto Sign-in' functionality to users.">
-    Automatically sign in to websites using stored credentials. When the feature is disabled, you will be asked for confirmation every time before signing in to a website.
+    Automatically sign in to websites using stored credentials. If disabled, you will be asked for confirmation every time before signing in to a website.
   </message>
   <message name="IDS_SETTINGS_PASSWORDS_DETAIL" desc="Description of what toggling the 'Manage passwords' setting does. Immediately underneath IDS_SETTINGS_PASSWORDS">
     Offer to save your web passwords
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d8af69c..6f25337 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -804,6 +804,8 @@
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service.h",
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.cc",
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.h",
+    "page_load_metrics/observers/media_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/media_page_load_metrics_observer.h",
     "page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.cc",
     "page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h",
     "page_load_metrics/observers/prerender_page_load_metrics_observer.cc",
@@ -858,8 +860,6 @@
     "performance_monitor/process_metrics_history.h",
     "permissions/chooser_context_base.cc",
     "permissions/chooser_context_base.h",
-    "permissions/delegation_tracker.cc",
-    "permissions/delegation_tracker.h",
     "permissions/permission_blacklist_client.cc",
     "permissions/permission_blacklist_client.h",
     "permissions/permission_context_base.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 88f243a..e78f8901 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -143,6 +143,10 @@
 const unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS | kOsAndroid;
 const unsigned kOsDesktop = kOsMac | kOsWin | kOsLinux | kOsCrOS;
 
+#if defined(USE_AURA)
+const unsigned kOsAura = kOsWin | kOsLinux | kOsCrOS;
+#endif  // USE_AURA
+
 const FeatureEntry::Choice kTouchEventFeatureDetectionChoices[] = {
   { IDS_GENERIC_EXPERIMENT_CHOICE_AUTOMATIC, "", "" },
   { IDS_GENERIC_EXPERIMENT_CHOICE_ENABLED,
@@ -870,15 +874,15 @@
      kOsLinux | kOsCrOS | kOsWin | kOsAndroid,
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableSmoothScrolling,
                                switches::kDisableSmoothScrolling)},
-#if defined(USE_AURA) || defined(OS_LINUX)
+#if defined(USE_AURA)
     {"overlay-scrollbars", IDS_FLAGS_OVERLAY_SCROLLBARS_NAME,
      IDS_FLAGS_OVERLAY_SCROLLBARS_DESCRIPTION,
      // Uses the system preference on Mac (a different implementation).
      // On Android, this is always enabled.
-     kOsLinux | kOsCrOS | kOsWin,
+     kOsAura,
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableOverlayScrollbar,
                                switches::kDisableOverlayScrollbar)},
-#endif  // USE_AURA || OS_LINUX
+#endif  // USE_AURA
     {   // See http://crbug.com/120416 for how to remove this flag.
      "save-page-as-mhtml", IDS_FLAGS_SAVE_PAGE_AS_MHTML_NAME,
      IDS_FLAGS_SAVE_PAGE_AS_MHTML_DESCRIPTION, kOsMac | kOsWin | kOsLinux,
@@ -1138,7 +1142,7 @@
 #if defined(USE_AURA)
     {"overscroll-history-navigation",
      IDS_FLAGS_OVERSCROLL_HISTORY_NAVIGATION_NAME,
-     IDS_FLAGS_OVERSCROLL_HISTORY_NAVIGATION_DESCRIPTION, kOsAll,
+     IDS_FLAGS_OVERSCROLL_HISTORY_NAVIGATION_DESCRIPTION, kOsAura,
      MULTI_VALUE_TYPE(kOverscrollHistoryNavigationChoices)},
 #endif  // USE_AURA
     {"scroll-end-effect", IDS_FLAGS_SCROLL_END_EFFECT_NAME,
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index dd439fb..58671d23 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -36,15 +36,18 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/text_input_test_utils.h"
 #include "extensions/browser/api/extensions_api_client.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/ime/composition_text.h"
+#include "ui/base/ime/composition_underline.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/test/ui_controls.h"
 #include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/range/range.h"
 
 using extensions::AppWindow;
 using extensions::ExtensionsAPIClient;
@@ -517,6 +520,7 @@
 class WebViewNewWindowInteractiveTest : public WebViewInteractiveTest {};
 class WebViewFocusInteractiveTest : public WebViewInteractiveTest {};
 class WebViewPointerLockInteractiveTest : public WebViewInteractiveTest {};
+class WebViewImeInteractiveTest : public WebViewInteractiveTest {};
 
 // The tests below aren't needed in --use-cross-process-frames-for-guests.
 class WebViewContextMenuInteractiveTest : public WebViewInteractiveTestBase {};
@@ -546,6 +550,10 @@
                         WebViewPointerLockInteractiveTest,
                         testing::Bool());
 
+INSTANTIATE_TEST_CASE_P(WebViewInteractiveTests,
+                        WebViewImeInteractiveTest,
+                        testing::Bool());
+
 // ui_test_utils::SendMouseMoveSync doesn't seem to work on OS_MACOSX, and
 // likely won't work on many other platforms as well, so for now this test
 // is for Windows and Linux only. As of Sept 17th, 2013 this test is disabled
@@ -1507,3 +1515,66 @@
 
   ASSERT_TRUE(next_step_listener.WaitUntilSatisfied());
 }
+
+#if defined(OS_MACOSX)
+// This test verifies that replacement range for IME works with <webview>s. To
+// verify this, a <webview> with an <input> inside is loaded. Then the <input>
+// is focused and  populated with some text. The test then sends an IPC to
+// commit some text which will replace part of the previous text some new text.
+IN_PROC_BROWSER_TEST_P(WebViewImeInteractiveTest,
+                       CommitTextWithReplacementRange) {
+  ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
+  LoadAndLaunchPlatformApp("web_view/ime", "WebViewImeTest.Launched");
+  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow()));
+
+  // Flush any pending events to make sure we start with a clean slate.
+  content::RunAllPendingInMessageLoop();
+
+  content::WebContents* guest_web_contents =
+      GetGuestViewManager()->GetLastGuestCreated();
+
+  // Click the <input> element inside the <webview>. In its focus handle, the
+  // <input> inside the <webview> initializes its value to "A B X D".
+  ExtensionTestMessageListener focus_listener("WebViewImeTest.InputFocused",
+                                              false);
+  content::WebContents* target_web_contents =
+      GetParam()
+          ? guest_web_contents
+          : guest_view::GuestViewBase::FromWebContents(guest_web_contents)
+                ->embedder_web_contents();
+  content::SimulateMouseClickAt(target_web_contents, 0,
+                                blink::WebMouseEvent::Button::Left,
+                                gfx::Point(50, 50));
+  focus_listener.WaitUntilSatisfied();
+
+  // Verify the text inside the <input> is "A B X D".
+  std::string value;
+  ASSERT_TRUE(ExecuteScriptAndExtractString(guest_web_contents,
+                                            "window.domAutomationController."
+                                            "send(document.querySelector('"
+                                            "input').value)",
+                                            &value));
+  EXPECT_EQ("A B X D", value);
+
+  // Now commit "C" to to replace the range (4, 5).
+  // For OOPIF guests, the target for IME is the RWH for the guest's main frame.
+  // For BrowserPlugin-based guests, input always goes to the embedder.
+  ExtensionTestMessageListener input_listener("WebViewImetest.InputReceived",
+                                              false);
+  content::RenderWidgetHost* target_rwh_for_input =
+      target_web_contents->GetRenderWidgetHostView()->GetRenderWidgetHost();
+  content::SendImeCommitTextToWidget(
+      target_rwh_for_input, base::UTF8ToUTF16("C"),
+      std::vector<ui::CompositionUnderline>(), gfx::Range(4, 5), 0);
+  input_listener.WaitUntilSatisfied();
+
+  // Get the input value from the guest.
+  value.clear();
+  ASSERT_TRUE(ExecuteScriptAndExtractString(guest_web_contents,
+                                            "window.domAutomationController."
+                                            "send(document.querySelector('"
+                                            "input').value)",
+                                            &value));
+  EXPECT_EQ("A B C D", value);
+}
+#endif  //  OS_MACOSX
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 8878bb27..3ad22f3 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -200,7 +200,6 @@
 #include "base/trace_event/trace_event_etw_export_win.h"
 #include "base/win/registry.h"
 #include "base/win/win_util.h"
-#include "base/win/windows_version.h"
 #include "chrome/browser/chrome_browser_main_win.h"
 #include "chrome/browser/component_updater/sw_reporter_installer_win.h"
 #include "chrome/browser/downgrade/user_data_downgrade.h"
@@ -1706,10 +1705,7 @@
   // This could be run as late as WM_QUERYENDSESSION for system update reboots,
   // but should run on startup if extended to handle crashes/hangs/patches.
   // Also, better to run once here than once for each HWND's WM_QUERYENDSESSION.
-  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
-    ChromeBrowserMainPartsWin::RegisterApplicationRestart(
-        parsed_command_line());
-  }
+  ChromeBrowserMainPartsWin::RegisterApplicationRestart(parsed_command_line());
 
   // Verify that the profile is not on a network share and if so prepare to show
   // notification to the user.
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index cd155a0b..631afa0b 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -425,7 +425,6 @@
 // static
 void ChromeBrowserMainPartsWin::RegisterApplicationRestart(
     const base::CommandLine& parsed_command_line) {
-  DCHECK(base::win::GetVersion() >= base::win::VERSION_VISTA);
   base::ScopedNativeLibrary library(base::FilePath(L"kernel32.dll"));
   // Get the function pointer for RegisterApplicationRestart.
   RegisterApplicationRestartProc register_application_restart =
@@ -444,15 +443,17 @@
 
   // Restart Chrome if the computer is restarted as the result of an update.
   // This could be extended to handle crashes, hangs, and patches.
+  const auto& command_line_string = command_line.GetCommandLineString();
   HRESULT hr = register_application_restart(
-      command_line.GetCommandLineString().c_str(),
+      command_line_string.c_str(),
       RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_PATCH);
   if (FAILED(hr)) {
     if (hr == E_INVALIDARG) {
-      LOG(WARNING) << "Command line too long for RegisterApplicationRestart";
+      LOG(WARNING) << "Command line too long for RegisterApplicationRestart: "
+                   << command_line_string;
     } else {
-      NOTREACHED() << "RegisterApplicationRestart failed. hr: " << hr <<
-                      ", command_line: " << command_line.GetCommandLineString();
+      NOTREACHED() << "RegisterApplicationRestart failed. hr: " << hr
+                   << ", command_line: " << command_line_string;
     }
   }
 }
diff --git a/chrome/browser/chromeos/input_method/input_method_util.cc b/chrome/browser/chromeos/input_method/input_method_util.cc
index 4caff1f5..48085cf 100644
--- a/chrome/browser/chromeos/input_method/input_method_util.cc
+++ b/chrome/browser/chromeos/input_method/input_method_util.cc
@@ -79,6 +79,9 @@
 // The engine ID map for migration. This migration is for input method IDs from
 // VPD so it's NOT a temporary migration.
 const char* const kEngineIdMigrationMap[][2] = {
+    // Workaround for invalid keyboard layout in kefka board vpd.
+    // See https://crbug.com/700625
+    {"ANSI", "xkb:us::eng"},
     {"ime:jp:mozc_jp", "nacl_mozc_jp"},
     {"ime:jp:mozc_us", "nacl_mozc_us"},
     {"ime:ko:hangul_2set", "ko-t-i0-und"},
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index 8444d612..b074a04 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -181,7 +181,7 @@
 
 // Used to make sure that the system tray is visible and within the screen
 // bounds after login.
-void TestSystemTrayIsVisible() {
+void TestSystemTrayIsVisible(bool otr) {
   ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
   aura::Window* primary_win = ash::Shell::GetPrimaryRootWindow();
   ash::WmWindow* wm_primary_win = ash::WmWindow::Get(primary_win);
@@ -190,7 +190,10 @@
                << "ShelfVisibilityState=" << wm_shelf->GetVisibilityState()
                << " ShelfAutoHideBehavior=" << wm_shelf->auto_hide_behavior());
   EXPECT_TRUE(tray->visible());
-  EXPECT_TRUE(RectContains(primary_win->bounds(), tray->GetBoundsInScreen()));
+
+  // This check flakes for LoginGuestTest: https://crbug.com/693106.
+  if (!otr)
+    EXPECT_TRUE(RectContains(primary_win->bounds(), tray->GetBoundsInScreen()));
 }
 
 }  // namespace
@@ -205,33 +208,31 @@
   EXPECT_EQ(profile_base_path, profile->GetPath().BaseName().value());
   EXPECT_FALSE(profile->IsOffTheRecord());
 
-  TestSystemTrayIsVisible();
+  TestSystemTrayIsVisible(false);
 }
 
 // Verifies the cursor is not hidden at startup when user is logged in.
 IN_PROC_BROWSER_TEST_F(LoginUserTest, CursorShown) {
   EXPECT_TRUE(ash::Shell::GetInstance()->cursor_manager()->IsCursorVisible());
 
-  TestSystemTrayIsVisible();
+  TestSystemTrayIsVisible(false);
 }
 
 // After a guest login, we should get the OTR default profile.
-// Test is flaky https://crbug.com/693106
-IN_PROC_BROWSER_TEST_F(LoginGuestTest, DISABLED_GuestIsOTR) {
+IN_PROC_BROWSER_TEST_F(LoginGuestTest, GuestIsOTR) {
   Profile* profile = browser()->profile();
   EXPECT_TRUE(profile->IsOffTheRecord());
   // Ensure there's extension service for this profile.
   EXPECT_TRUE(extensions::ExtensionSystem::Get(profile)->extension_service());
 
-  TestSystemTrayIsVisible();
+  TestSystemTrayIsVisible(true);
 }
 
 // Verifies the cursor is not hidden at startup when running guest session.
-// Test is flaky https://crbug.com/693106
-IN_PROC_BROWSER_TEST_F(LoginGuestTest, DISABLED_CursorShown) {
+IN_PROC_BROWSER_TEST_F(LoginGuestTest, CursorShown) {
   EXPECT_TRUE(ash::Shell::GetInstance()->cursor_manager()->IsCursorVisible());
 
-  TestSystemTrayIsVisible();
+  TestSystemTrayIsVisible(true);
 }
 
 // Verifies the cursor is hidden at startup on login screen.
@@ -249,7 +250,7 @@
   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
       FROM_HERE, LoginDisplayHost::default_host());
 
-  TestSystemTrayIsVisible();
+  TestSystemTrayIsVisible(false);
 }
 
 // Verifies that the webui for login comes up successfully.
@@ -276,7 +277,7 @@
   SubmitGaiaAuthOfflineForm(kTestUser, kPassword);
   session_start_waiter.Wait();
 
-  TestSystemTrayIsVisible();
+  TestSystemTrayIsVisible(false);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/content_settings/chrome_content_settings_utils.h b/chrome/browser/content_settings/chrome_content_settings_utils.h
index 650103f47..50429a6 100644
--- a/chrome/browser/content_settings/chrome_content_settings_utils.h
+++ b/chrome/browser/content_settings/chrome_content_settings_utils.h
@@ -47,6 +47,8 @@
   POPUPS_ACTION_SELECTED_ALWAYS_ALLOW_POPUPS_FROM,
   POPUPS_ACTION_CLICKED_LIST_ITEM_CLICKED,
   POPUPS_ACTION_CLICKED_MANAGE_POPUPS_BLOCKING,
+  POPUPS_ACTION_DISPLAYED_INFOBAR_ON_MOBILE,
+  POPUPS_ACTION_CLICKED_ALWAYS_SHOW_ON_MOBILE,
   POPUPS_ACTION_COUNT
 };
 
diff --git a/chrome/browser/download/download_request_limiter_unittest.cc b/chrome/browser/download/download_request_limiter_unittest.cc
index 82a5940d..8d9c2d29 100644
--- a/chrome/browser/download/download_request_limiter_unittest.cc
+++ b/chrome/browser/download/download_request_limiter_unittest.cc
@@ -29,7 +29,7 @@
 #else
 #include "chrome/browser/download/download_permission_request.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #endif
 
 using content::WebContents;
diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc
index 479e108..d019e8e5 100644
--- a/chrome/browser/extensions/extension_disabled_ui.cc
+++ b/chrome/browser/extensions/extension_disabled_ui.cc
@@ -163,17 +163,7 @@
                  content::Source<Profile>(service->profile()));
 }
 
-ExtensionDisabledGlobalError::~ExtensionDisabledGlobalError() {
-  if (is_remote_install_) {
-    UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponseRemoteInstall",
-                              user_response_,
-                              EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
-  } else {
-    UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponse",
-                              user_response_,
-                              EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
-  }
-}
+ExtensionDisabledGlobalError::~ExtensionDisabledGlobalError() {}
 
 GlobalError::Severity ExtensionDisabledGlobalError::GetSeverity() {
   return SEVERITY_LOW;
@@ -276,6 +266,19 @@
 }
 
 void ExtensionDisabledGlobalError::OnBubbleViewDidClose(Browser* browser) {
+  // If the user takes an action, |user_response_| is set in
+  // BubbleView[Cancel|Accept]Pressed(). Otherwise, the IGNORE value set in the
+  // constructor is correct.
+  UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponseRemoteInstall2",
+                            user_response_,
+                            EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
+  UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponse2",
+                            user_response_,
+                            EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
+  // Reset in case the user does not follow through on subsequent dialogs to
+  // confirm removal decision, in which case the bubble can be shown again
+  // when the user clicks on the global error in the menu.
+  user_response_ = IGNORED;
 }
 
 void ExtensionDisabledGlobalError::BubbleViewAcceptButtonPressed(
@@ -284,6 +287,7 @@
                                               service_->profile())) {
     return;
   }
+  user_response_ = REENABLE;
   // Delay extension reenabling so this bubble closes properly.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
@@ -298,9 +302,9 @@
   // Supervised users may never remove custodian-installed extensions.
   DCHECK(!extensions::util::IsExtensionSupervised(extension_,
                                                   service_->profile()));
-
   uninstall_dialog_.reset(extensions::ExtensionUninstallDialog::Create(
       service_->profile(), browser->window()->GetNativeWindow(), this));
+  user_response_ = UNINSTALL;
   // Delay showing the uninstall dialog, so that this function returns
   // immediately, to close the bubble properly. See crbug.com/121544.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -341,7 +345,6 @@
   const Extension* extension = content::Details<const Extension>(details).ptr();
   if (extension != extension_)
     return;
-  user_response_ = UNINSTALL;
   RemoveGlobalError();
 }
 
@@ -350,7 +353,6 @@
     const Extension* extension) {
   if (extension != extension_)
     return;
-  user_response_ = REENABLE;
   RemoveGlobalError();
 }
 
diff --git a/chrome/browser/extensions/extension_override_apitest.cc b/chrome/browser/extensions/extension_override_apitest.cc
index ad91455c..b1c8ae3 100644
--- a/chrome/browser/extensions/extension_override_apitest.cc
+++ b/chrome/browser/extensions/extension_override_apitest.cc
@@ -17,10 +17,14 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
 #include "extensions/common/constants.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 
 using content::WebContents;
 
@@ -191,6 +195,43 @@
                SchemeIs(kExtensionScheme));
 }
 
+// Check that when an overridden new tab page has focus, a subframe navigation
+// on that page does not steal the focus away by focusing the omnibox.
+// See https://crbug.com/700124.
+IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest,
+                       SubframeNavigationInOverridenNTPDoesNotAffectFocus) {
+  host_resolver()->AddRule("*", "127.0.0.1");
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Load an extension that overrides the new tab page.
+  const Extension* extension = LoadExtension(data_dir().AppendASCII("newtab"));
+
+  // Navigate to the new tab page.  The overridden new tab page
+  // will call chrome.test.sendMessage('controlled by first').
+  ExtensionTestMessageListener listener("controlled by first", false);
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(ExtensionControlsPage(contents, extension->id()));
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+
+  // Start off with the main page focused.
+  contents->Focus();
+  EXPECT_TRUE(contents->GetRenderWidgetHostView()->HasFocus());
+
+  // Inject an iframe and navigate it to a cross-site URL.  With
+  // --site-per-process, this will go into a separate process.
+  GURL cross_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  std::string script = "var f = document.createElement('iframe');\n"
+                       "f.src = '" + cross_site_url.spec() + "';\n"
+                       "document.body.appendChild(f);\n";
+  EXPECT_TRUE(ExecuteScript(contents, script));
+  WaitForLoadStop(contents);
+
+  // The page should still have focus.  The cross-process subframe navigation
+  // above should not try to focus the omnibox, which would make this false.
+  EXPECT_TRUE(contents->GetRenderWidgetHostView()->HasFocus());
+}
+
 // Times out consistently on Win, http://crbug.com/45173.
 #if defined(OS_WIN)
 #define MAYBE_OverrideHistory DISABLED_OverrideHistory
diff --git a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc
index 5f13e59..13845b55 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc
+++ b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc
@@ -62,7 +62,7 @@
 #include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h"
 #else
 #include "chrome/browser/permissions/permission_request_manager.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #endif
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/media/cast_remoting_sender.cc b/chrome/browser/media/cast_remoting_sender.cc
index 00de025..7a55a9114 100644
--- a/chrome/browser/media/cast_remoting_sender.cc
+++ b/chrome/browser/media/cast_remoting_sender.cc
@@ -92,7 +92,7 @@
       latest_acked_frame_id_(media::cast::FrameId::first() - 1),
       duplicate_ack_counter_(0),
       input_queue_discards_remaining_(0),
-      pipe_watcher_(FROM_HERE),
+      pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       flow_restart_pending_(true),
       weak_factory_(this) {
   // Confirm this constructor is running on the IO BrowserThread.
@@ -164,10 +164,11 @@
   sender->error_callback_ = error_callback;
 
   sender->pipe_ = std::move(pipe);
-  sender->pipe_watcher_.Start(
-      sender->pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
-      base::Bind(&CastRemotingSender::ProcessInputQueue,
-                 base::Unretained(sender)));
+  sender->pipe_watcher_.Watch(sender->pipe_.get(),
+                              MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+                              base::Bind(&CastRemotingSender::ProcessInputQueue,
+                                         base::Unretained(sender)));
+  sender->pipe_watcher_.ArmOrNotify();
   sender->binding_.Bind(std::move(request));
   sender->binding_.set_connection_error_handler(sender->error_callback_);
 }
@@ -378,8 +379,10 @@
           MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE);
       if (result == MOJO_RESULT_OK)
         return true;  // Successfully discarded data.
-      if (result == MOJO_RESULT_OUT_OF_RANGE)
+      if (result == MOJO_RESULT_OUT_OF_RANGE) {
+        pipe_watcher_.ArmOrNotify();
         return false;  // Retry later.
+      }
       LOG(ERROR) << SENDER_SSRC
                  << "Unexpected result when discarding from data pipe ("
                  << result << ')';
@@ -395,8 +398,10 @@
         MOJO_READ_DATA_FLAG_ALL_OR_NONE);
     if (result == MOJO_RESULT_OK)
       return true;  // Successfully consumed data.
-    if (result == MOJO_RESULT_OUT_OF_RANGE)
+    if (result == MOJO_RESULT_OUT_OF_RANGE) {
+      pipe_watcher_.ArmOrNotify();
       return false;  // Retry later.
+    }
     LOG(ERROR)
         << SENDER_SSRC << "Read from data pipe failed (" << result << ')';
   } while (false);
diff --git a/chrome/browser/media/cast_remoting_sender.h b/chrome/browser/media/cast_remoting_sender.h
index 0228d79..9b177633 100644
--- a/chrome/browser/media/cast_remoting_sender.h
+++ b/chrome/browser/media/cast_remoting_sender.h
@@ -15,7 +15,7 @@
 #include "media/cast/net/rtcp/rtcp_defines.h"
 #include "media/mojo/interfaces/remoting.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace cast {
 
@@ -196,7 +196,7 @@
 
   // Watches |pipe_| for more data to become available, and then calls
   // ProcessInputQueue().
-  mojo::Watcher pipe_watcher_;
+  mojo::SimpleWatcher pipe_watcher_;
 
   // Set to true if the first frame has not yet been sent, or if a
   // CancelInFlightData() operation just completed. This causes TrySendFrame()
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller.h b/chrome/browser/media/webrtc/media_stream_devices_controller.h
index 1777cd9..059ed42d 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller.h
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller.h
@@ -28,6 +28,10 @@
 class MediaStreamDevicesControllerBrowserTest;
 }
 
+namespace test {
+class MediaStreamDevicesControllerTestApi;
+}
+
 class MediaStreamDevicesController : public PermissionRequest {
  public:
   static void RequestPermissions(
@@ -67,6 +71,7 @@
 
  private:
   friend class MediaStreamDevicesControllerTest;
+  friend class test::MediaStreamDevicesControllerTestApi;
   friend class policy::MediaStreamDevicesControllerBrowserTest;
 
   // Delegate showing permission prompts.
diff --git a/chrome/browser/media/webrtc/webrtc_webcam_browsertest.cc b/chrome/browser/media/webrtc/webrtc_webcam_browsertest.cc
index 003dd20..49665dd 100644
--- a/chrome/browser/media/webrtc/webrtc_webcam_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_webcam_browsertest.cc
@@ -18,10 +18,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest-param-test.h"
 
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 static const char kMainWebrtcTestHtmlPage[] =
     "/webrtc/webrtc_jsep01_test.html";
 
@@ -93,17 +89,6 @@
             GetUserMediaAndGetStreamSize(tab, kVideoCallConstraintsVGA));
   EXPECT_EQ("640x360",
             GetUserMediaAndGetStreamSize(tab, kVideoCallConstraints360p));
-
-// TODO(chfremer): Reenable these tests when https://crbug.com/676041 is
-// resolved.
-#if defined(OS_WIN)
-  auto win_version = base::win::GetVersion();
-  if (win_version == base::win::VERSION_WIN8 ||
-      win_version == base::win::VERSION_WIN8_1) {
-    return;
-  }
-#endif
-
   EXPECT_EQ("1280x720",
             GetUserMediaAndGetStreamSize(tab, kVideoCallConstraints720p));
   EXPECT_EQ("1920x1080",
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index 17118cbf6..3aa27449b 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -94,8 +94,9 @@
     : pid(0),
       num_processes(0),
       process_type(content::PROCESS_TYPE_UNKNOWN),
-      renderer_type(RENDERER_UNKNOWN) {
-}
+      num_open_fds(-1),
+      open_fds_soft_limit(-1),
+      renderer_type(RENDERER_UNKNOWN) {}
 
 ProcessMemoryInformation::ProcessMemoryInformation(
     const ProcessMemoryInformation& other) = default;
@@ -181,6 +182,10 @@
     log += StringPrintf(", %d MB swapped",
                         static_cast<int>(iter1->working_set.swapped) / 1024);
 #endif
+    if (iter1->num_open_fds != -1 || iter1->open_fds_soft_limit != -1) {
+      log += StringPrintf(", %d FDs open of %d", iter1->num_open_fds,
+                          iter1->open_fds_soft_limit);
+    }
     log += "\n";
   }
   return log;
diff --git a/chrome/browser/memory_details.h b/chrome/browser/memory_details.h
index 7cd017d..6ad23e7 100644
--- a/chrome/browser/memory_details.h
+++ b/chrome/browser/memory_details.h
@@ -61,6 +61,10 @@
   int num_processes;
   // If this is a child process of Chrome, what type (i.e. plugin) it is.
   int process_type;
+  // Number of open file descriptors in this process.
+  int num_open_fds;
+  // Maximum number of file descriptors that can be opened in this process.
+  int open_fds_soft_limit;
   // If this is a renderer process, what type it is.
   RendererProcessType renderer_type;
   // A collection of titles used, i.e. for a tab it'll show all the page titles.
diff --git a/chrome/browser/memory_details_android.cc b/chrome/browser/memory_details_android.cc
index 649948b6..aed8fe1a 100644
--- a/chrome/browser/memory_details_android.cc
+++ b/chrome/browser/memory_details_android.cc
@@ -63,6 +63,8 @@
     std::unique_ptr<base::ProcessMetrics> metrics(
         base::ProcessMetrics::CreateProcessMetrics(*i));
     metrics->GetWorkingSetKBytes(&pmi.working_set);
+    // TODO(dcastagna): Compute number of open fds (pmi.num_open_fds) and soft
+    // limits (pmi.open_fds_soft_limit) on android.
 
     out->processes.push_back(pmi);
   }
diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
index ce5f426e..a7f4c544 100644
--- a/chrome/browser/memory_details_linux.cc
+++ b/chrome/browser/memory_details_linux.cc
@@ -71,6 +71,8 @@
     std::unique_ptr<base::ProcessMetrics> metrics(
         base::ProcessMetrics::CreateProcessMetrics(pid));
     metrics->GetWorkingSetKBytes(&pmi.working_set);
+    pmi.num_open_fds = metrics->GetOpenFdCount();
+    pmi.open_fds_soft_limit = metrics->GetOpenFdSoftLimit();
 
     process_data.processes.push_back(pmi);
   }
diff --git a/chrome/browser/metrics/metrics_memory_details.cc b/chrome/browser/metrics/metrics_memory_details.cc
index 807bc09..cea3e27 100644
--- a/chrome/browser/metrics/metrics_memory_details.cc
+++ b/chrome/browser/metrics/metrics_memory_details.cc
@@ -91,26 +91,45 @@
     size_t committed = browser.processes[index].committed.priv +
                        browser.processes[index].committed.mapped +
                        browser.processes[index].committed.image;
+    int num_open_fds = browser.processes[index].num_open_fds;
+    int open_fds_soft_limit = browser.processes[index].open_fds_soft_limit;
     aggregate_memory += sample;
     switch (browser.processes[index].process_type) {
       case content::PROCESS_TYPE_BROWSER:
         UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Browser.Large2", sample / 1024);
         UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Browser.Committed",
                                       committed / 1024);
+        if (num_open_fds != -1 && open_fds_soft_limit != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.Browser.OpenFDs", num_open_fds);
+          UMA_HISTOGRAM_COUNTS_10000("Memory.Browser.OpenFDsSoftLimit",
+                                     open_fds_soft_limit);
+        }
         continue;
       case content::PROCESS_TYPE_RENDERER: {
         UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.RendererAll", sample / 1024);
         UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.RendererAll.Committed",
                                       committed / 1024);
+        if (num_open_fds != -1 && open_fds_soft_limit != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.RendererAll.OpenFDs",
+                                     num_open_fds);
+          UMA_HISTOGRAM_COUNTS_10000("Memory.RendererAll.OpenFDsSoftLimit",
+                                     open_fds_soft_limit);
+        }
         ProcessMemoryInformation::RendererProcessType renderer_type =
             browser.processes[index].renderer_type;
         switch (renderer_type) {
           case ProcessMemoryInformation::RENDERER_EXTENSION:
             UMA_HISTOGRAM_MEMORY_KB("Memory.Extension", sample);
+            if (num_open_fds != -1) {
+              UMA_HISTOGRAM_COUNTS_10000("Memory.Extension.OpenFDs",
+                                         num_open_fds);
+            }
             extension_count++;
             continue;
           case ProcessMemoryInformation::RENDERER_CHROME:
             UMA_HISTOGRAM_MEMORY_KB("Memory.Chrome", sample);
+            if (num_open_fds != -1)
+              UMA_HISTOGRAM_COUNTS_10000("Memory.Chrome.OpenFDs", num_open_fds);
             chrome_count++;
             continue;
           case ProcessMemoryInformation::RENDERER_UNKNOWN:
@@ -123,24 +142,41 @@
                                           sample / 1024);
             UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Renderer.Committed",
                                           committed / 1024);
+            if (num_open_fds != -1) {
+              UMA_HISTOGRAM_COUNTS_10000("Memory.Renderer.OpenFDs",
+                                         num_open_fds);
+            }
             renderer_count++;
             continue;
         }
       }
       case content::PROCESS_TYPE_UTILITY:
         UMA_HISTOGRAM_MEMORY_KB("Memory.Utility", sample);
+        if (num_open_fds != -1)
+          UMA_HISTOGRAM_COUNTS_10000("Memory.Utility.OpenFDs", num_open_fds);
         other_count++;
         continue;
       case content::PROCESS_TYPE_ZYGOTE:
         UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample);
+        if (num_open_fds != -1)
+          UMA_HISTOGRAM_COUNTS_10000("Memory.Zygote.OpenFDs", num_open_fds);
         other_count++;
         continue;
       case content::PROCESS_TYPE_SANDBOX_HELPER:
         UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample);
+        if (num_open_fds != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.SandboxHelper.OpenFDs",
+                                     num_open_fds);
+        }
         other_count++;
         continue;
       case content::PROCESS_TYPE_GPU:
         UMA_HISTOGRAM_MEMORY_KB("Memory.Gpu", sample);
+        if (num_open_fds != -1 && open_fds_soft_limit != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.Gpu.OpenFDs", num_open_fds);
+          UMA_HISTOGRAM_COUNTS_10000("Memory.Gpu.OpenFDsSoftLimit",
+                                     open_fds_soft_limit);
+        }
         other_count++;
         continue;
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -152,20 +188,36 @@
           UMA_HISTOGRAM_MEMORY_KB("Memory.PepperFlashPlugin", sample);
         }
         UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPlugin", sample);
+        if (num_open_fds != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.PepperPlugin.OpenFDs",
+                                     num_open_fds);
+        }
         pepper_plugin_count++;
         continue;
       }
       case content::PROCESS_TYPE_PPAPI_BROKER:
         UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPluginBroker", sample);
+        if (num_open_fds != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.PepperPluginBroker.OpenFDs",
+                                     num_open_fds);
+        }
         pepper_plugin_broker_count++;
         continue;
 #endif
       case PROCESS_TYPE_NACL_LOADER:
         UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClient", sample);
+        if (num_open_fds != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.NativeClient.OpenFDs",
+                                     num_open_fds);
+        }
         other_count++;
         continue;
       case PROCESS_TYPE_NACL_BROKER:
         UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClientBroker", sample);
+        if (num_open_fds != -1) {
+          UMA_HISTOGRAM_COUNTS_10000("Memory.NativeClientBroker.OpenFDs",
+                                     num_open_fds);
+        }
         other_count++;
         continue;
       default:
diff --git a/chrome/browser/net/nqe/ui_network_quality_estimator_service.cc b/chrome/browser/net/nqe/ui_network_quality_estimator_service.cc
index fd5cf78..1ea9d129 100644
--- a/chrome/browser/net/nqe/ui_network_quality_estimator_service.cc
+++ b/chrome/browser/net/nqe/ui_network_quality_estimator_service.cc
@@ -21,6 +21,7 @@
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/nqe/network_qualities_prefs_manager.h"
+#include "net/nqe/network_quality.h"
 
 namespace {
 
@@ -145,9 +146,9 @@
 UINetworkQualityEstimatorService::UINetworkQualityEstimatorService(
     Profile* profile)
     : type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
-      http_rtt_(base::TimeDelta::FromMilliseconds(-1)),
-      transport_rtt_(base::TimeDelta::FromMilliseconds(-1)),
-      downstream_throughput_kbps_(-1),
+      http_rtt_(net::nqe::internal::InvalidRTT()),
+      transport_rtt_(net::nqe::internal::InvalidRTT()),
+      downstream_throughput_kbps_(net::nqe::internal::kInvalidThroughput),
       weak_factory_(this) {
   DCHECK(profile);
   // If this is running in a context without an IOThread, don't try to create
@@ -239,6 +240,33 @@
   effective_connection_type_observer_list_.RemoveObserver(observer);
 }
 
+base::Optional<base::TimeDelta> UINetworkQualityEstimatorService::GetHttpRTT()
+    const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (http_rtt_ == net::nqe::internal::InvalidRTT())
+    return base::Optional<base::TimeDelta>();
+  return http_rtt_;
+}
+
+base::Optional<base::TimeDelta>
+UINetworkQualityEstimatorService::GetTransportRTT() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (transport_rtt_ == net::nqe::internal::InvalidRTT())
+    return base::Optional<base::TimeDelta>();
+  return transport_rtt_;
+}
+
+base::Optional<int32_t>
+UINetworkQualityEstimatorService::GetDownstreamThroughputKbps() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (downstream_throughput_kbps_ == net::nqe::internal::kInvalidThroughput)
+    return base::Optional<int32_t>();
+  return downstream_throughput_kbps_;
+}
+
 void UINetworkQualityEstimatorService::AddRTTAndThroughputEstimatesObserver(
     net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/net/nqe/ui_network_quality_estimator_service.h b/chrome/browser/net/nqe/ui_network_quality_estimator_service.h
index af77188b..128456a 100644
--- a/chrome/browser/net/nqe/ui_network_quality_estimator_service.h
+++ b/chrome/browser/net/nqe/ui_network_quality_estimator_service.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_NET_NQE_UI_NETWORK_QUALITY_ESTIMATOR_SERVICE_H_
 #define CHROME_BROWSER_NET_NQE_UI_NETWORK_QUALITY_ESTIMATOR_SERVICE_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <memory>
 
@@ -46,6 +48,9 @@
   void RemoveEffectiveConnectionTypeObserver(
       net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
       override;
+  base::Optional<base::TimeDelta> GetHttpRTT() const override;
+  base::Optional<base::TimeDelta> GetTransportRTT() const override;
+  base::Optional<int32_t> GetDownstreamThroughputKbps() const override;
 
   // Must be called on the UI thread. |observer| will be notified on the UI
   // thread. |observer| would be notified of the changes in the HTTP RTT,
diff --git a/chrome/browser/net/nqe/ui_network_quality_estimator_service_browsertest.cc b/chrome/browser/net/nqe/ui_network_quality_estimator_service_browsertest.cc
index e4119d5..72d41e60 100644
--- a/chrome/browser/net/nqe/ui_network_quality_estimator_service_browsertest.cc
+++ b/chrome/browser/net/nqe/ui_network_quality_estimator_service_browsertest.cc
@@ -257,11 +257,19 @@
   nqe_test_util::OverrideRTTsAndWait(rtt_1);
   EXPECT_EQ(rtt_1, nqe_observer.http_rtt());
 
+  EXPECT_EQ(rtt_1, nqe_service->GetHttpRTT());
+  EXPECT_EQ(rtt_1, nqe_service->GetTransportRTT());
+  EXPECT_FALSE(nqe_service->GetDownstreamThroughputKbps().has_value());
+
   base::TimeDelta rtt_2 = base::TimeDelta::FromMilliseconds(200);
 
   nqe_test_util::OverrideRTTsAndWait(rtt_2);
   EXPECT_EQ(rtt_2, nqe_observer.http_rtt());
 
+  EXPECT_EQ(rtt_2, nqe_service->GetHttpRTT());
+  EXPECT_EQ(rtt_2, nqe_service->GetTransportRTT());
+  EXPECT_FALSE(nqe_service->GetDownstreamThroughputKbps().has_value());
+
   nqe_service->RemoveRTTAndThroughputEstimatesObserver(&nqe_observer);
 
   base::TimeDelta rtt_3 = base::TimeDelta::FromMilliseconds(300);
@@ -269,6 +277,10 @@
   nqe_test_util::OverrideRTTsAndWait(rtt_3);
   EXPECT_EQ(rtt_2, nqe_observer.http_rtt());
 
+  EXPECT_EQ(rtt_3, nqe_service->GetHttpRTT());
+  EXPECT_EQ(rtt_3, nqe_service->GetTransportRTT());
+  EXPECT_FALSE(nqe_service->GetDownstreamThroughputKbps().has_value());
+
   // Observer should be notified on addition.
   TestRTTAndThroughputEstimatesObserver nqe_observer_2;
   nqe_service->AddRTTAndThroughputEstimatesObserver(&nqe_observer_2);
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 29277e1..dfee46c 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -11,6 +11,7 @@
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
+#include "base/mac/scoped_mach_port.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/nullable_string16.h"
 #include "base/strings/string_number_conversions.h"
@@ -25,13 +26,15 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h"
-#import "chrome/browser/ui/cocoa/notifications/notification_delivery.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
+#import "chrome/browser/ui/cocoa/notifications/notification_delivery.h"
 #import "chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h"
 #include "chrome/common/features.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/crash/content/app/crashpad.h"
 #include "components/url_formatter/elide_url.h"
 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationConstants.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -428,6 +431,11 @@
 @implementation AlertDispatcherImpl {
   // The connection to the XPC server in charge of delivering alerts.
   base::scoped_nsobject<NSXPCConnection> xpcConnection_;
+
+  // YES if the remote object has had |-setMachExceptionPort:| called
+  // since the service was last started, interrupted, or invalidated.
+  // If NO, then -serviceProxy will set the exception port.
+  BOOL setExceptionPort_;
 }
 
 - (instancetype)init {
@@ -443,12 +451,14 @@
 
     xpcConnection_.get().interruptionHandler = ^{
       LOG(WARNING) << "connection interrupted: interruptionHandler: ";
+      setExceptionPort_ = NO;
       // TODO(miguelg): perhaps add some UMA here.
       // We will be getting this handler both when the XPC server crashes or
       // when it decides to close the connection.
     };
     xpcConnection_.get().invalidationHandler = ^{
       LOG(WARNING) << "connection invalidationHandler received";
+      setExceptionPort_ = NO;
       // This means that the connection should be recreated if it needs
       // to be used again. It should not really happen.
       DCHECK(false) << "XPC Connection invalidated";
@@ -464,23 +474,44 @@
 }
 
 - (void)dispatchNotification:(NSDictionary*)data {
-  [[xpcConnection_ remoteObjectProxy] deliverNotification:data];
+  [[self serviceProxy] deliverNotification:data];
 }
 
 - (void)closeNotificationWithId:(NSString*)notificationId
                   withProfileId:(NSString*)profileId {
-  [[xpcConnection_ remoteObjectProxy] closeNotificationWithId:notificationId
-                                                withProfileId:profileId];
+  [[self serviceProxy] closeNotificationWithId:notificationId
+                                 withProfileId:profileId];
 }
 
 - (void)closeAllNotifications {
-  [[xpcConnection_ remoteObjectProxy] closeAllNotifications];
+  [[self serviceProxy] closeAllNotifications];
 }
 
-// NotificationReply implementation
+// NotificationReply:
+
 - (void)notificationClick:(NSDictionary*)notificationResponseData {
   NotificationPlatformBridgeMac::ProcessNotificationResponse(
       notificationResponseData);
 }
 
+// Private methods:
+
+// Retrieves the connection's remoteObjectProxy. Always use this as opposed
+// to going directly through the connection, since this will ensure that the
+// service has its exception port configured for crash reporting.
+- (id<NotificationDelivery>)serviceProxy {
+  id<NotificationDelivery> proxy = [xpcConnection_ remoteObjectProxy];
+
+  if (!setExceptionPort_) {
+    base::mac::ScopedMachSendRight exceptionPort(
+        crash_reporter::GetCrashpadClient().GetHandlerMachPort());
+    base::scoped_nsobject<CrXPCMachPort> xpcPort(
+        [[CrXPCMachPort alloc] initWithMachSendRight:std::move(exceptionPort)]);
+    [proxy setMachExceptionPort:xpcPort];
+    setExceptionPort_ = YES;
+  }
+
+  return proxy;
+}
+
 @end
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index c17e82e..e1d11c4 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -104,6 +104,22 @@
   RegisterInputEventObserver(new_host);
 }
 
+void MetricsWebContentsObserver::MediaStartedPlaying(
+    const content::WebContentsObserver::MediaPlayerInfo& video_type,
+    const content::WebContentsObserver::MediaPlayerId& id) {
+  content::RenderFrameHost* render_frame_host = id.first;
+  if (!render_frame_host->GetRenderViewHost() ||
+      render_frame_host->GetRenderViewHost()->GetMainFrame() !=
+          web_contents()->GetMainFrame()) {
+    // Ignore media that starts playing in a document that was navigated away
+    // from.
+    return;
+  }
+  if (committed_load_)
+    committed_load_->MediaStartedPlaying(
+        video_type, render_frame_host == web_contents()->GetMainFrame());
+}
+
 bool MetricsWebContentsObserver::OnMessageReceived(
     const IPC::Message& message,
     content::RenderFrameHost* render_frame_host) {
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 681e7ce..a725db2 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -65,6 +65,9 @@
   void RenderProcessGone(base::TerminationStatus status) override;
   void RenderViewHostChanged(content::RenderViewHost* old_host,
                              content::RenderViewHost* new_host) override;
+  void MediaStartedPlaying(
+      const content::WebContentsObserver::MediaPlayerInfo& video_type,
+      const content::WebContentsObserver::MediaPlayerId& id) override;
 
   // These methods are forwarded from the MetricsNavigationThrottle.
   void WillStartNavigationRequest(content::NavigationHandle* navigation_handle);
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
new file mode 100644
index 0000000..6e73c491
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h"
+
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "chrome/common/page_load_metrics/page_load_timing.h"
+
+namespace {
+
+const char kHistogramNetworkBytes[] =
+    "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network";
+const char kHistogramCacheBytes[] =
+    "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache";
+const char kHistogramTotalBytes[] =
+    "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total";
+
+}  // namespace
+
+MediaPageLoadMetricsObserver::MediaPageLoadMetricsObserver()
+    : cache_bytes_(0), network_bytes_(0), played_media_(false) {}
+
+MediaPageLoadMetricsObserver::~MediaPageLoadMetricsObserver() = default;
+
+void MediaPageLoadMetricsObserver::OnLoadedResource(
+    const page_load_metrics::ExtraRequestInfo& extra_request_info) {
+  if (extra_request_info.was_cached) {
+    cache_bytes_ += extra_request_info.raw_body_bytes;
+  } else {
+    network_bytes_ += extra_request_info.raw_body_bytes;
+  }
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+MediaPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
+    const page_load_metrics::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
+  // app is about to be backgrounded, as part of the Activity.onPause()
+  // flow. After this method is invoked, Chrome may be killed without further
+  // notification, so we record final metrics collected up to this point.
+  if (info.did_commit && played_media_) {
+    RecordByteHistograms();
+  }
+  return STOP_OBSERVING;
+}
+
+void MediaPageLoadMetricsObserver::OnComplete(
+    const page_load_metrics::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!played_media_)
+    return;
+  RecordByteHistograms();
+}
+
+void MediaPageLoadMetricsObserver::MediaStartedPlaying(
+    const content::WebContentsObserver::MediaPlayerInfo& video_type,
+    bool is_in_main_frame) {
+  if (played_media_)
+    return;
+  // Track media (audio or video) in all frames of the page load.
+  played_media_ = true;
+}
+
+void MediaPageLoadMetricsObserver::RecordByteHistograms() {
+  DCHECK(played_media_);
+  PAGE_BYTES_HISTOGRAM(kHistogramNetworkBytes, network_bytes_);
+  PAGE_BYTES_HISTOGRAM(kHistogramCacheBytes, cache_bytes_);
+  PAGE_BYTES_HISTOGRAM(kHistogramTotalBytes, network_bytes_ + cache_bytes_);
+}
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
new file mode 100644
index 0000000..bc3c684
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_MEDIA_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_MEDIA_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace page_load_metrics {
+struct PageLoadExtraInfo;
+struct PageLoadTiming;
+}
+
+// Observer responsible for recording metrics on pages that play at least one
+// MEDIA request.
+class MediaPageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+  MediaPageLoadMetricsObserver();
+  ~MediaPageLoadMetricsObserver() override;
+
+  // page_load_metrics::PageLoadMetricsObserver:
+  void OnComplete(const page_load_metrics::PageLoadTiming& timing,
+                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+  FlushMetricsOnAppEnterBackground(
+      const page_load_metrics::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnLoadedResource(
+      const page_load_metrics::ExtraRequestInfo& extra_request_info) override;
+  void MediaStartedPlaying(
+      const content::WebContentsObserver::MediaPlayerInfo& video_type,
+      bool is_in_main_frame) override;
+
+ private:
+  // Records histograms for byte information.
+  void RecordByteHistograms();
+
+  // The number of body (not header) prefilter bytes consumed by requests for
+  // the page.
+  int64_t cache_bytes_;
+  int64_t network_bytes_;
+
+  // Whether the page load played a media element.
+  bool played_media_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaPageLoadMetricsObserver);
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_MEDIA_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
new file mode 100644
index 0000000..d8e9e8c
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/time/time.h"
+#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/common/page_load_metrics/page_load_timing.h"
+#include "third_party/WebKit/public/platform/WebLoadingBehaviorFlag.h"
+#include "url/gurl.h"
+
+namespace {
+
+const char kDefaultTestUrl[] = "https://google.com";
+
+}  // namespace
+
+class MediaPageLoadMetricsObserverTest
+    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+ public:
+  MediaPageLoadMetricsObserverTest() {}
+  ~MediaPageLoadMetricsObserverTest() override = default;
+
+  void ResetTest() {
+    // Reset to the default testing state. Does not reset histogram state.
+    timing_.navigation_start = base::Time::FromDoubleT(1);
+    timing_.response_start = base::TimeDelta::FromSeconds(2);
+    timing_.parse_start = base::TimeDelta::FromSeconds(3);
+    timing_.first_contentful_paint = base::TimeDelta::FromSeconds(4);
+    timing_.first_image_paint = base::TimeDelta::FromSeconds(5);
+    timing_.first_text_paint = base::TimeDelta::FromSeconds(6);
+    timing_.load_event_start = base::TimeDelta::FromSeconds(7);
+    PopulateRequiredTimingFields(&timing_);
+
+    network_bytes_ = 0;
+    cache_bytes_ = 0;
+  }
+
+  void SimulatePageLoad(bool simulate_play_media,
+                        bool simulate_app_background) {
+    NavigateAndCommit(GURL(kDefaultTestUrl));
+
+    if (simulate_play_media)
+      SimulateMediaPlayed();
+
+    SimulateTimingUpdate(timing_);
+
+    // Prepare 4 resources of varying size and configurations.
+    page_load_metrics::ExtraRequestInfo resources[] = {
+        // Cached request.
+        {true /*was_cached*/, 1024 * 40 /* raw_body_bytes */,
+         false /* data_reduction_proxy_used*/,
+         0 /* original_network_content_length */},
+        // Uncached non-proxied request.
+        {false /*was_cached*/, 1024 * 40 /* raw_body_bytes */,
+         false /* data_reduction_proxy_used*/,
+         1024 * 40 /* original_network_content_length */},
+        // Uncached proxied request with .1 compression ratio.
+        {false /*was_cached*/, 1024 * 40 /* raw_body_bytes */,
+         false /* data_reduction_proxy_used*/,
+         1024 * 40 /* original_network_content_length */},
+        // Uncached proxied request with .5 compression ratio.
+        {false /*was_cached*/, 1024 * 40 /* raw_body_bytes */,
+         false /* data_reduction_proxy_used*/,
+         1024 * 40 /* original_network_content_length */},
+    };
+
+    for (auto request : resources) {
+      SimulateLoadedResource(request);
+      if (!request.was_cached) {
+        network_bytes_ += request.raw_body_bytes;
+      } else {
+        cache_bytes_ += request.raw_body_bytes;
+      }
+    }
+
+    if (simulate_app_background) {
+      // The histograms should be logged when the app is backgrounded.
+      SimulateAppEnterBackground();
+    } else {
+      NavigateToUntrackedUrl();
+    }
+  }
+
+ protected:
+  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
+    tracker->AddObserver(base::MakeUnique<MediaPageLoadMetricsObserver>());
+  }
+
+  // Simulated byte usage since the last time the test was reset.
+  int64_t network_bytes_;
+  int64_t cache_bytes_;
+
+ private:
+  page_load_metrics::PageLoadTiming timing_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaPageLoadMetricsObserverTest);
+};
+
+TEST_F(MediaPageLoadMetricsObserverTest, MediaPlayed) {
+  ResetTest();
+  SimulatePageLoad(true /* simulate_play_media */,
+                   false /* simulate_app_background */);
+
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
+      static_cast<int>(network_bytes_ / 1024), 1);
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache",
+      static_cast<int>(cache_bytes_ / 1024), 1);
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total",
+      static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
+}
+
+TEST_F(MediaPageLoadMetricsObserverTest, MediaPlayedAppBackground) {
+  ResetTest();
+  SimulatePageLoad(true /* simulate_play_media */,
+                   true /* simulate_app_background */);
+
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
+      static_cast<int>(network_bytes_ / 1024), 1);
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache",
+      static_cast<int>(cache_bytes_ / 1024), 1);
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total",
+      static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
+}
+
+TEST_F(MediaPageLoadMetricsObserverTest, MediaNotPlayed) {
+  ResetTest();
+  SimulatePageLoad(false /* simulate_play_media */,
+                   false /* simulate_app_background */);
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network", 0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache", 0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total", 0);
+}
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
index 3de5193..c74e1b8 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
@@ -14,6 +14,7 @@
 #include "chrome/common/page_load_metrics/page_load_metrics_messages.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/web_contents_tester.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 
@@ -152,6 +153,14 @@
   observer_->FlushMetricsOnAppEnterBackground();
 }
 
+void PageLoadMetricsObserverTestHarness::SimulateMediaPlayed() {
+  content::WebContentsObserver::MediaPlayerInfo video_type(
+      true /* in_has_video*/);
+  content::RenderFrameHost* render_frame_host = web_contents()->GetMainFrame();
+  observer_->MediaStartedPlaying(video_type,
+                                 std::make_pair(render_frame_host, 0));
+}
+
 const base::HistogramTester&
 PageLoadMetricsObserverTestHarness::histogram_tester() const {
   return histogram_tester_;
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
index 67d244e..2f6fd42 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
@@ -65,6 +65,9 @@
   // Simulates the app being backgrounded.
   void SimulateAppEnterBackground();
 
+  // Simulate playing a media element.
+  void SimulateMediaPlayed();
+
   const base::HistogramTester& histogram_tester() const;
 
   // Gets the PageLoadExtraInfo for the committed_load_ in observer_.
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index ffca700..21940835 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/google_captcha_observer.h"
 #include "chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h"
@@ -79,8 +80,9 @@
         base::MakeUnique<google_captcha_observer::GoogleCaptchaObserver>());
     tracker->AddObserver(
         base::MakeUnique<DocumentWritePageLoadMetricsObserver>());
+    tracker->AddObserver(base::MakeUnique<MediaPageLoadMetricsObserver>());
     tracker->AddObserver(
-        base::WrapUnique(new previews::PreviewsPageLoadMetricsObserver()));
+        base::MakeUnique<previews::PreviewsPageLoadMetricsObserver>());
     tracker->AddObserver(
         base::MakeUnique<ServiceWorkerPageLoadMetricsObserver>());
     tracker->AddObserver(base::MakeUnique<SubresourceFilterMetricsObserver>());
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index 80600db..41acd9c 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -11,6 +11,7 @@
 #include "base/optional.h"
 #include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "url/gurl.h"
 
@@ -323,6 +324,11 @@
   virtual void OnLoadingBehaviorObserved(
       const page_load_metrics::PageLoadExtraInfo& extra_info) {}
 
+  // Invoked when a media element starts playing.
+  virtual void MediaStartedPlaying(
+      const content::WebContentsObserver::MediaPlayerInfo& video_type,
+      bool is_in_main_frame) {}
+
   // Invoked when the UMA metrics subsystem is persisting metrics as the
   // application goes into the background, on platforms where the browser
   // process may be killed after backgrounding (Android). Implementers should
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index aff76c3..dfccbaf 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "ui/base/page_transition_types.h"
 
@@ -732,4 +733,11 @@
   }
 }
 
+void PageLoadTracker::MediaStartedPlaying(
+    const content::WebContentsObserver::MediaPlayerInfo& video_type,
+    bool is_in_main_frame) {
+  for (const auto& observer : observers_)
+    observer->MediaStartedPlaying(video_type, is_in_main_frame);
+}
+
 }  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index bbd86413..08111a55 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/page_load_metrics/user_input_tracker.h"
 #include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "content/public/browser/global_request_id.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "ui/base/page_transition_types.h"
 
 class GURL;
@@ -231,6 +232,11 @@
   bool HasMatchingNavigationRequestID(
       const content::GlobalRequestID& request_id) const;
 
+  // Invoked when a media element starts playing.
+  void MediaStartedPlaying(
+      const content::WebContentsObserver::MediaPlayerInfo& video_type,
+      bool is_in_main_frame);
+
  private:
   // This function converts a TimeTicks value taken in the browser process
   // to navigation_start_ if:
diff --git a/chrome/browser/permissions/delegation_tracker.cc b/chrome/browser/permissions/delegation_tracker.cc
deleted file mode 100644
index 9b1183d..0000000
--- a/chrome/browser/permissions/delegation_tracker.cc
+++ /dev/null
@@ -1,103 +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.
-
-#include "chrome/browser/permissions/delegation_tracker.h"
-
-#include <unordered_set>
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/permissions/permission_util.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-
-class DelegationTracker::DelegatedForChild : content::WebContentsObserver {
- public:
-  DelegatedForChild(content::RenderFrameHost* child_rfh,
-                    const std::vector<ContentSettingsType>& permissions,
-                    const base::Callback<void(content::RenderFrameHost*)>&
-                        rfh_destroyed_callback)
-      : content::WebContentsObserver(
-            content::WebContents::FromRenderFrameHost(child_rfh)),
-        child_rfh_(child_rfh),
-        permissions_(permissions.begin(), permissions.end()),
-        rfh_destroyed_callback_(rfh_destroyed_callback) {}
-
-  ~DelegatedForChild() override {}
-
-  bool HasPermission(ContentSettingsType permission) {
-    return permissions_.count(permission) == 1;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DelegatedForChild);
-
-  void ClearPermissions(content::RenderFrameHost* render_frame_host) {
-    if (render_frame_host == child_rfh_)
-      rfh_destroyed_callback_.Run(render_frame_host);  // Will delete |this|.
-  }
-
-  // WebContentsObserver
-  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
-                              content::RenderFrameHost* new_host) override {
-    ClearPermissions(old_host);
-  }
-
-  void FrameDeleted(content::RenderFrameHost* render_frame_host) override {
-    ClearPermissions(render_frame_host);
-  }
-
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override {
-    if (navigation_handle->HasCommitted())
-      ClearPermissions(navigation_handle->GetRenderFrameHost());
-  }
-
-  content::RenderFrameHost* child_rfh_;
-
-  std::unordered_set<ContentSettingsType, ContentSettingsTypeHash> permissions_;
-
-  base::Callback<void(content::RenderFrameHost*)> rfh_destroyed_callback_;
-};
-
-DelegationTracker::DelegationTracker() {}
-
-DelegationTracker::~DelegationTracker() {}
-
-void DelegationTracker::SetDelegatedPermissions(
-    content::RenderFrameHost* child_rfh,
-    const std::vector<ContentSettingsType>& permissions) {
-  DCHECK(child_rfh && child_rfh->GetParent());
-  delegated_permissions_[child_rfh] = base::MakeUnique<DelegatedForChild>(
-      child_rfh, permissions,
-      base::Bind(&DelegationTracker::RenderFrameHostChanged,
-                 base::Unretained(this)));
-}
-
-bool DelegationTracker::IsGranted(content::RenderFrameHost* requesting_rfh,
-                                  ContentSettingsType permission) {
-  content::RenderFrameHost* child_rfh = requesting_rfh;
-  while (child_rfh->GetParent()) {
-    // Parents with unique origins can't delegate permission.
-    url::Origin parent_origin =
-        child_rfh->GetParent()->GetLastCommittedOrigin();
-    if (parent_origin.unique())
-      return false;
-
-    if (!child_rfh->GetLastCommittedOrigin().IsSameOriginWith(parent_origin)) {
-      const auto& it = delegated_permissions_.find(child_rfh);
-      if (it == delegated_permissions_.end())
-        return false;
-      if (!it->second->HasPermission(permission))
-        return false;
-    }
-    child_rfh = child_rfh->GetParent();
-  }
-  return true;
-}
-
-void DelegationTracker::RenderFrameHostChanged(content::RenderFrameHost* rfh) {
-  delegated_permissions_.erase(rfh);
-}
diff --git a/chrome/browser/permissions/delegation_tracker.h b/chrome/browser/permissions/delegation_tracker.h
deleted file mode 100644
index 68c1e79..0000000
--- a/chrome/browser/permissions/delegation_tracker.h
+++ /dev/null
@@ -1,59 +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 CHROME_BROWSER_PERMISSIONS_DELEGATION_TRACKER_H_
-#define CHROME_BROWSER_PERMISSIONS_DELEGATION_TRACKER_H_
-
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-#include "base/macros.h"
-#include "components/content_settings/core/common/content_settings_types.h"
-
-namespace content {
-
-class RenderFrameHost;
-
-}  // namespace content
-
-// Keeps track of which permissions are delegated to frames. There are two
-// operations possible:
-// 1) Setting the permissions which are delegated to a frame by its parent, and
-// 2) Querying whether a particular permission is granted to a frame.
-//
-// If a frame is destroyed or navigated, permissions will no longer be delegated
-// to it.
-class DelegationTracker {
- public:
-  DelegationTracker();
-  ~DelegationTracker();
-
-  // Set the |permissions| which are delegated to |child_rfh| by its parent.
-  void SetDelegatedPermissions(
-      content::RenderFrameHost* child_rfh,
-      const std::vector<ContentSettingsType>& permissions);
-
-  // Query whether |permission| is granted to |requesting_rfh|. This will return
-  // true if |requesting_rfh| is a top-level frame or if it has been delegated
-  // |permission| through its ancestor frames. Specifically, each frame on the
-  // path between the main frame and |requesting_rfh| must either delegate
-  // |permission| to it's child OR have the same origin as it's child on that
-  // path in order for this to return true.
-  bool IsGranted(content::RenderFrameHost* requesting_rfh,
-                 ContentSettingsType permission);
-
- private:
-  class DelegatedForChild;
-
-  void RenderFrameHostChanged(content::RenderFrameHost* rfh);
-
-  std::unordered_map<content::RenderFrameHost*,
-                     std::unique_ptr<DelegatedForChild>>
-      delegated_permissions_;
-
-  DISALLOW_COPY_AND_ASSIGN(DelegationTracker);
-};
-
-#endif  // CHROME_BROWSER_PERMISSIONS_DELEGATION_TRACKER_H_
diff --git a/chrome/browser/permissions/delegation_tracker_unittest.cc b/chrome/browser/permissions/delegation_tracker_unittest.cc
deleted file mode 100644
index f00bb14..0000000
--- a/chrome/browser/permissions/delegation_tracker_unittest.cc
+++ /dev/null
@@ -1,227 +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.
-
-#include "chrome/browser/permissions/delegation_tracker.h"
-
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/test_renderer_host.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-const char* kOrigin1 = "https://google.com";
-const char* kOrigin2 = "https://maps.google.com";
-const char* kOrigin3 = "https://example.com";
-const char* kUniqueOrigin = "about:blank";
-
-class DelegationTrackerTest : public ChromeRenderViewHostTestHarness {
- protected:
-  content::RenderFrameHost* GetMainRFH(const char* origin) {
-    content::RenderFrameHost* result = web_contents()->GetMainFrame();
-    content::RenderFrameHostTester::For(result)
-        ->InitializeRenderFrameIfNeeded();
-    content::RenderFrameHostTester::For(result)->SimulateNavigationCommit(
-        GURL(origin));
-    return result;
-  }
-
-  content::RenderFrameHost* AddChildRFH(content::RenderFrameHost* parent,
-                                        const char* origin) {
-    content::RenderFrameHost* result =
-        content::RenderFrameHostTester::For(parent)->AppendChild("");
-    content::RenderFrameHostTester::For(result)
-        ->InitializeRenderFrameIfNeeded();
-    content::RenderFrameHostTester::For(result)->SimulateNavigationCommit(
-        GURL(origin));
-    return result;
-  }
-};
-
-TEST_F(DelegationTrackerTest, SingleFrame) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* parent = GetMainRFH(kOrigin1);
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, SingleAncestorSameOrigin) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* parent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* child = AddChildRFH(parent, kOrigin1);
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, SingleAncestorNoDelegation) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* parent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, SingleAncestorPermissionDelegated) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* parent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
-
-  tracker.SetDelegatedPermissions(child, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
-}
-
-TEST_F(DelegationTrackerTest, SingleAncestorMultiplePermissionsDelegated) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* parent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
-
-  tracker.SetDelegatedPermissions(child, {CONTENT_SETTINGS_TYPE_GEOLOCATION,
-                                          CONTENT_SETTINGS_TYPE_NOTIFICATIONS});
-
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
-}
-
-TEST_F(DelegationTrackerTest, SingleAncestorMultipleChildren) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* parent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* child1 = AddChildRFH(parent, kOrigin2);
-  content::RenderFrameHost* child2 = AddChildRFH(parent, kOrigin2);
-
-  tracker.SetDelegatedPermissions(child1, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(tracker.IsGranted(child1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, MultipleAncestorsNotDelegated) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* grandparent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* parent = AddChildRFH(grandparent, kOrigin2);
-  content::RenderFrameHost* child1 = AddChildRFH(parent, kOrigin3);
-  content::RenderFrameHost* child2 = AddChildRFH(parent, kOrigin3);
-
-  tracker.SetDelegatedPermissions(child1, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_FALSE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, MultipleAncestorsDelegated) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* grandparent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* parent = AddChildRFH(grandparent, kOrigin2);
-  content::RenderFrameHost* child1 = AddChildRFH(parent, kOrigin3);
-  content::RenderFrameHost* child2 = AddChildRFH(parent, kOrigin3);
-
-  tracker.SetDelegatedPermissions(parent, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-  tracker.SetDelegatedPermissions(child1, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, MultipleAncestorsSameOrigin) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* grandparent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* parent = AddChildRFH(grandparent, kOrigin1);
-  content::RenderFrameHost* child1 = AddChildRFH(parent, kOrigin1);
-  content::RenderFrameHost* child2 = AddChildRFH(parent, kOrigin1);
-
-  tracker.SetDelegatedPermissions(parent, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-  tracker.SetDelegatedPermissions(child1, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, MultipleAncestorsComplexSinglePermission) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* great_grandparent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* grandparent =
-      AddChildRFH(great_grandparent, kOrigin2);
-  content::RenderFrameHost* parent1 = AddChildRFH(grandparent, kOrigin2);
-  content::RenderFrameHost* parent2 = AddChildRFH(grandparent, kOrigin3);
-  content::RenderFrameHost* child = AddChildRFH(parent1, kOrigin3);
-
-  tracker.SetDelegatedPermissions(grandparent,
-                                  {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-  tracker.SetDelegatedPermissions(child, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(
-      tracker.IsGranted(grandparent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(parent1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(parent2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, MultipleAncestorsComplexMultiplePermissions) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* great_grandparent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* grandparent =
-      AddChildRFH(great_grandparent, kOrigin2);
-  content::RenderFrameHost* parent1 = AddChildRFH(grandparent, kOrigin2);
-  content::RenderFrameHost* parent2 = AddChildRFH(grandparent, kOrigin3);
-  content::RenderFrameHost* child = AddChildRFH(parent1, kOrigin3);
-
-  tracker.SetDelegatedPermissions(
-      grandparent,
-      {CONTENT_SETTINGS_TYPE_GEOLOCATION, CONTENT_SETTINGS_TYPE_NOTIFICATIONS});
-  tracker.SetDelegatedPermissions(child, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(
-      tracker.IsGranted(grandparent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(
-      tracker.IsGranted(grandparent, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
-
-  EXPECT_TRUE(tracker.IsGranted(parent1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(parent1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
-
-  EXPECT_FALSE(tracker.IsGranted(parent2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(parent2, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
-
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
-}
-
-TEST_F(DelegationTrackerTest, RenderFrameHostChanged) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* grandparent = GetMainRFH(kOrigin1);
-  content::RenderFrameHost* parent = AddChildRFH(grandparent, kOrigin2);
-  content::RenderFrameHost* child = AddChildRFH(parent, kOrigin3);
-
-  tracker.SetDelegatedPermissions(parent, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-  tracker.SetDelegatedPermissions(child, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  EXPECT_TRUE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_TRUE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-
-  content::RenderFrameHostTester::For(parent)->SimulateNavigationCommit(
-      GURL(kUniqueOrigin));
-
-  EXPECT_FALSE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
-
-TEST_F(DelegationTrackerTest, UniqueOrigins) {
-  DelegationTracker tracker;
-  content::RenderFrameHost* grandparent = GetMainRFH(kUniqueOrigin);
-  content::RenderFrameHost* parent = AddChildRFH(grandparent, kOrigin2);
-  content::RenderFrameHost* child = AddChildRFH(parent, kOrigin3);
-
-  tracker.SetDelegatedPermissions(parent, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-  tracker.SetDelegatedPermissions(child, {CONTENT_SETTINGS_TYPE_GEOLOCATION});
-
-  // Unique origins should never be able to delegate permission.
-  EXPECT_FALSE(tracker.IsGranted(parent, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-  EXPECT_FALSE(tracker.IsGranted(child, CONTENT_SETTINGS_TYPE_GEOLOCATION));
-}
diff --git a/chrome/browser/permissions/permission_prompt_android.h b/chrome/browser/permissions/permission_prompt_android.h
index e115345..9c052e2 100644
--- a/chrome/browser/permissions/permission_prompt_android.h
+++ b/chrome/browser/permissions/permission_prompt_android.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_PERMISSION_PROMPT_ANDROID_H_
 #define CHROME_BROWSER_PERMISSIONS_PERMISSION_PROMPT_ANDROID_H_
 
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 
 class InfoBarService;
 
diff --git a/chrome/browser/permissions/permission_request_manager.cc b/chrome/browser/permissions/permission_request_manager.cc
index eab05c2..1ed9063 100644
--- a/chrome/browser/permissions/permission_request_manager.cc
+++ b/chrome/browser/permissions/permission_request_manager.cc
@@ -12,7 +12,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/permissions/permission_request.h"
 #include "chrome/browser/permissions/permission_uma_util.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/permissions/permission_request_manager.h b/chrome/browser/permissions/permission_request_manager.h
index f6c4c4a..b2df0b9 100644
--- a/chrome/browser/permissions/permission_request_manager.h
+++ b/chrome/browser/permissions/permission_request_manager.h
@@ -11,7 +11,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index 4e74b17..e018fa6 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -7,11 +7,18 @@
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
 #include "build/build_config.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
+#include "chrome/browser/custom_handlers/register_protocol_handler_permission_request.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/media/webrtc/media_stream_devices_controller.h"
 #include "chrome/browser/permissions/permission_context_base.h"
+#include "chrome/browser/permissions/permission_request_impl.h"
 #include "chrome/browser/permissions/permission_util.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -20,6 +27,39 @@
 #include "content/public/test/test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
+namespace test {
+class MediaStreamDevicesControllerTestApi
+    : public MediaStreamDevicesController::PermissionPromptDelegate {
+ public:
+  static void AddRequestToManager(
+      PermissionRequestManager* manager,
+      content::WebContents* web_contents,
+      const content::MediaStreamRequest& request,
+      const content::MediaResponseCallback& callback) {
+    MediaStreamDevicesControllerTestApi delegate(manager);
+    MediaStreamDevicesController::RequestPermissionsWithDelegate(
+        web_contents, request, callback, &delegate);
+  }
+
+ private:
+  // MediaStreamDevicesController::PermissionPromptDelegate:
+  void ShowPrompt(
+      bool user_gesture,
+      content::WebContents* web_contents,
+      std::unique_ptr<MediaStreamDevicesController> controller) override {
+    manager_->AddRequest(controller.release());
+  }
+
+  explicit MediaStreamDevicesControllerTestApi(
+      PermissionRequestManager* manager)
+      : manager_(manager) {}
+
+  PermissionRequestManager* manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaStreamDevicesControllerTestApi);
+};
+}  // namespace test
+
 namespace {
 
 const char* kPermissionsKillSwitchFieldStudy =
@@ -68,8 +108,166 @@
 
  private:
   std::unique_ptr<MockPermissionPromptFactory> mock_permission_prompt_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionRequestManagerBrowserTest);
 };
 
+// Harness for testing permissions dialogs invoked by PermissionRequestManager.
+// Uses a "real" PermissionPromptFactory rather than a mock.
+class PermissionDialogTest
+    : public SupportsTestDialog<PermissionRequestManagerBrowserTest> {
+ public:
+  PermissionDialogTest() {}
+
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    // Skip super: It will install a mock permission UI factory, but for this
+    // test we want to show "real" UI.
+    InProcessBrowserTest::SetUpOnMainThread();
+  }
+
+ private:
+  GURL GetUrl() { return GURL("https://example.com"); }
+
+  PermissionRequest* MakeRegisterProtocolHandlerRequest();
+  void AddMediaRequest(PermissionRequestManager* manager,
+                       ContentSettingsType permission);
+  PermissionRequest* MakePermissionRequest(ContentSettingsType permission);
+
+  // TestBrowserDialog:
+  void ShowDialog(const std::string& name) override;
+
+  // Holds requests that do not delete themselves.
+  std::vector<std::unique_ptr<PermissionRequest>> owned_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionDialogTest);
+};
+
+PermissionRequest* PermissionDialogTest::MakeRegisterProtocolHandlerRequest() {
+  std::string protocol = "mailto";
+  bool user_gesture = true;
+  ProtocolHandler handler =
+      ProtocolHandler::CreateProtocolHandler(protocol, GetUrl());
+  ProtocolHandlerRegistry* registry =
+      ProtocolHandlerRegistryFactory::GetForBrowserContext(
+          browser()->profile());
+  // Deleted in RegisterProtocolHandlerPermissionRequest::RequestFinished().
+  return new RegisterProtocolHandlerPermissionRequest(registry, handler,
+                                                      GetUrl(), user_gesture);
+}
+
+void PermissionDialogTest::AddMediaRequest(PermissionRequestManager* manager,
+                                           ContentSettingsType permission) {
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  content::MediaStreamRequestType request_type = content::MEDIA_DEVICE_ACCESS;
+  content::MediaStreamType audio_type = content::MEDIA_NO_SERVICE;
+  content::MediaStreamType video_type = content::MEDIA_NO_SERVICE;
+  std::string audio_id = "audio_id";
+  std::string video_id = "video_id";
+
+  if (permission == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
+    audio_type = content::MEDIA_DEVICE_AUDIO_CAPTURE;
+  else
+    video_type = content::MEDIA_DEVICE_VIDEO_CAPTURE;
+  content::MediaStreamRequest request(0, 0, 0, GetUrl(), false, request_type,
+                                      audio_id, video_id, audio_type,
+                                      video_type, false);
+
+  // Add fake devices, otherwise the request will auto-block.
+  MediaCaptureDevicesDispatcher::GetInstance()->SetTestAudioCaptureDevices(
+      content::MediaStreamDevices(
+          1, content::MediaStreamDevice(content::MEDIA_DEVICE_AUDIO_CAPTURE,
+                                        audio_id, "Fake Audio")));
+  MediaCaptureDevicesDispatcher::GetInstance()->SetTestVideoCaptureDevices(
+      content::MediaStreamDevices(
+          1, content::MediaStreamDevice(content::MEDIA_DEVICE_VIDEO_CAPTURE,
+                                        video_id, "Fake Video")));
+
+  auto response = [](const content::MediaStreamDevices& devices,
+                     content::MediaStreamRequestResult result,
+                     std::unique_ptr<content::MediaStreamUI> ui) {};
+  test::MediaStreamDevicesControllerTestApi::AddRequestToManager(
+      manager, web_contents, request, base::Bind(response));
+}
+
+PermissionRequest* PermissionDialogTest::MakePermissionRequest(
+    ContentSettingsType permission) {
+  bool user_gesture = true;
+  auto decided = [](bool, ContentSetting) {};
+  auto cleanup = [] {};  // Leave cleanup to test harness destructor.
+  owned_requests_.push_back(base::MakeUnique<PermissionRequestImpl>(
+      GetUrl(), permission, browser()->profile(), user_gesture,
+      base::Bind(decided), base::Bind(cleanup)));
+  return owned_requests_.back().get();
+}
+
+void PermissionDialogTest::ShowDialog(const std::string& name) {
+  constexpr const char* kMultipleName = "multiple";
+  // Permissions to request for a "multiple" request. Only types handled in
+  // PermissionRequestImpl::GetMessageTextFragment() are valid.
+  constexpr ContentSettingsType kMultipleRequests[] = {
+      CONTENT_SETTINGS_TYPE_GEOLOCATION, CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      CONTENT_SETTINGS_TYPE_MIDI_SYSEX};
+  constexpr struct {
+    const char* name;
+    ContentSettingsType type;
+  } kNameToType[] = {
+      {"flash", CONTENT_SETTINGS_TYPE_PLUGINS},
+      {"geolocation", CONTENT_SETTINGS_TYPE_GEOLOCATION},
+      {"protected_media", CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER},
+      {"notifications", CONTENT_SETTINGS_TYPE_NOTIFICATIONS},
+      {"mic", CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC},
+      {"camera", CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA},
+      {"protocol_handlers", CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS},
+      {"midi", CONTENT_SETTINGS_TYPE_MIDI_SYSEX},
+      {kMultipleName, CONTENT_SETTINGS_TYPE_DEFAULT}};
+  const auto* it = std::begin(kNameToType);
+  for (; it != std::end(kNameToType); ++it) {
+    if (name == it->name)
+      break;
+  }
+  if (it == std::end(kNameToType)) {
+    ADD_FAILURE() << "Unknown: " << name;
+    return;
+  }
+  PermissionRequestManager* manager = GetPermissionRequestManager();
+  switch (it->type) {
+    case CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS:
+      manager->AddRequest(MakeRegisterProtocolHandlerRequest());
+      break;
+    case CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS:
+      // TODO(tapted): Prompt for downloading multiple files.
+      break;
+    case CONTENT_SETTINGS_TYPE_DURABLE_STORAGE:
+      // TODO(tapted): Prompt for quota request.
+      break;
+    case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
+    case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+      AddMediaRequest(manager, it->type);
+      break;
+    // Regular permissions requests.
+    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
+    case CONTENT_SETTINGS_TYPE_PUSH_MESSAGING:  // Same as notifications.
+    case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
+    case CONTENT_SETTINGS_TYPE_GEOLOCATION:
+    case CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER:  // ChromeOS only.
+    case CONTENT_SETTINGS_TYPE_PPAPI_BROKER:
+    case CONTENT_SETTINGS_TYPE_PLUGINS:  // Flash.
+      manager->AddRequest(MakePermissionRequest(it->type));
+      break;
+    case CONTENT_SETTINGS_TYPE_DEFAULT:
+      EXPECT_EQ(kMultipleName, name);
+      for (auto request : kMultipleRequests)
+        manager->AddRequest(MakePermissionRequest(request));
+      break;
+    default:
+      ADD_FAILURE() << "Not a permission type, or one that doesn't prompt.";
+      return;
+  }
+  manager->DisplayPendingRequests();
+}
+
 // Requests before the load event should be bundled into one bubble.
 // http://crbug.com/512849 flaky
 IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
@@ -248,4 +446,55 @@
   EXPECT_EQ(1, bubble_factory()->total_request_count());
 }
 
+// Host wants to run flash.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_flash) {
+  RunDialog();
+}
+
+// Host wants to know your location.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_geolocation) {
+  RunDialog();
+}
+
+// Host wants to show notifications.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_notifications) {
+  RunDialog();
+}
+
+// Host wants to use your microphone.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_mic) {
+  RunDialog();
+}
+
+// Host wants to use your camera.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_camera) {
+  RunDialog();
+}
+
+// Host wants to open email links.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_protocol_handlers) {
+  RunDialog();
+}
+
+// Host wants to use your MIDI devices.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_midi) {
+  RunDialog();
+}
+
+// Shows a permissions bubble with multiple requests.
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest, InvokeDialog_multiple) {
+  RunDialog();
+}
+
+// CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER is ChromeOS only.
+#if defined(OS_CHROMEOS)
+#define MAYBE_InvokeDialog_protected_media InvokeDialog_protected_media
+#else
+#define MAYBE_InvokeDialog_protected_media DISABLED_InvokeDialog_protected_media
+#endif
+IN_PROC_BROWSER_TEST_F(PermissionDialogTest,
+                       MAYBE_InvokeDialog_protected_media) {
+  RunDialog();
+}
+
 }  // anonymous namespace
diff --git a/chrome/browser/permissions/permission_request_manager_unittest.cc b/chrome/browser/permissions/permission_request_manager_unittest.cc
index 14cfdbc1..a9d6cc1 100644
--- a/chrome/browser/permissions/permission_request_manager_unittest.cc
+++ b/chrome/browser/permissions/permission_request_manager_unittest.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/permissions/permission_request.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/permissions/permission_uma_util.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/permissions/permissions_browsertest.cc b/chrome/browser/permissions/permissions_browsertest.cc
index a95a94f..4adc2125 100644
--- a/chrome/browser/permissions/permissions_browsertest.cc
+++ b/chrome/browser/permissions/permissions_browsertest.cc
@@ -7,8 +7,8 @@
 #include "base/command_line.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/plugins/flash_permission_browsertest.cc b/chrome/browser/plugins/flash_permission_browsertest.cc
index 19b287ba..227bfc0 100644
--- a/chrome/browser/plugins/flash_permission_browsertest.cc
+++ b/chrome/browser/plugins/flash_permission_browsertest.cc
@@ -8,7 +8,7 @@
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/permissions/permissions_browsertest.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/variations/variations_switches.h"
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index af3de65..dea2c3a 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -81,6 +81,10 @@
 using content::BrowserContext;
 using content::BrowserThread;
 
+using EnforcementLevel = PrefHashFilter::EnforcementLevel;
+using PrefTrackingStrategy = PrefHashFilter::PrefTrackingStrategy;
+using ValueType = PrefHashFilter::ValueType;
+
 namespace {
 
 #if defined(OS_WIN)
@@ -97,158 +101,80 @@
 // See CleanupDeprecatedTrackedPreferences() in pref_hash_filter.cc to remove a
 // deprecated tracked preference.
 const PrefHashFilter::TrackedPreferenceMetadata kTrackedPrefs[] = {
-  {
-    0, prefs::kShowHomeButton,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    1, prefs::kHomePageIsNewTabPage,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    2, prefs::kHomePage,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    3, prefs::kRestoreOnStartup,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    4, prefs::kURLsToRestoreOnStartup,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {0, prefs::kShowHomeButton, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {1, prefs::kHomePageIsNewTabPage, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {2, prefs::kHomePage, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {3, prefs::kRestoreOnStartup, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {4, prefs::kURLsToRestoreOnStartup, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  {
-    5, extensions::pref_names::kExtensions,
-    PrefHashFilter::NO_ENFORCEMENT,
-    PrefHashFilter::TRACKING_STRATEGY_SPLIT,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {5, extensions::pref_names::kExtensions, EnforcementLevel::NO_ENFORCEMENT,
+     PrefTrackingStrategy::SPLIT, ValueType::IMPERSONAL},
 #endif
-  {
-    6, prefs::kGoogleServicesLastUsername,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_PERSONAL
-  },
-  {
-    7, prefs::kSearchProviderOverrides,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {6, prefs::kGoogleServicesLastUsername, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::PERSONAL},
+    {7, prefs::kSearchProviderOverrides, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 #if !defined(OS_ANDROID)
-  {
-    11, prefs::kPinnedTabs,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {11, prefs::kPinnedTabs, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 #endif
-  {
-    14, DefaultSearchManager::kDefaultSearchProviderDataPrefName,
-    PrefHashFilter::NO_ENFORCEMENT,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    // Protecting kPreferenceResetTime does two things:
-    //  1) It ensures this isn't accidently set by someone stomping the pref
-    //     file.
-    //  2) More importantly, it declares kPreferenceResetTime as a protected
-    //     pref which is required for it to be visible when queried via the
-    //     SegregatedPrefStore. This is because it's written directly in the
-    //     protected JsonPrefStore by that store's PrefHashFilter if there was
-    //     a reset in FilterOnLoad and SegregatedPrefStore will not look for it
-    //     in the protected JsonPrefStore unless it's declared as a protected
-    //     preference here.
-    15, user_prefs::kPreferenceResetTime,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  // kSyncRemainingRollbackTries is deprecated and will be removed a few
-  // releases after M50.
-  {
-    18, prefs::kSafeBrowsingIncidentsSent,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {14, DefaultSearchManager::kDefaultSearchProviderDataPrefName,
+     EnforcementLevel::NO_ENFORCEMENT, PrefTrackingStrategy::ATOMIC,
+     ValueType::IMPERSONAL},
+    {// Protecting kPreferenceResetTime does two things:
+     //  1) It ensures this isn't accidently set by someone stomping the pref
+     //     file.
+     //  2) More importantly, it declares kPreferenceResetTime as a protected
+     //     pref which is required for it to be visible when queried via the
+     //     SegregatedPrefStore. This is because it's written directly in the
+     //     protected JsonPrefStore by that store's PrefHashFilter if there was
+     //     a reset in FilterOnLoad and SegregatedPrefStore will not look for it
+     //     in the protected JsonPrefStore unless it's declared as a protected
+     //     preference here.
+     15, user_prefs::kPreferenceResetTime, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    // kSyncRemainingRollbackTries is deprecated and will be removed a few
+    // releases after M50.
+    {18, prefs::kSafeBrowsingIncidentsSent, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 #if defined(OS_WIN)
-  {
-    19, prefs::kSwReporterPromptVersion,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {19, prefs::kSwReporterPromptVersion, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 #endif
-  // This pref is deprecated and will be removed a few releases after M43.
-  // kGoogleServicesAccountId replaces it.
-  {
-    21, prefs::kGoogleServicesUsername,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_PERSONAL
-  },
+    // This pref is deprecated and will be removed a few releases after M43.
+    // kGoogleServicesAccountId replaces it.
+    {21, prefs::kGoogleServicesUsername, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::PERSONAL},
 #if defined(OS_WIN)
-  {
-    22, prefs::kSwReporterPromptSeed,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {22, prefs::kSwReporterPromptSeed, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 #endif
-  {
-    23, prefs::kGoogleServicesAccountId,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_PERSONAL
-  },
-  {
-    24, prefs::kGoogleServicesLastAccountId,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_PERSONAL
-  },
+    {23, prefs::kGoogleServicesAccountId, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::PERSONAL},
+    {24, prefs::kGoogleServicesLastAccountId, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::PERSONAL},
 #if defined(OS_WIN)
-  {
-    25, prefs::kSettingsResetPromptPromptWave,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    26, prefs::kSettingsResetPromptLastTriggeredForDefaultSearch,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    27, prefs::kSettingsResetPromptLastTriggeredForStartupUrls,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
-  {
-    28, prefs::kSettingsResetPromptLastTriggeredForHomepage,
-    PrefHashFilter::ENFORCE_ON_LOAD,
-    PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-    PrefHashFilter::VALUE_IMPERSONAL
-  },
+    {25, prefs::kSettingsResetPromptPromptWave,
+     EnforcementLevel::ENFORCE_ON_LOAD, PrefTrackingStrategy::ATOMIC,
+     ValueType::IMPERSONAL},
+    {26, prefs::kSettingsResetPromptLastTriggeredForDefaultSearch,
+     EnforcementLevel::ENFORCE_ON_LOAD, PrefTrackingStrategy::ATOMIC,
+     ValueType::IMPERSONAL},
+    {27, prefs::kSettingsResetPromptLastTriggeredForStartupUrls,
+     EnforcementLevel::ENFORCE_ON_LOAD, PrefTrackingStrategy::ATOMIC,
+     ValueType::IMPERSONAL},
+    {28, prefs::kSettingsResetPromptLastTriggeredForHomepage,
+     EnforcementLevel::ENFORCE_ON_LOAD, PrefTrackingStrategy::ATOMIC,
+     ValueType::IMPERSONAL},
 #endif  // defined(OS_WIN)
-  // See note at top, new items added here also need to be added to
-  // histograms.xml's TrackedPreference enum.
+
+    // See note at top, new items added here also need to be added to
+    // histograms.xml's TrackedPreference enum.
 };
 
 // One more than the last tracked preferences ID above.
@@ -342,20 +268,20 @@
 
     if (GROUP_NO_ENFORCEMENT == enforcement_group) {
       // Remove enforcement for all tracked preferences.
-      data.enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
+      data.enforcement_level = EnforcementLevel::NO_ENFORCEMENT;
     }
 
     if (enforcement_group >= GROUP_ENFORCE_ALWAYS_WITH_DSE &&
         data.name == DefaultSearchManager::kDefaultSearchProviderDataPrefName) {
       // Specifically enable default search settings enforcement.
-      data.enforcement_level = PrefHashFilter::ENFORCE_ON_LOAD;
+      data.enforcement_level = EnforcementLevel::ENFORCE_ON_LOAD;
     }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     if (enforcement_group >= GROUP_ENFORCE_ALWAYS_WITH_EXTENSIONS_AND_DSE &&
         data.name == extensions::pref_names::kExtensions) {
       // Specifically enable extension settings enforcement.
-      data.enforcement_level = PrefHashFilter::ENFORCE_ON_LOAD;
+      data.enforcement_level = EnforcementLevel::ENFORCE_ON_LOAD;
     }
 #endif
 
diff --git a/chrome/browser/prefs/profile_pref_store_manager.cc b/chrome/browser/prefs/profile_pref_store_manager.cc
index 891e7be..4310f9f1 100644
--- a/chrome/browser/prefs/profile_pref_store_manager.cc
+++ b/chrome/browser/prefs/profile_pref_store_manager.cc
@@ -120,7 +120,8 @@
            it = tracking_configuration_.begin();
        it != tracking_configuration_.end();
        ++it) {
-    if (it->enforcement_level > PrefHashFilter::NO_ENFORCEMENT) {
+    if (it->enforcement_level >
+        PrefHashFilter::EnforcementLevel::NO_ENFORCEMENT) {
       protected_configuration.push_back(*it);
       protected_pref_names.insert(it->name);
     } else {
@@ -147,11 +148,8 @@
   scoped_refptr<JsonPrefStore> unprotected_pref_store(new JsonPrefStore(
       profile_path_.Append(chrome::kPreferencesFilename), io_task_runner.get(),
       std::move(unprotected_pref_hash_filter)));
-  // TODO(gab): Remove kDeprecatedProtectedPreferencesFilename as an alternate
-  // file in M40+.
   scoped_refptr<JsonPrefStore> protected_pref_store(new JsonPrefStore(
       profile_path_.Append(chrome::kSecurePreferencesFilename),
-      profile_path_.Append(chrome::kProtectedPreferencesFilenameDeprecated),
       io_task_runner.get(), std::move(protected_pref_hash_filter)));
 
   SetupTrackedPreferencesMigration(
diff --git a/chrome/browser/prefs/profile_pref_store_manager_unittest.cc b/chrome/browser/prefs/profile_pref_store_manager_unittest.cc
index 60c4165..62d22de8 100644
--- a/chrome/browser/prefs/profile_pref_store_manager_unittest.cc
+++ b/chrome/browser/prefs/profile_pref_store_manager_unittest.cc
@@ -78,10 +78,10 @@
 const char kGoodbyeWorld[] = "GOODBYEWORLD";
 
 const PrefHashFilter::TrackedPreferenceMetadata kConfiguration[] = {
-    {0u, kTrackedAtomic, PrefHashFilter::NO_ENFORCEMENT,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
-    {1u, kProtectedAtomic, PrefHashFilter::ENFORCE_ON_LOAD,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC}};
+    {0u, kTrackedAtomic, PrefHashFilter::EnforcementLevel::NO_ENFORCEMENT,
+     PrefHashFilter::PrefTrackingStrategy::ATOMIC},
+    {1u, kProtectedAtomic, PrefHashFilter::EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefHashFilter::PrefTrackingStrategy::ATOMIC}};
 
 const size_t kExtraReportingId = 2u;
 const size_t kReportingIdCount = 3u;
@@ -106,7 +106,7 @@
     for (const PrefHashFilter::TrackedPreferenceMetadata* it = kConfiguration;
          it != kConfiguration + arraysize(kConfiguration);
          ++it) {
-      if (it->strategy == PrefHashFilter::TRACKING_STRATEGY_ATOMIC) {
+      if (it->strategy == PrefHashFilter::PrefTrackingStrategy::ATOMIC) {
         profile_pref_registry_->RegisterStringPref(it->name, std::string());
       } else {
         profile_pref_registry_->RegisterDictionaryPref(it->name);
@@ -119,11 +119,11 @@
     // SegregatedPrefStore. Only declare it after configured prefs have been
     // registered above for this test as kPreferenceResetTime is already
     // registered in ProfilePrefStoreManager::RegisterProfilePrefs.
-    PrefHashFilter::TrackedPreferenceMetadata pref_reset_time_config =
-        {configuration_.rbegin()->reporting_id + 1,
-         user_prefs::kPreferenceResetTime,
-         PrefHashFilter::ENFORCE_ON_LOAD,
-         PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
+    PrefHashFilter::TrackedPreferenceMetadata pref_reset_time_config = {
+        configuration_.rbegin()->reporting_id + 1,
+        user_prefs::kPreferenceResetTime,
+        PrefHashFilter::EnforcementLevel::ENFORCE_ON_LOAD,
+        PrefHashFilter::PrefTrackingStrategy::ATOMIC};
     configuration_.push_back(pref_reset_time_config);
 
     ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
@@ -358,8 +358,9 @@
 
   // Now update the configuration to protect it.
   PrefHashFilter::TrackedPreferenceMetadata new_protected = {
-      kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
-      PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
+      kExtraReportingId, kUnprotectedPref,
+      PrefHashFilter::EnforcementLevel::ENFORCE_ON_LOAD,
+      PrefHashFilter::PrefTrackingStrategy::ATOMIC};
   configuration_.push_back(new_protected);
   ReloadConfiguration();
 
@@ -391,7 +392,7 @@
            configuration_.begin();
        it != configuration_.end();
        ++it) {
-    it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
+    it->enforcement_level = PrefHashFilter::EnforcementLevel::NO_ENFORCEMENT;
   }
   ReloadConfiguration();
 
@@ -409,8 +410,9 @@
   // Now introduce protection, including the never-before tracked "new_pref".
   configuration_ = original_configuration;
   PrefHashFilter::TrackedPreferenceMetadata new_protected = {
-      kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
-      PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
+      kExtraReportingId, kUnprotectedPref,
+      PrefHashFilter::EnforcementLevel::ENFORCE_ON_LOAD,
+      PrefHashFilter::PrefTrackingStrategy::ATOMIC};
   configuration_.push_back(new_protected);
   ReloadConfiguration();
 
@@ -431,8 +433,9 @@
 
   // Now update the configuration to protect it.
   PrefHashFilter::TrackedPreferenceMetadata new_protected = {
-      kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
-      PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
+      kExtraReportingId, kUnprotectedPref,
+      PrefHashFilter::EnforcementLevel::ENFORCE_ON_LOAD,
+      PrefHashFilter::PrefTrackingStrategy::ATOMIC};
   configuration_.push_back(new_protected);
   seed_ = "new-seed-to-break-trust";
   ReloadConfiguration();
@@ -464,7 +467,7 @@
        it != configuration_.end();
        ++it) {
     if (it->name == kProtectedAtomic) {
-      it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
+      it->enforcement_level = PrefHashFilter::EnforcementLevel::NO_ENFORCEMENT;
       break;
     }
   }
diff --git a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
index 2744825..1b5fd90 100644
--- a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
+++ b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
@@ -221,7 +221,7 @@
     // Sanity check that old protected pref file is never present in modern
     // Chromes.
     EXPECT_FALSE(base::PathExists(
-        profile_dir.Append(chrome::kProtectedPreferencesFilenameDeprecated)));
+        profile_dir.Append(FILE_PATH_LITERAL("Protected Preferences"))));
 
     // Read the preferences from disk.
 
diff --git a/chrome/browser/printing/print_view_manager.cc b/chrome/browser/printing/print_view_manager.cc
index 5f6053ac..59eb8f24 100644
--- a/chrome/browser/printing/print_view_manager.cc
+++ b/chrome/browser/printing/print_view_manager.cc
@@ -136,7 +136,8 @@
 
 void PrintViewManager::PrintPreviewDone() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_NE(NOT_PREVIEWING, print_preview_state_);
+  if (print_preview_state_ == NOT_PREVIEWING)
+    return;
 
   if (print_preview_state_ == SCRIPTED_PREVIEW) {
     auto& map = g_scripted_print_preview_closure_map.Get();
@@ -161,7 +162,7 @@
 void PrintViewManager::RenderFrameDeleted(
     content::RenderFrameHost* render_frame_host) {
   if (render_frame_host == print_preview_rfh_)
-    print_preview_state_ = NOT_PREVIEWING;
+    PrintPreviewDone();
   PrintViewManagerBase::RenderFrameDeleted(render_frame_host);
 }
 
diff --git a/chrome/browser/printing/print_view_manager_unittest.cc b/chrome/browser/printing/print_view_manager_unittest.cc
new file mode 100644
index 0000000..aa8469d
--- /dev/null
+++ b/chrome/browser/printing/print_view_manager_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/printing/print_view_manager.h"
+#include "chrome/browser/printing/print_preview_test.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "content/public/test/test_renderer_host.h"
+
+namespace printing {
+
+using PrintViewManagerTest = PrintPreviewTest;
+
+TEST_F(PrintViewManagerTest, PrintSubFrameAndDestroy) {
+  chrome::NewTab(browser());
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  content::RenderFrameHost* sub_frame =
+      content::RenderFrameHostTester::For(web_contents->GetMainFrame())
+          ->AppendChild("child");
+
+  PrintViewManager* print_view_manager =
+      PrintViewManager::FromWebContents(web_contents);
+  ASSERT_TRUE(print_view_manager);
+  EXPECT_FALSE(print_view_manager->print_preview_rfh());
+
+  print_view_manager->PrintPreviewNow(sub_frame, false);
+  EXPECT_TRUE(print_view_manager->print_preview_rfh());
+
+  content::RenderFrameHostTester::For(sub_frame)->Detach();
+  EXPECT_FALSE(print_view_manager->print_preview_rfh());
+}
+
+}  // namespace printing
diff --git a/chrome/browser/resources/net_internals/source_entry.js b/chrome/browser/resources/net_internals/source_entry.js
index 25f0e81b..126750c 100644
--- a/chrome/browser/resources/net_internals/source_entry.js
+++ b/chrome/browser/resources/net_internals/source_entry.js
@@ -82,6 +82,7 @@
         case EventSourceType.SOCKET_STREAM:
         case EventSourceType.HTTP_STREAM_JOB:
         case EventSourceType.HTTP_STREAM_JOB_CONTROLLER:
+        case EventSourceType.BIDIRECTIONAL_STREAM:
           this.description_ = e.params.url;
           break;
         // TODO(davidben): Remove CONNECT_JOB after M57 is released.
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
index 2ef0b67..fe1e45a2 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
@@ -26,7 +26,7 @@
               font-size:[[computeMinimumFontSize_(
                   prefs.webkit.webprefs.minimum_font_size.value)]]px;
               font-family:
-                  '[[prefs.webkit.webprefs.fonts.standard.Zyyy.value]]';"
+                  '[[prefs.webkit.webprefs.fonts.standard.Zyyy.value]]';">
         <span>[[
             computeMinimumFontSize_(
                 prefs.webkit.webprefs.minimum_font_size.value)]]</span>
@@ -39,83 +39,87 @@
       </cr-slider>
     </div>
     <div class="settings-box">
-      <div class="start">
-        <h2>$i18n{standardFont}</h2>
-        <div class="list-frame">
-          <div class="list-item">
-            <settings-dropdown-menu class="start"
-                pref="{{prefs.webkit.webprefs.fonts.standard.Zyyy}}"
-                menu-options="[[fontOptions_]]">
-            </settings-dropdown-menu>
-          </div>
-          <div class="list-item underbar"
-              style="
-                  font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
-                  font-family:
-                      '[[prefs.webkit.webprefs.fonts.standard.Zyyy.value]]';"
-            <span>
-              [[prefs.webkit.webprefs.default_font_size.value]]:
-              $i18n{quickBrownFox}
-            </span>
-          </div>
-        </div>
-        <h2>$i18n{serifFont}</h2>
-        <div class="list-frame">
-          <div class="list-item">
-            <settings-dropdown-menu class="start"
-                pref="{{prefs.webkit.webprefs.fonts.serif.Zyyy}}"
-                menu-options="[[fontOptions_]]">
-            </settings-dropdown-menu>
-          </div>
-          <div class="list-item underbar"
-              style="
-                  font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
-                  font-family:
-                      '[[prefs.webkit.webprefs.fonts.serif.Zyyy.value]]';"
-            <span>
-              [[prefs.webkit.webprefs.default_font_size.value]]:
-              $i18n{quickBrownFox}
-            </span>
-          </div>
-        </div>
-        <h2>$i18n{sansSerifFont}</h2>
-        <div class="list-frame">
-          <div class="list-item">
-            <settings-dropdown-menu class="start"
-                pref="{{prefs.webkit.webprefs.fonts.sansserif.Zyyy}}"
-                menu-options="[[fontOptions_]]">
-            </settings-dropdown-menu>
-          </div>
-          <div class="list-item underbar"
-              style="
-                  font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
-                  font-family:
-                      '[[prefs.webkit.webprefs.fonts.sansserif.Zyyy.value]]';"
-            <span>
-              [[prefs.webkit.webprefs.default_font_size.value]]:
-              $i18n{quickBrownFox}
-            </span>
-          </div>
-        </div>
-        <h2>$i18n{fixedWidthFont}</h2>
-        <div class="list-frame">
-          <div class="list-item">
-            <settings-dropdown-menu class="start"
-                pref="{{prefs.webkit.webprefs.fonts.fixed.Zyyy}}"
-                menu-options="[[fontOptions_]]">
-            </settings-dropdown-menu>
-          </div>
-          <div class="list-item"
-              style="
-                  font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
-                  font-family:
-                      '[[prefs.webkit.webprefs.fonts.fixed.Zyyy.value]]';"
-            <span>
-              [[prefs.webkit.webprefs.default_font_size.value]]:
-              $i18n{quickBrownFox}
-            </span>
-          </div>
-        </div>
+      <h2>$i18n{standardFont}</h2>
+    </div>
+    <div class="list-frame">
+      <div class="list-item">
+        <settings-dropdown-menu class="start"
+            pref="{{prefs.webkit.webprefs.fonts.standard.Zyyy}}"
+            menu-options="[[fontOptions_]]">
+        </settings-dropdown-menu>
+      </div>
+      <div class="list-item underbar"
+          style="
+              font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
+              font-family:
+                  '[[prefs.webkit.webprefs.fonts.standard.Zyyy.value]]';">
+        <span>
+          [[prefs.webkit.webprefs.default_font_size.value]]:
+          $i18n{quickBrownFox}
+        </span>
+      </div>
+    </div>
+    <div class="settings-box">
+      <h2>$i18n{serifFont}</h2>
+    </div>
+    <div class="list-frame">
+      <div class="list-item">
+        <settings-dropdown-menu class="start"
+            pref="{{prefs.webkit.webprefs.fonts.serif.Zyyy}}"
+            menu-options="[[fontOptions_]]">
+        </settings-dropdown-menu>
+      </div>
+      <div class="list-item underbar"
+          style="
+              font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
+              font-family:
+                  '[[prefs.webkit.webprefs.fonts.serif.Zyyy.value]]';">
+        <span>
+          [[prefs.webkit.webprefs.default_font_size.value]]:
+          $i18n{quickBrownFox}
+        </span>
+      </div>
+    </div>
+    <div class="settings-box">
+      <h2>$i18n{sansSerifFont}</h2>
+    </div>
+    <div class="list-frame">
+      <div class="list-item">
+        <settings-dropdown-menu class="start"
+            pref="{{prefs.webkit.webprefs.fonts.sansserif.Zyyy}}"
+            menu-options="[[fontOptions_]]">
+        </settings-dropdown-menu>
+      </div>
+      <div class="list-item underbar"
+          style="
+              font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
+              font-family:
+                  '[[prefs.webkit.webprefs.fonts.sansserif.Zyyy.value]]';">
+        <span>
+          [[prefs.webkit.webprefs.default_font_size.value]]:
+          $i18n{quickBrownFox}
+        </span>
+      </div>
+    </div>
+    <div class="settings-box">
+      <h2>$i18n{fixedWidthFont}</h2>
+    </div>
+    <div class="list-frame">
+      <div class="list-item">
+        <settings-dropdown-menu class="start"
+            pref="{{prefs.webkit.webprefs.fonts.fixed.Zyyy}}"
+            menu-options="[[fontOptions_]]">
+        </settings-dropdown-menu>
+      </div>
+      <div class="list-item"
+          style="
+              font-size:[[prefs.webkit.webprefs.default_font_size.value]]px;
+              font-family:
+                  '[[prefs.webkit.webprefs.fonts.fixed.Zyyy.value]]';">
+        <span>
+          [[prefs.webkit.webprefs.default_font_size.value]]:
+          $i18n{quickBrownFox}
+        </span>
       </div>
     </div>
     <template is="dom-if" if="[[!isGuest_]]">
diff --git a/chrome/browser/resources/settings/compiled_resources2.gyp b/chrome/browser/resources/settings/compiled_resources2.gyp
index b8b4242c..252bb8a 100644
--- a/chrome/browser/resources/settings/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/compiled_resources2.gyp
@@ -48,6 +48,7 @@
       'target_name': 'route',
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
diff --git a/chrome/browser/resources/settings/route.html b/chrome/browser/resources/settings/route.html
index 102248e..1300e857 100644
--- a/chrome/browser/resources/settings/route.html
+++ b/chrome/browser/resources/settings/route.html
@@ -1,3 +1,4 @@
 <link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="i18n_setup.html">
 
 <script src="route.js"></script>
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 87a714c..7d02247ac 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -148,9 +148,12 @@
   r.CERTIFICATES = r.PRIVACY.createChild('/certificates');
 
   r.SITE_SETTINGS = r.PRIVACY.createChild('/content');
-  r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all');
-  r.SITE_SETTINGS_SITE_DETAILS =
-      r.SITE_SETTINGS_ALL.createChild('/content/siteDetails');
+
+  if (loadTimeData.getBoolean('enableSiteSettings')) {
+    r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all');
+    r.SITE_SETTINGS_SITE_DETAILS =
+        r.SITE_SETTINGS_ALL.createChild('/content/siteDetails');
+  }
 
   r.SITE_SETTINGS_HANDLERS = r.SITE_SETTINGS.createChild('/handlers');
 
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 268aff11..fa13cad 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -30,7 +30,7 @@
         };
         -webkit-user-select: none;
         background-color: var(--settings-background-color);
-        color: var(--paper-grey-800);
+        color: var(--paper-grey-900);
         display: flex;
         flex-direction: column;
         line-height: 154%; /* Apply 20px line-height to all texts by default. */
diff --git a/chrome/browser/resources/settings/site_settings/zoom_levels.html b/chrome/browser/resources/settings/site_settings/zoom_levels.html
index 2e2df3e0..f817afb0 100644
--- a/chrome/browser/resources/settings/site_settings/zoom_levels.html
+++ b/chrome/browser/resources/settings/site_settings/zoom_levels.html
@@ -16,6 +16,7 @@
 
       .zoom-label {
         -webkit-margin-end: 16px;
+        color: var(--paper-grey-600);
       }
 
       .empty-message {
diff --git a/chrome/browser/safe_browsing/permission_reporter_browsertest.cc b/chrome/browser/safe_browsing/permission_reporter_browsertest.cc
index 678c15c..00d89850 100644
--- a/chrome/browser/safe_browsing/permission_reporter_browsertest.cc
+++ b/chrome/browser/safe_browsing/permission_reporter_browsertest.cc
@@ -17,8 +17,8 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
 #include "chrome/common/safe_browsing/permission_report.pb.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc
index afdcd87c..57072b8 100644
--- a/chrome/browser/shell_integration_linux.cc
+++ b/chrome/browser/shell_integration_linux.cc
@@ -164,26 +164,27 @@
 #endif
 }
 
-#if !defined(OS_CHROMEOS)
-// If |check| is true, returns the output of "xdg-settings check {property}
-// *.desktop", otherwise returns the output of "xdg-settings get {property}",
-// where property is "default-web-browser" if |protocol| is empty or
-// "default-url-scheme-handler |protocol|" otherwise.  Returns "" if
-// xdg-settings fails for any reason.
-std::string GetXdgSettingsOutput(bool check,
-                                 const std::string& protocol,
-                                 base::Environment* env) {
+// If |protocol| is empty this function checks if Chrome is the default browser,
+// otherwise it checks if Chrome is the default handler application for
+// |protocol|.
+DefaultWebClientState GetIsDefaultWebClient(const std::string& protocol) {
+#if defined(OS_CHROMEOS)
+  return UNKNOWN_DEFAULT;
+#else
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+
   std::vector<std::string> argv;
   argv.push_back(kXdgSettings);
-  argv.push_back(check ? "check" : "get");
+  argv.push_back("check");
   if (protocol.empty()) {
     argv.push_back(kXdgSettingsDefaultBrowser);
   } else {
     argv.push_back(kXdgSettingsDefaultSchemeHandler);
     argv.push_back(protocol);
   }
-  if (check)
-    argv.push_back(shell_integration_linux::GetDesktopName(env));
+  argv.push_back(shell_integration_linux::GetDesktopName(env.get()));
 
   std::string reply;
   int success_code;
@@ -196,47 +197,15 @@
     }
   }
 
-  if (!ran_ok || success_code != EXIT_SUCCESS)
-    return std::string();
-
-  return reply;
-}
-#endif
-
-// If |protocol| is empty this function checks if Chrome is the default browser,
-// otherwise it checks if Chrome is the default handler application for
-// |protocol|.
-DefaultWebClientState GetDefaultWebClient(const std::string& protocol) {
-#if defined(OS_CHROMEOS)
-  return UNKNOWN_DEFAULT;
-#else
-  base::ThreadRestrictions::AssertIOAllowed();
-
-  std::unique_ptr<base::Environment> env(base::Environment::Create());
-  std::string xdg_is_default = GetXdgSettingsOutput(true, protocol, env.get());
-  if (base::StartsWith(xdg_is_default, "yes", base::CompareCase::SENSITIVE)) {
-    return IS_DEFAULT;
-  }
-  if (base::StartsWith(xdg_is_default, "no", base::CompareCase::SENSITIVE)) {
-    // An output of "no" does not necessarily mean Chrom[e,ium] is not the
-    // default.  According to the xdg-settings man page, this can happen when
-    // "only some of the underlying settings actually reflect that value". Don't
-    // return NOT_DEFAULT unless we're sure, or else an annoying "Chrome is not
-    // your default browser" banner will appear on every launch
-    // (https://crbug.com/578888).
-    if (base::StartsWith(GetXdgSettingsOutput(false, protocol, env.get()),
-                         shell_integration_linux::GetDesktopName(env.get()),
-                         base::CompareCase::SENSITIVE)) {
-      // This is the odd case where 'xdg-settings check' said that Chrome wasn't
-      // the default, but 'xdg-settings get' returned Chrome as the default.
-      return UNKNOWN_DEFAULT;
-    }
-    // xdg-settings says the default is non-Chrome, and is self-consistent.
-    return NOT_DEFAULT;
+  if (!ran_ok || success_code != EXIT_SUCCESS) {
+    // xdg-settings failed: we can't determine or set the default browser.
+    return UNKNOWN_DEFAULT;
   }
 
-  // xdg-settings failed: we can't determine or set the default browser.
-  return UNKNOWN_DEFAULT;
+  // Allow any reply that starts with "yes".
+  return base::StartsWith(reply, "yes", base::CompareCase::SENSITIVE)
+             ? IS_DEFAULT
+             : NOT_DEFAULT;
 #endif
 }
 
@@ -274,7 +243,7 @@
 }
 
 DefaultWebClientState GetDefaultBrowser() {
-  return GetDefaultWebClient(std::string());
+  return GetIsDefaultWebClient(std::string());
 }
 
 bool IsFirefoxDefaultBrowser() {
@@ -290,7 +259,7 @@
 }
 
 DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
-  return GetDefaultWebClient(protocol);
+  return GetIsDefaultWebClient(protocol);
 }
 
 }  // namespace shell_integration
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 138012a..c85832b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -127,6 +127,7 @@
     "passwords/password_manager_presenter.cc",
     "passwords/password_manager_presenter.h",
     "passwords/password_ui_view.h",
+    "permission_bubble/permission_prompt.h",
     "platform_keys_certificate_selector_chromeos.h",
     "prefs/prefs_tab_helper.cc",
     "prefs/prefs_tab_helper.h",
@@ -197,7 +198,6 @@
     "views/platform_keys_certificate_selector_chromeos.cc",
     "views/platform_keys_certificate_selector_chromeos.h",
     "web_contents_sizer.h",
-    "website_settings/permission_prompt.h",
     "webui/about_ui.cc",
     "webui/about_ui.h",
     "webui/bluetooth_internals/bluetooth_internals_ui.cc",
@@ -759,8 +759,6 @@
       "layout_constants.h",
       "location_bar/location_bar.cc",
       "location_bar/location_bar.h",
-      "location_bar/location_bar_util.cc",
-      "location_bar/location_bar_util.h",
       "native_window_tracker.h",
       "omnibox/alternate_nav_infobar_delegate.cc",
       "omnibox/alternate_nav_infobar_delegate.h",
@@ -795,6 +793,8 @@
       "pdf/adobe_reader_info_win.h",
       "pdf/chrome_pdf_web_contents_helper_client.cc",
       "pdf/chrome_pdf_web_contents_helper_client.h",
+      "permission_bubble/chooser_bubble_delegate.cc",
+      "permission_bubble/chooser_bubble_delegate.h",
       "sad_tab.cc",
       "sad_tab.h",
       "sad_tab_helper.cc",
@@ -899,8 +899,6 @@
       "unload_controller_web_contents_delegate.h",
       "user_manager.cc",
       "user_manager.h",
-      "website_settings/chooser_bubble_delegate.cc",
-      "website_settings/chooser_bubble_delegate.h",
       "webui/app_launcher_login_handler.cc",
       "webui/app_launcher_login_handler.h",
       "webui/bookmarks_ui.cc",
@@ -1547,6 +1545,8 @@
       "views/payments/validation_delegate.h",
       "views/payments/view_stack.cc",
       "views/payments/view_stack.h",
+      "views/permission_bubble/permission_prompt_impl.cc",
+      "views/permission_bubble/permission_prompt_impl.h",
       "views/subtle_notification_view.cc",
       "views/subtle_notification_view.h",
       "views/sync/bubble_sync_promo_view.cc",
@@ -1559,8 +1559,6 @@
       "views/toolbar/toolbar_actions_bar_bubble_views.h",
       "views/update_recommended_message_box.cc",
       "views/update_recommended_message_box.h",
-      "views/website_settings/permission_prompt_impl.cc",
-      "views/website_settings/permission_prompt_impl.h",
     ]
     deps += [
       "//chrome/browser/ui/views",
@@ -1815,6 +1813,9 @@
         "views/passwords/manage_passwords_bubble_view.h",
         "views/passwords/manage_passwords_icon_views.cc",
         "views/passwords/manage_passwords_icon_views.h",
+        "views/permission_bubble/chooser_bubble_ui_view.cc",
+        "views/permission_bubble/chooser_bubble_ui_view.h",
+        "views/permission_bubble/permission_prompt_impl_views.cc",
         "views/process_singleton_dialog_linux.cc",
         "views/profiles/profile_indicator_icon.cc",
         "views/profiles/profile_indicator_icon.h",
@@ -1898,9 +1899,6 @@
         "views/validation_message_bubble_view.h",
         "views/webshare/webshare_target_picker_view.cc",
         "views/webshare/webshare_target_picker_view.h",
-        "views/website_settings/chooser_bubble_ui_view.cc",
-        "views/website_settings/chooser_bubble_ui_view.h",
-        "views/website_settings/permission_prompt_impl_views.cc",
       ]
 
       if (use_aura) {
@@ -2884,6 +2882,13 @@
         "cocoa/passwords/signin_promo_view_controller.mm",
         "cocoa/passwords/update_pending_password_view_controller.h",
         "cocoa/passwords/update_pending_password_view_controller.mm",
+        "cocoa/permission_bubble/chooser_bubble_ui_cocoa.h",
+        "cocoa/permission_bubble/chooser_bubble_ui_cocoa.mm",
+        "cocoa/permission_bubble/permission_bubble_cocoa.h",
+        "cocoa/permission_bubble/permission_bubble_cocoa.mm",
+        "cocoa/permission_bubble/permission_bubble_controller.h",
+        "cocoa/permission_bubble/permission_bubble_controller.mm",
+        "cocoa/permission_bubble/permission_prompt_impl_views_mac.mm",
         "cocoa/profiles/avatar_base_controller.h",
         "cocoa/profiles/avatar_base_controller.mm",
         "cocoa/profiles/avatar_button.h",
@@ -2988,13 +2993,6 @@
         "cocoa/view_resizer.h",
         "cocoa/web_contents_modal_dialog_manager_views_mac.h",
         "cocoa/web_contents_modal_dialog_manager_views_mac.mm",
-        "cocoa/website_settings/chooser_bubble_ui_cocoa.h",
-        "cocoa/website_settings/chooser_bubble_ui_cocoa.mm",
-        "cocoa/website_settings/permission_bubble_cocoa.h",
-        "cocoa/website_settings/permission_bubble_cocoa.mm",
-        "cocoa/website_settings/permission_bubble_controller.h",
-        "cocoa/website_settings/permission_bubble_controller.mm",
-        "cocoa/website_settings/permission_prompt_impl_views_mac.mm",
         "cocoa/window_size_autosaver.h",
         "cocoa/window_size_autosaver.mm",
 
diff --git a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc b/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
index 66c7b91f..4b709f1 100644
--- a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
+++ b/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
@@ -6,7 +6,7 @@
 
 #include <stddef.h>
 #include <utility>
-
+#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -49,6 +49,9 @@
   }
 
   infobar_service->AddInfoBar(std::move(infobar));
+
+  content_settings::RecordPopupsAction(
+      content_settings::POPUPS_ACTION_DISPLAYED_INFOBAR_ON_MOBILE);
 }
 
 PopupBlockedInfoBarDelegate::~PopupBlockedInfoBarDelegate() {
@@ -116,5 +119,7 @@
       it != blocked_popups.end(); ++it)
     popup_blocker_helper->ShowBlockedPopup(it->first);
 
+  content_settings::RecordPopupsAction(
+      content_settings::POPUPS_ACTION_CLICKED_ALWAYS_SHOW_ON_MOBILE);
   return true;
 }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.cc b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
index f888755..dfd61ec 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
@@ -172,6 +172,7 @@
   // ImageDecoder::ImageRequest
   void OnImageDecoded(const SkBitmap& bitmap) override;
   void OnDecodeImageFailed() override;
+
  private:
   base::WeakPtr<ArcAppIcon> host_;
   int dimension_;
@@ -206,6 +207,8 @@
     VLOG(2) << "Decoded ARC icon has unexpected dimension "
             << bitmap.width() << "x" << bitmap.height() << ". Expected "
             << expected_dim << "x" << ".";
+
+    host_->MaybeRequestIcon(scale_factor_);
     host_->DiscardDecodeRequest(this);
     return;
   }
@@ -214,7 +217,6 @@
   image_skia.AddRepresentation(gfx::ImageSkiaRep(
       bitmap,
       ui::GetScaleForScaleFactor(scale_factor_)));
-
   host_->Update(&image_skia);
   host_->DiscardDecodeRequest(this);
 }
@@ -225,6 +227,7 @@
   if (!host_)
     return;
 
+  host_->MaybeRequestIcon(scale_factor_);
   host_->DiscardDecodeRequest(this);
 }
 
@@ -270,7 +273,7 @@
       base::Bind(&ArcAppIcon::OnIconRead, weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ArcAppIcon::RequestIcon(ui::ScaleFactor scale_factor) {
+void ArcAppIcon::MaybeRequestIcon(ui::ScaleFactor scale_factor) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   ArcAppListPrefs* prefs = ArcAppListPrefs::Get(context_);
   DCHECK(prefs);
@@ -278,7 +281,7 @@
   // ArcAppListPrefs notifies ArcAppModelBuilder via Observer when icon is ready
   // and ArcAppModelBuilder refreshes the icon of the corresponding item by
   // calling LoadScaleFactor.
-  prefs->RequestIcon(app_id_, scale_factor);
+  prefs->MaybeRequestIcon(app_id_, scale_factor);
 }
 
 // static
@@ -293,24 +296,29 @@
     path_to_read = path;
   } else {
     if (default_app_path.empty() || !base::PathExists(default_app_path)) {
-      return base::WrapUnique(new ArcAppIcon::ReadResult(
-          false, true, scale_factor, std::string()));
+      return base::MakeUnique<ArcAppIcon::ReadResult>(false, true, scale_factor,
+                                                      std::string());
     }
     path_to_read = default_app_path;
   }
 
+  bool request_to_install = path_to_read != path;
+
   // Read the file from disk.
   std::string unsafe_icon_data;
-  if (!base::ReadFileToString(path_to_read, &unsafe_icon_data)) {
+  if (!base::ReadFileToString(path_to_read, &unsafe_icon_data) ||
+      unsafe_icon_data.empty()) {
     VLOG(2) << "Failed to read an ARC icon from file " << path.MaybeAsASCII();
-    return base::WrapUnique(new ArcAppIcon::ReadResult(
-        true, path_to_read != path, scale_factor, std::string()));
+
+    // If |unsafe_icon_data| is empty typically means we have a file corruption
+    // on cached icon file. Send request to re install the icon.
+    request_to_install |= unsafe_icon_data.empty();
+    return base::MakeUnique<ArcAppIcon::ReadResult>(
+        true, request_to_install, scale_factor, std::string());
   }
 
-  return base::WrapUnique(new ArcAppIcon::ReadResult(false,
-                                                     path_to_read != path,
-                                                     scale_factor,
-                                                     unsafe_icon_data));
+  return base::MakeUnique<ArcAppIcon::ReadResult>(
+      false, request_to_install, scale_factor, unsafe_icon_data);
 }
 
 void ArcAppIcon::OnIconRead(
@@ -318,7 +326,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (read_result->request_to_install)
-    RequestIcon(read_result->scale_factor);
+    MaybeRequestIcon(read_result->scale_factor);
 
   if (!read_result->unsafe_icon_data.empty()) {
     decode_requests_.push_back(base::MakeUnique<DecodeRequest>(
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.h b/chrome/browser/ui/app_list/arc/arc_app_icon.h
index 69e2a1b..2d4c618 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.h
@@ -82,7 +82,7 @@
   class DecodeRequest;
   struct ReadResult;
 
-  void RequestIcon(ui::ScaleFactor scale_factor);
+  void MaybeRequestIcon(ui::ScaleFactor scale_factor);
   static std::unique_ptr<ArcAppIcon::ReadResult> ReadOnFileThread(
       ui::ScaleFactor scale_factor,
       const base::FilePath& path,
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 7a5f456..294e5ed 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -317,6 +317,23 @@
   return ToIconPath(GetAppPath(app_id), scale_factor);
 }
 
+bool ArcAppListPrefs::IsIconRequestRecorded(
+    const std::string& app_id,
+    ui::ScaleFactor scale_factor) const {
+  const auto iter = request_icon_recorded_.find(app_id);
+  if (iter == request_icon_recorded_.end())
+    return false;
+  return iter->second & (1 << scale_factor);
+}
+
+void ArcAppListPrefs::MaybeRemoveIconRequestRecord(const std::string& app_id) {
+  request_icon_recorded_.erase(app_id);
+}
+
+void ArcAppListPrefs::ClearIconRequestRecord() {
+  request_icon_recorded_.clear();
+}
+
 void ArcAppListPrefs::RequestIcon(const std::string& app_id,
                                   ui::ScaleFactor scale_factor) {
   // ArcSessionManager can be terminated during test tear down, before callback
@@ -330,12 +347,14 @@
     return;
   }
 
-  // In case app is not ready, defer this request.
-  if (!ready_apps_.count(app_id)) {
-    request_icon_deferred_[app_id] =
-        request_icon_deferred_[app_id] | 1 << scale_factor;
+  // In case app is not ready, recorded request will be send to ARC when app
+  // becomes ready.
+  // This record will prevent ArcAppIcon from resending request to ARC for app
+  // icon when icon file decode failure is suffered in case app sends bad icon.
+  request_icon_recorded_[app_id] |= (1 << scale_factor);
+
+  if (!ready_apps_.count(app_id))
     return;
-  }
 
   if (!app_instance_holder_->has_instance()) {
     // AppInstance should be ready since we have app_id in ready_apps_. This
@@ -371,6 +390,12 @@
   }
 }
 
+void ArcAppListPrefs::MaybeRequestIcon(const std::string& app_id,
+                                       ui::ScaleFactor scale_factor) {
+  if (!IsIconRequestRecorded(app_id, scale_factor))
+    RequestIcon(app_id, scale_factor);
+}
+
 void ArcAppListPrefs::SetNotificationsEnabled(const std::string& app_id,
                                               bool enabled) {
   if (!IsRegistered(app_id)) {
@@ -739,6 +764,7 @@
   default_apps_installations_.clear();
   detect_default_app_availability_timeout_.Stop();
   binding_.Close();
+  ClearIconRequestRecord();
 
   if (sync_service_) {
     sync_service_->StopSyncing(syncer::ARC_PACKAGE);
@@ -837,14 +863,13 @@
   }
 
   if (app_ready) {
-    auto deferred_icons = request_icon_deferred_.find(app_id);
-    if (deferred_icons != request_icon_deferred_.end()) {
+    auto pending_icons = request_icon_recorded_.find(app_id);
+    if (pending_icons != request_icon_recorded_.end()) {
       for (uint32_t i = ui::SCALE_FACTOR_100P; i < ui::NUM_SCALE_FACTORS; ++i) {
-        if (deferred_icons->second & (1 << i)) {
+        if (pending_icons->second & (1 << i)) {
           RequestIcon(app_id, static_cast<ui::ScaleFactor>(i));
         }
       }
-      request_icon_deferred_.erase(deferred_icons);
     }
 
     bool deferred_notifications_enabled;
@@ -862,6 +887,8 @@
     arc::RemoveCachedIcon(app_info->icon_resource_id);
   }
 
+  MaybeRemoveIconRequestRecord(app_id);
+
   // From now, app is not available.
   ready_apps_.erase(app_id);
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index cc30435..0529948 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -199,11 +199,9 @@
   // Sets last launched time for the requested app.
   void SetLastLaunchTime(const std::string& app_id, const base::Time& time);
 
-  // Requests to load an app icon for specific scale factor. If the app or ARC
-  // bridge service is not ready, then defer this request until the app gets
-  // available. Once new icon is installed notifies an observer
-  // OnAppIconUpdated.
-  void RequestIcon(const std::string& app_id, ui::ScaleFactor scale_factor);
+  // Calls RequestIcon if no request is recorded.
+  void MaybeRequestIcon(const std::string& app_id,
+                        ui::ScaleFactor scale_factor);
 
   // Sets notification enabled flag for given value. If the app or ARC bridge
   // service is not ready, then defer this request until the app gets
@@ -249,6 +247,7 @@
 
  private:
   friend class ChromeLauncherControllerImplTest;
+  friend class ArcAppModelBuilderTest;
 
   // See the Create methods.
   ArcAppListPrefs(
@@ -343,6 +342,12 @@
                        ui::ScaleFactor scale_factor,
                        bool install_succeed);
 
+  // Requests to load an app icon for specific scale factor. If the app or ARC
+  // bridge service is not ready, then defer this request until the app gets
+  // available. Once new icon is installed notifies an observer
+  // OnAppIconUpdated.
+  void RequestIcon(const std::string& app_id, ui::ScaleFactor scale_factor);
+
   // This checks if app is not registered yet and in this case creates
   // non-launchable app entry.
   void MaybeAddNonLaunchableApp(const base::Optional<std::string>& name,
@@ -369,6 +374,14 @@
   // some default app is not available yet.
   void MaybeSetDefaultAppLoadingTimeout();
 
+  bool IsIconRequestRecorded(const std::string& app_id,
+                             ui::ScaleFactor scale_factor) const;
+
+  // Remove the IconRequestRecord associated with app_id.
+  void MaybeRemoveIconRequestRecord(const std::string& app_id);
+
+  void ClearIconRequestRecord();
+
   Profile* const profile_;
 
   // Owned by the BrowserContext.
@@ -387,10 +400,15 @@
   std::unordered_set<std::string> tracked_apps_;
   // Contains number of ARC packages that are currently installing.
   int installing_packages_count_ = 0;
-  // Keeps deferred icon load requests. Each app may contain several requests
-  // for different scale factor. Scale factor is defined by specific bit
-  // position.
-  std::map<std::string, uint32_t> request_icon_deferred_;
+  // Keeps record for icon request. Each app may contain several requests for
+  // different scale factor. Scale factor is defined by specific bit position.
+  // Mainly two usages:
+  // 1. Keeps deferred icon load requests when apps are not ready. Request will
+  // be sent when apps becomes ready.
+  // 2. Keeps record of icon request sent to Android. In each user session, one
+  // request per app per scale_factor is allowed once.
+  // When ARC is disabled or the app is uninstalled, the record will be erased.
+  std::map<std::string, uint32_t> request_icon_recorded_;
   // True if this preference has been initialized once.
   bool is_initialized_ = false;
   // True if apps were restored.
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 2f66084..4b35b2d 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -373,6 +373,14 @@
     }
   }
 
+  // Removes icon request record and allowd re-sending icon request.
+  void MaybeRemoveIconRequestRecord(const std::string& app_id) {
+    ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
+    ASSERT_NE(nullptr, prefs);
+
+    prefs->MaybeRemoveIconRequestRecord(app_id);
+  }
+
   void AddPackage(const arc::mojom::ArcPackageInfo& package) {
     arc_test_.AddPackage(package);
   }
@@ -938,8 +946,6 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(base::PathExists(app_path));
 
-  // Request icon, this will create app folder.
-  prefs->RequestIcon(app_id, scale_factor);
   // Now send generated icon for the app.
   std::string png_data;
   EXPECT_TRUE(app_instance()->GenerateAndSendIcon(
@@ -1092,7 +1098,9 @@
   content::BrowserThread::GetBlockingPool()->FlushForTesting();
   base::RunLoop().RunUntilIdle();
 
-  // Store number of requests generated during the App List item creation.
+  // Store number of requests generated during the App List item creation. Same
+  // request will not be re-sent without clearing the request record in
+  // ArcAppListPrefs.
   const size_t initial_icon_request_count =
       app_instance()->icon_requests().size();
 
@@ -1130,6 +1138,9 @@
   }
 
   // Fallback when shortcut is not found for shelf group id, use app id instead.
+  // Remove the IconRequestRecord for |app_id| to observe the icon request for
+  // |app_id| is re-sent.
+  MaybeRemoveIconRequestRecord(app_id);
   icon_loader.FetchImage(id_shortcut_absent);
   EXPECT_EQ(2UL, delegate.update_image_cnt());
   content::BrowserThread::GetBlockingPool()->FlushForTesting();
@@ -1145,6 +1156,64 @@
   }
 }
 
+// If the cached icon file is corrupted, we expect send request to ARC for a new
+// icon.
+TEST_P(ArcAppModelBuilderTest, IconLoaderWithBadIcon) {
+  const arc::mojom::AppInfo& app = fake_apps()[0];
+  const std::string app_id = ArcAppTest::GetAppId(app);
+
+  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
+  ASSERT_NE(nullptr, prefs);
+
+  app_instance()->RefreshAppList();
+  app_instance()->SendRefreshAppList(std::vector<arc::mojom::AppInfo>(
+      fake_apps().begin(), fake_apps().begin() + 1));
+  content::BrowserThread::GetBlockingPool()->FlushForTesting();
+  base::RunLoop().RunUntilIdle();
+
+  // Store number of requests generated during the App List item creation. Same
+  // request will not be re-sent without clearing the request record in
+  // ArcAppListPrefs.
+  const size_t initial_icon_request_count =
+      app_instance()->icon_requests().size();
+
+  FakeAppIconLoaderDelegate delegate;
+  ArcAppIconLoader icon_loader(profile(), app_list::kListIconSize, &delegate);
+  icon_loader.FetchImage(app_id);
+
+  content::BrowserThread::GetBlockingPool()->FlushForTesting();
+  base::RunLoop().RunUntilIdle();
+  // Although icon file is still missing, expect no new request sent to ARC as
+  // them are recorded in IconRequestRecord in ArcAppListPrefs.
+  EXPECT_EQ(app_instance()->icon_requests().size(), initial_icon_request_count);
+  // Validate default image.
+  ValidateIcon(delegate.image());
+
+  MaybeRemoveIconRequestRecord(app_id);
+
+  // Install Bad image.
+  const std::vector<ui::ScaleFactor>& scale_factors =
+      ui::GetSupportedScaleFactors();
+  ArcAppItem* app_item = FindArcItem(app_id);
+  for (auto& scale_factor : scale_factors) {
+    app_instance()->GenerateAndSendBadIcon(
+        app, static_cast<arc::mojom::ScaleFactor>(scale_factor));
+    const float scale = ui::GetScaleForScaleFactor(scale_factor);
+    // Force the icon to be loaded.
+    app_item->icon().GetRepresentation(scale);
+    WaitForIconReady(prefs, app_id, scale_factor);
+  }
+  // After clear request record related to |app_id|, when bad icon is installed,
+  // decoding failure will trigger re-sending new icon request to ARC.
+  EXPECT_TRUE(app_instance()->icon_requests().size() >
+              initial_icon_request_count);
+  for (size_t i = initial_icon_request_count;
+       i < app_instance()->icon_requests().size(); ++i) {
+    const auto& request = app_instance()->icon_requests()[i];
+    EXPECT_TRUE(request->IsForApp(app));
+  }
+}
+
 // TODO(crbug.com/628425) -- reenable once this test is less flaky.
 TEST_P(ArcAppModelBuilderTest, DISABLED_IconLoader) {
   const arc::mojom::AppInfo& app = fake_apps()[0];
diff --git a/chrome/browser/ui/app_list/start_page_service.cc b/chrome/browser/ui/app_list/start_page_service.cc
index db48a06..a6c4bd1 100644
--- a/chrome/browser/ui/app_list/start_page_service.cc
+++ b/chrome/browser/ui/app_list/start_page_service.cc
@@ -159,21 +159,6 @@
   explicit StartPageWebContentsDelegate(Profile* profile) : profile_(profile) {}
   ~StartPageWebContentsDelegate() override {}
 
-  void RequestMediaAccessPermission(
-      content::WebContents* web_contents,
-      const content::MediaStreamRequest& request,
-      const content::MediaResponseCallback& callback) override {
-    MediaStreamDevicesController::RequestPermissions(web_contents, request,
-                                                     callback);
-  }
-
-  bool CheckMediaAccessPermission(content::WebContents* web_contents,
-                                  const GURL& security_origin,
-                                  content::MediaStreamType type) override {
-    return MediaCaptureDevicesDispatcher::GetInstance()
-        ->CheckMediaAccessPermission(web_contents, security_origin, type);
-  }
-
   void AddNewContents(content::WebContents* source,
                       content::WebContents* new_contents,
                       WindowOpenDisposition disposition,
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index c2a9f4f..7773489 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -123,6 +123,7 @@
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
+#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
 #include "chrome/browser/ui/search/search_delegate.h"
 #include "chrome/browser/ui/search/search_model.h"
 #include "chrome/browser/ui/search/search_tab_helper.h"
@@ -139,7 +140,6 @@
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/unload_controller.h"
 #include "chrome/browser/ui/validation_message_bubble.h"
-#include "chrome/browser/ui/website_settings/chooser_bubble_delegate.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/browser/ui/window_sizer/window_sizer.h"
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 65249707..4d7ac82 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -64,6 +64,7 @@
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #include "chrome/browser/ui/cocoa/l10n_util.h"
 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
+#include "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_button_controller.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_icon_controller.h"
@@ -75,7 +76,6 @@
 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
 #import "chrome/browser/ui/cocoa/translate/translate_bubble_controller.h"
-#include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index c7ae5d7..4936bae1 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -32,12 +32,13 @@
 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
 #import "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
-#include "chrome/browser/ui/cocoa/fullscreen_low_power_coordinator.h"
 #import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h"
+#include "chrome/browser/ui/cocoa/fullscreen_low_power_coordinator.h"
 #import "chrome/browser/ui/cocoa/fullscreen_window.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_button_controller.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_icon_controller.h"
 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
@@ -45,7 +46,6 @@
 #import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm b/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm
index 71b9e2d..424c6e1 100644
--- a/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.mm
@@ -4,14 +4,36 @@
 
 #import "chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.h"
 
+#include <stddef.h>
+
+#include "base/i18n/rtl.h"
+#include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #import "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
-#include "chrome/browser/ui/location_bar/location_bar_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/text_elider.h"
+
+namespace {
+
+// Build a short string to use in keyword-search when the field isn't very big.
+base::string16 CalculateMinString(const base::string16& description) {
+  // Chop at the first '.' or whitespace.
+  const size_t chop_index = description.find_first_of(base::kWhitespaceUTF16 +
+                                                      base::ASCIIToUTF16("."));
+  base::string16 min_string(
+      (chop_index == base::string16::npos)
+          ? gfx::TruncateString(description, 3, gfx::WORD_BREAK)
+          : description.substr(0, chop_index));
+  base::i18n::AdjustStringForLocaleDirection(&min_string);
+  return min_string;
+}
+
+}  // namespace
 
 SelectedKeywordDecoration::SelectedKeywordDecoration() {
   // Note: the unit test
@@ -53,8 +75,7 @@
 
 void SelectedKeywordDecoration::SetKeyword(const base::string16& short_name,
                                            bool is_extension_keyword) {
-  const base::string16 min_name(
-      location_bar_util::CalculateMinString(short_name));
+  const base::string16 min_name(CalculateMinString(short_name));
   const int keyword_text_id = IDS_OMNIBOX_KEYWORD_TEXT_MD;
 
   NSString* full_string =
diff --git a/chrome/browser/ui/cocoa/notifications/BUILD.gn b/chrome/browser/ui/cocoa/notifications/BUILD.gn
index 4fb22a7..4ea320f 100644
--- a/chrome/browser/ui/cocoa/notifications/BUILD.gn
+++ b/chrome/browser/ui/cocoa/notifications/BUILD.gn
@@ -25,6 +25,7 @@
   deps = [
     ":common",
     "//base:base",
+    "//third_party/crashpad/crashpad/client",
   ]
 
   libs = [
@@ -53,6 +54,8 @@
     "notification_delivery.h",
     "notification_response_builder_mac.h",
     "notification_response_builder_mac.mm",
+    "xpc_mach_port.h",
+    "xpc_mach_port.mm",
   ]
 
   deps = [
diff --git a/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm b/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm
index d81f2b6..528e198 100644
--- a/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm
+++ b/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm
@@ -8,11 +8,17 @@
 #import "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
 #import "chrome/browser/ui/cocoa/notifications/xpc_transaction_handler.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
 
 @class NSUserNotificationCenter;
 
 @implementation AlertNotificationService {
   XPCTransactionHandler* transactionHandler_;
+
+  // Ensures that the XPC service has been configured for crash reporting.
+  // Other messages should not be sent to a new instance of the service
+  // before -setMachExceptionPort: is called.
+  BOOL didSetExceptionPort_;
 }
 
 - (instancetype)initWithTransactionHandler:(XPCTransactionHandler*)handler {
@@ -22,7 +28,21 @@
   return self;
 }
 
+- (void)setMachExceptionPort:(CrXPCMachPort*)port {
+  base::mac::ScopedMachSendRight sendRight([port takeRight]);
+  if (!sendRight.is_valid()) {
+    NOTREACHED();
+    return;
+  }
+
+  crashpad::CrashpadClient client;
+  didSetExceptionPort_ = client.SetHandlerMachPort(std::move(sendRight));
+  DCHECK(didSetExceptionPort_);
+}
+
 - (void)deliverNotification:(NSDictionary*)notificationData {
+  DCHECK(didSetExceptionPort_);
+
   base::scoped_nsobject<NotificationBuilder> builder(
       [[NotificationBuilder alloc] initWithDictionary:notificationData]);
 
@@ -34,6 +54,8 @@
 
 - (void)closeNotificationWithId:(NSString*)notificationId
                   withProfileId:(NSString*)profileId {
+  DCHECK(didSetExceptionPort_);
+
   NSUserNotificationCenter* notificationCenter =
       [NSUserNotificationCenter defaultUserNotificationCenter];
   for (NSUserNotification* candidate in
@@ -54,6 +76,8 @@
 }
 
 - (void)closeAllNotifications {
+  DCHECK(didSetExceptionPort_);
+
   [[NSUserNotificationCenter defaultUserNotificationCenter]
       removeAllDeliveredNotifications];
   [transactionHandler_ closeTransactionIfNeeded];
diff --git a/chrome/browser/ui/cocoa/notifications/notification_delivery.h b/chrome/browser/ui/cocoa/notifications/notification_delivery.h
index 8670956..b575bb38 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_delivery.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_delivery.h
@@ -7,12 +7,17 @@
 
 #import <Foundation/Foundation.h>
 
+#import "chrome/browser/ui/cocoa/notifications/xpc_mach_port.h"
+
 // Collection of protocols used for XPC communication between chrome
 // and the alert notification xpc service.
 
 // Protocol for the XPC notification service.
 @protocol NotificationDelivery
 
+// Sets the Mach exception handler port to use for the XPCService.
+- (void)setMachExceptionPort:(CrXPCMachPort*)port;
+
 // |notificationData| is generated using a NofiticationBuilder object.
 - (void)deliverNotification:(NSDictionary*)notificationData;
 
@@ -22,6 +27,7 @@
 
 // Closes all the alerts being displayed.
 - (void)closeAllNotifications;
+
 @end
 
 // Response protocol for the XPC notification service to notify Chrome of
diff --git a/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm b/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm
index 1a648adf..2a7d182 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <AppKit/AppKit.h>
-
 #import "chrome/browser/ui/cocoa/notifications/notification_service_delegate.h"
 
+#import <AppKit/AppKit.h>
+
 #include "base/mac/scoped_nsobject.h"
 #import "chrome/browser/ui/cocoa/notifications/alert_notification_service.h"
 #import "chrome/browser/ui/cocoa/notifications/notification_delivery.h"
@@ -57,7 +57,7 @@
   return YES;
 }
 
-// NSUserNotification center delegate
+// NSUserNotificationCenterDelegate:
 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
        didActivateNotification:(NSUserNotification*)notification {
   NSDictionary* response =
@@ -65,7 +65,7 @@
   [[connection_ remoteObjectProxy] notificationClick:response];
 }
 
-// _NSUserNotificationCenterDelegatePrivate
+// _NSUserNotificationCenterDelegatePrivate:
 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
                didDismissAlert:(NSUserNotification*)notification {
   NSDictionary* response =
diff --git a/chrome/browser/ui/cocoa/notifications/xpc_mach_port.h b/chrome/browser/ui/cocoa/notifications/xpc_mach_port.h
new file mode 100644
index 0000000..cdf4041
--- /dev/null
+++ b/chrome/browser/ui/cocoa/notifications/xpc_mach_port.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_XPC_MACH_PORT_H_
+#define CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_XPC_MACH_PORT_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/mac/scoped_mach_port.h"
+
+// An XPC transit type for Mach send rights. While this class conforms to
+// NSSecureCoding, it is only valid to use the NSCoding methods for XPC.
+//
+// The public XPC APIs do not proivde any way to transfer Mach ports. Using the
+// C-based API, the functions for setting/creating an XPC Mach port object can
+// simply be forward-declared. But the Foundation API does not provide any way
+// to pass a Mach port as part of a message. The OS_xpc_object Obj-C types
+// do not conform to NSSecureCoding either, so they cannot be used to transport
+// ports via the Foundation-level API.
+//
+// Instead, this class encodes Mach port OOL data using the private
+// NSXPCEncoder and NSXPCDecoder APIs. These APIs expose methods by which
+// xpc_objct_t's (and their OS_object-bridged-siblings) can be encoded into a
+// Foundation-level XPC message.
+@interface CrXPCMachPort : NSObject<NSSecureCoding>
+
+// Creates a new transit Mach port by taking ownership of the |sendRight|.
+- (instancetype)initWithMachSendRight:(base::mac::ScopedMachSendRight)sendRight;
+
+// Relinquishes ownership of the Mach port to the caller.
+- (base::mac::ScopedMachSendRight)takeRight;
+
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_XPC_MACH_PORT_H_
diff --git a/chrome/browser/ui/cocoa/notifications/xpc_mach_port.mm b/chrome/browser/ui/cocoa/notifications/xpc_mach_port.mm
new file mode 100644
index 0000000..aa8cfba
--- /dev/null
+++ b/chrome/browser/ui/cocoa/notifications/xpc_mach_port.mm
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/notifications/xpc_mach_port.h"
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+
+@class OS_xpc_mach_send;
+
+extern "C" {
+
+OS_xpc_mach_send* xpc_mach_send_create(mach_port_t);
+mach_port_t xpc_mach_send_copy_right(OS_xpc_mach_send*);
+
+}  // extern "C"
+
+// Give the compiler declarations for the NSXPCCoder methods.
+@protocol ForwardDeclareXPCCoder
+- (void)encodeXPCObject:(id)object forKey:(id)key;
+- (id)decodeXPCObjectForKey:(id)key;
+@end
+
+namespace {
+
+NSString* const kCrSendRight = @"org.chromium.xpc.MachSendRight";
+
+}  // namespace
+
+@implementation CrXPCMachPort {
+  base::mac::ScopedMachSendRight port_;
+}
+
+- (instancetype)initWithMachSendRight:
+    (base::mac::ScopedMachSendRight)sendRight {
+  if ((self = [super init])) {
+    DCHECK(sendRight.is_valid());
+    port_ = std::move(sendRight);
+  }
+  return self;
+}
+
+- (base::mac::ScopedMachSendRight)takeRight {
+  return std::move(port_);
+}
+
+// NSCoding:
+- (instancetype)initWithCoder:(NSCoder*)coder {
+  DCHECK([coder isKindOfClass:NSClassFromString(@"NSXPCDecoder")]);
+  if ((self = [super init])) {
+    id coderAsId = coder;
+
+    OS_xpc_mach_send* xpcObject =
+        [coderAsId decodeXPCObjectForKey:kCrSendRight];
+    if (!xpcObject)
+      return nil;
+
+    port_.reset(xpc_mach_send_copy_right(xpcObject));
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)coder {
+  DCHECK([coder isKindOfClass:NSClassFromString(@"NSXPCEncoder")]);
+  DCHECK(port_.is_valid());
+
+  id coderAsId = coder;
+
+  base::scoped_nsobject<OS_xpc_mach_send> xpcObject(
+      xpc_mach_send_create(port_.get()));
+  [coderAsId encodeXPCObject:xpcObject forKey:kCrSendRight];
+}
+
+// NSSecureCoding:
++ (BOOL)supportsSecureCoding {
+  return YES;
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/website_settings/OWNERS b/chrome/browser/ui/cocoa/permission_bubble/OWNERS
similarity index 100%
rename from chrome/browser/ui/cocoa/website_settings/OWNERS
rename to chrome/browser/ui/cocoa/permission_bubble/OWNERS
diff --git a/chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.h b/chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.h
similarity index 84%
rename from chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.h
rename to chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.h
index 5ce3262..eec1f13f 100644
--- a/chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.h
+++ b/chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.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 CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_CHOOSER_BUBBLE_UI_COCOA_H_
-#define CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_CHOOSER_BUBBLE_UI_COCOA_H_
+#ifndef CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_CHOOSER_BUBBLE_UI_COCOA_H_
+#define CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_CHOOSER_BUBBLE_UI_COCOA_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -42,4 +42,4 @@
   DISALLOW_COPY_AND_ASSIGN(ChooserBubbleUiCocoa);
 };
 
-#endif  // CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_CHOOSER_BUBBLE_UI_COCOA_H_
+#endif  // CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_CHOOSER_BUBBLE_UI_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.mm b/chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.mm
similarity index 98%
rename from chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.mm
rename to chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.mm
index f7e8f1ee5..5950ab6 100644
--- a/chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "chrome/browser/ui/cocoa/website_settings/chooser_bubble_ui_cocoa.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/chooser_bubble_ui_cocoa.h"
 
 #include <stddef.h>
 
@@ -22,7 +22,7 @@
 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
-#include "chrome/browser/ui/website_settings/chooser_bubble_delegate.h"
+#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
 #include "components/bubble/bubble_controller.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h
similarity index 86%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h
index e13dadf..a683ad3 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.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 CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_PERMISSION_BUBBLE_COCOA_H_
-#define CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_PERMISSION_BUBBLE_COCOA_H_
+#ifndef CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_PERMISSION_BUBBLE_COCOA_H_
+#define CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_PERMISSION_BUBBLE_COCOA_H_
 
 #import <Foundation/Foundation.h>
 
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/cocoa/info_bubble_view.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "content/public/browser/web_contents.h"
 
 class Browser;
@@ -56,4 +56,4 @@
   DISALLOW_COPY_AND_ASSIGN(PermissionBubbleCocoa);
 };
 
-#endif  // CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_PERMISSION_BUBBLE_COCOA_H_
+#endif  // CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_PERMISSION_BUBBLE_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.mm
similarity index 88%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.mm
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.mm
index fb8f01f..88d5821 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
+#include "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
 
 #include "base/memory/ptr_util.h"
 #import "chrome/browser/ui/cocoa/base_bubble_controller.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h"
-#import "chrome/browser/ui/website_settings/permission_prompt.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h"
+#import "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "content/public/browser/web_contents.h"
 #import "ui/base/cocoa/nsview_additions.h"
 
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa_browser_test.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_browser_test.mm
similarity index 93%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa_browser_test.mm
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_browser_test.mm
index 663b3f2..de7d740 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa_browser_test.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_browser_test.mm
@@ -4,11 +4,11 @@
 
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands_mac.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
+#include "chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/website_settings/permission_bubble_browser_test_util.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #import "testing/gtest_mac.h"
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa_interactive_uitest.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_interactive_uitest.mm
similarity index 100%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa_interactive_uitest.mm
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_interactive_uitest.mm
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h
similarity index 87%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h
index 25e3da5b..5dec145 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_PERMISSION_BUBBLE_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_PERMISSION_BUBBLE_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_PERMISSION_BUBBLE_CONTROLLER_H_
+#define CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_PERMISSION_BUBBLE_CONTROLLER_H_
 
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/scoped_nsobject.h"
 #import "chrome/browser/ui/cocoa/omnibox_decoration_bubble_controller.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "ui/base/models/simple_menu_model.h"
 
 class Browser;
@@ -65,4 +65,4 @@
 
 @end
 
-#endif  // CHROME_BROWSER_UI_COCOA_WEBSITE_SETTINGS_PERMISSION_BUBBLE_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_COCOA_PERMISSION_BUBBLE_PERMISSION_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.mm
similarity index 98%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.mm
index 5872ab0..3fff34a 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h"
 
 #include <algorithm>
 
@@ -25,12 +25,12 @@
 #include "chrome/browser/ui/cocoa/page_info/permission_selector_button.h"
 #include "chrome/browser/ui/cocoa/page_info/split_block_button.h"
 #include "chrome/browser/ui/cocoa/page_info/website_settings_utils_cocoa.h"
-#include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
+#include "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/page_info/permission_menu_model.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller_unittest.mm
similarity index 98%
rename from chrome/browser/ui/cocoa/website_settings/permission_bubble_controller_unittest.mm
rename to chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller_unittest.mm
index fb360b4..4ff4a76 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h"
 
 #include <Carbon/Carbon.h>
 
@@ -18,9 +18,9 @@
 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
 #import "chrome/browser/ui/cocoa/page_info/split_block_button.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
 #import "chrome/browser/ui/cocoa/test/cocoa_profile_test.h"
 #include "chrome/browser/ui/cocoa/test/run_loop_testing.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_prompt_impl_views_mac.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_prompt_impl_views_mac.mm
similarity index 86%
rename from chrome/browser/ui/cocoa/website_settings/permission_prompt_impl_views_mac.mm
rename to chrome/browser/ui/cocoa/permission_bubble/permission_prompt_impl_views_mac.mm
index cfd0f53..79c8a95 100644
--- a/chrome/browser/ui/cocoa/website_settings/permission_prompt_impl_views_mac.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_prompt_impl_views_mac.mm
@@ -8,9 +8,9 @@
 #include "chrome/browser/ui/browser_window.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
-#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h"
-#include "chrome/browser/ui/views/website_settings/permission_prompt_impl.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
+#import "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_controller.h"
+#include "chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h"
 #import "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/base/material_design/material_design_controller.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
diff --git a/chrome/browser/ui/location_bar/location_bar_util.cc b/chrome/browser/ui/location_bar/location_bar_util.cc
deleted file mode 100644
index 38188593..0000000
--- a/chrome/browser/ui/location_bar/location_bar_util.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/location_bar/location_bar_util.h"
-
-#include <stddef.h>
-
-#include "base/i18n/rtl.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ui/gfx/text_elider.h"
-
-namespace location_bar_util {
-
-base::string16 CalculateMinString(const base::string16& description) {
-  // Chop at the first '.' or whitespace.
-  const size_t chop_index = description.find_first_of(
-      base::kWhitespaceUTF16 + base::ASCIIToUTF16("."));
-  base::string16 min_string((chop_index == base::string16::npos) ?
-      gfx::TruncateString(description, 3, gfx::WORD_BREAK) :
-      description.substr(0, chop_index));
-  base::i18n::AdjustStringForLocaleDirection(&min_string);
-  return min_string;
-}
-
-}  // namespace location_bar_util
diff --git a/chrome/browser/ui/location_bar/location_bar_util.h b/chrome/browser/ui/location_bar/location_bar_util.h
deleted file mode 100644
index a4083697..0000000
--- a/chrome/browser/ui/location_bar/location_bar_util.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_UTIL_H_
-#define CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_UTIL_H_
-
-#include "base/strings/string16.h"
-
-namespace location_bar_util {
-
-// Build a short string to use in keyword-search when the field isn't very big.
-base::string16 CalculateMinString(const base::string16& description);
-
-}  // namespace location_bar_util
-
-#endif  // CHROME_BROWSER_UI_LOCATION_BAR_LOCATION_BAR_UTIL_H_
diff --git a/chrome/browser/ui/website_settings/OWNERS b/chrome/browser/ui/permission_bubble/OWNERS
similarity index 100%
rename from chrome/browser/ui/website_settings/OWNERS
rename to chrome/browser/ui/permission_bubble/OWNERS
diff --git a/chrome/browser/ui/website_settings/chooser_bubble_delegate.cc b/chrome/browser/ui/permission_bubble/chooser_bubble_delegate.cc
similarity index 92%
rename from chrome/browser/ui/website_settings/chooser_bubble_delegate.cc
rename to chrome/browser/ui/permission_bubble/chooser_bubble_delegate.cc
index 1792db2..121eb179 100644
--- a/chrome/browser/ui/website_settings/chooser_bubble_delegate.cc
+++ b/chrome/browser/ui/permission_bubble/chooser_bubble_delegate.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 "chrome/browser/ui/website_settings/chooser_bubble_delegate.h"
+#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
 
 #include "chrome/browser/chooser_controller/chooser_controller.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ui/website_settings/chooser_bubble_delegate.h b/chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h
similarity index 86%
rename from chrome/browser/ui/website_settings/chooser_bubble_delegate.h
rename to chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h
index e521b54..134cda0 100644
--- a/chrome/browser/ui/website_settings/chooser_bubble_delegate.h
+++ b/chrome/browser/ui/permission_bubble/chooser_bubble_delegate.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 CHROME_BROWSER_UI_WEBSITE_SETTINGS_CHOOSER_BUBBLE_DELEGATE_H_
-#define CHROME_BROWSER_UI_WEBSITE_SETTINGS_CHOOSER_BUBBLE_DELEGATE_H_
+#ifndef CHROME_BROWSER_UI_PERMISSION_BUBBLE_CHOOSER_BUBBLE_DELEGATE_H_
+#define CHROME_BROWSER_UI_PERMISSION_BUBBLE_CHOOSER_BUBBLE_DELEGATE_H_
 
 #include "base/macros.h"
 #include "components/bubble/bubble_delegate.h"
@@ -42,4 +42,4 @@
   DISALLOW_COPY_AND_ASSIGN(ChooserBubbleDelegate);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBSITE_SETTINGS_CHOOSER_BUBBLE_DELEGATE_H_
+#endif  // CHROME_BROWSER_UI_PERMISSION_BUBBLE_CHOOSER_BUBBLE_DELEGATE_H_
diff --git a/chrome/browser/ui/website_settings/mock_permission_prompt.cc b/chrome/browser/ui/permission_bubble/mock_permission_prompt.cc
similarity index 90%
rename from chrome/browser/ui/website_settings/mock_permission_prompt.cc
rename to chrome/browser/ui/permission_bubble/mock_permission_prompt.cc
index d0e9cb0..48dd339 100644
--- a/chrome/browser/ui/website_settings/mock_permission_prompt.cc
+++ b/chrome/browser/ui/permission_bubble/mock_permission_prompt.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/website_settings/mock_permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt.h"
 
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 
 MockPermissionPrompt::~MockPermissionPrompt() {
   Hide();
diff --git a/chrome/browser/ui/website_settings/mock_permission_prompt.h b/chrome/browser/ui/permission_bubble/mock_permission_prompt.h
similarity index 81%
rename from chrome/browser/ui/website_settings/mock_permission_prompt.h
rename to chrome/browser/ui/permission_bubble/mock_permission_prompt.h
index a3a1d01..efe690fe 100644
--- a/chrome/browser/ui/website_settings/mock_permission_prompt.h
+++ b/chrome/browser/ui/permission_bubble/mock_permission_prompt.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBSITE_SETTINGS_MOCK_PERMISSION_PROMPT_H_
-#define CHROME_BROWSER_UI_WEBSITE_SETTINGS_MOCK_PERMISSION_PROMPT_H_
+#ifndef CHROME_BROWSER_UI_PERMISSION_BUBBLE_MOCK_PERMISSION_PROMPT_H_
+#define CHROME_BROWSER_UI_PERMISSION_BUBBLE_MOCK_PERMISSION_PROMPT_H_
 
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 
 class MockPermissionPromptFactory;
 class PermissionRequestManager;
@@ -40,4 +40,4 @@
   bool is_visible_;
 };
 
-#endif  // CHROME_BROWSER_UI_WEBSITE_SETTINGS_MOCK_PERMISSION_PROMPT_H_
+#endif  // CHROME_BROWSER_UI_PERMISSION_BUBBLE_MOCK_PERMISSION_PROMPT_H_
diff --git a/chrome/browser/ui/website_settings/mock_permission_prompt_factory.cc b/chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.cc
similarity index 94%
rename from chrome/browser/ui/website_settings/mock_permission_prompt_factory.cc
rename to chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.cc
index ddf28bfc..11a96b4 100644
--- a/chrome/browser/ui/website_settings/mock_permission_prompt_factory.cc
+++ b/chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/website_settings/mock_permission_prompt_factory.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h"
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
-#include "chrome/browser/ui/website_settings/mock_permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/mock_permission_prompt.h"
 #include "content/public/browser/web_contents.h"
 
 MockPermissionPromptFactory::MockPermissionPromptFactory(
diff --git a/chrome/browser/ui/website_settings/mock_permission_prompt_factory.h b/chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h
similarity index 90%
rename from chrome/browser/ui/website_settings/mock_permission_prompt_factory.h
rename to chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.h
index 2170345..ad02121 100644
--- a/chrome/browser/ui/website_settings/mock_permission_prompt_factory.h
+++ b/chrome/browser/ui/permission_bubble/mock_permission_prompt_factory.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 CHROME_BROWSER_UI_WEBSITE_SETTINGS_MOCK_PERMISSION_PROMPT_FACTORY_H_
-#define CHROME_BROWSER_UI_WEBSITE_SETTINGS_MOCK_PERMISSION_PROMPT_FACTORY_H_
+#ifndef CHROME_BROWSER_UI_PERMISSION_BUBBLE_MOCK_PERMISSION_PROMPT_FACTORY_H_
+#define CHROME_BROWSER_UI_PERMISSION_BUBBLE_MOCK_PERMISSION_PROMPT_FACTORY_H_
 
 #include <memory>
 #include <vector>
@@ -82,4 +82,4 @@
   DISALLOW_COPY_AND_ASSIGN(MockPermissionPromptFactory);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBSITE_SETTINGS_MOCK_PERMISSION_PROMPT_FACTORY_H_
+#endif  // CHROME_BROWSER_UI_PERMISSION_BUBBLE_MOCK_PERMISSION_PROMPT_FACTORY_H_
diff --git a/chrome/browser/ui/website_settings/permission_bubble_browser_test_util.cc b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
similarity index 96%
rename from chrome/browser/ui/website_settings/permission_bubble_browser_test_util.cc
rename to chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
index de85749..fe6b7a0 100644
--- a/chrome/browser/ui/website_settings/permission_bubble_browser_test_util.cc
+++ b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.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 "chrome/browser/ui/website_settings/permission_bubble_browser_test_util.h"
+#include "chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h"
 
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
diff --git a/chrome/browser/ui/website_settings/permission_bubble_browser_test_util.h b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h
similarity index 85%
rename from chrome/browser/ui/website_settings/permission_bubble_browser_test_util.h
rename to chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h
index 752bbc6..66a8295 100644
--- a/chrome/browser/ui/website_settings/permission_bubble_browser_test_util.h
+++ b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBSITE_SETTINGS_PERMISSION_BUBBLE_BROWSER_TEST_UTIL_H_
-#define CHROME_BROWSER_UI_WEBSITE_SETTINGS_PERMISSION_BUBBLE_BROWSER_TEST_UTIL_H_
+#ifndef CHROME_BROWSER_UI_PERMISSION_BUBBLE_PERMISSION_BUBBLE_BROWSER_TEST_UTIL_H_
+#define CHROME_BROWSER_UI_PERMISSION_BUBBLE_PERMISSION_BUBBLE_BROWSER_TEST_UTIL_H_
 
 #include <memory>
 #include <vector>
 
 #include "base/macros.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 
 namespace base {
 class CommandLine;
@@ -68,4 +68,4 @@
   DISALLOW_COPY_AND_ASSIGN(PermissionBubbleKioskBrowserTest);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBSITE_SETTINGS_PERMISSION_BUBBLE_BROWSER_TEST_UTIL_H_
+#endif  // CHROME_BROWSER_UI_PERMISSION_BUBBLE_PERMISSION_BUBBLE_BROWSER_TEST_UTIL_H_
diff --git a/chrome/browser/ui/website_settings/permission_prompt.h b/chrome/browser/ui/permission_bubble/permission_prompt.h
similarity index 92%
rename from chrome/browser/ui/website_settings/permission_prompt.h
rename to chrome/browser/ui/permission_bubble/permission_prompt.h
index 4b7dd15e..ec5b65e 100644
--- a/chrome/browser/ui/website_settings/permission_prompt.h
+++ b/chrome/browser/ui/permission_bubble/permission_prompt.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 CHROME_BROWSER_UI_WEBSITE_SETTINGS_PERMISSION_PROMPT_H_
-#define CHROME_BROWSER_UI_WEBSITE_SETTINGS_PERMISSION_PROMPT_H_
+#ifndef CHROME_BROWSER_UI_PERMISSION_BUBBLE_PERMISSION_PROMPT_H_
+#define CHROME_BROWSER_UI_PERMISSION_BUBBLE_PERMISSION_PROMPT_H_
 
 #include <memory>
 #include <vector>
@@ -76,4 +76,4 @@
   virtual gfx::NativeWindow GetNativeWindow() = 0;
 };
 
-#endif  // CHROME_BROWSER_UI_WEBSITE_SETTINGS_PERMISSION_PROMPT_H_
+#endif  // CHROME_BROWSER_UI_PERMISSION_BUBBLE_PERMISSION_PROMPT_H_
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 4a1ee58..7dca4b0 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -117,8 +117,8 @@
   // the label has zero size it doesn't actually matter what we compute its X
   // value to be, since it won't be visible.
   const int label_x = image_->bounds().right() + GetInternalSpacing();
-  const int label_width =
-      std::max(0, width() - label_x - bubble_trailing_padding);
+  const int label_width = std::max(
+      0, width() - label_x - bubble_trailing_padding - kSpaceBesideSeparator);
   label_->SetBounds(label_x, 0, label_width, height());
 }
 
@@ -229,13 +229,7 @@
   gfx::Rect bounds(label_->bounds());
   const int kSeparatorHeight = 16;
   bounds.Inset(0, (bounds.height() - kSeparatorHeight) / 2);
-
-  // Draw the 1 px separator.
-  gfx::ScopedCanvas scoped_canvas(canvas);
-  const float scale = canvas->UndoDeviceScaleFactor();
-  // Keep the separator aligned on a pixel center.
-  const gfx::RectF pixel_aligned_bounds =
-      gfx::ScaleRect(gfx::RectF(bounds), scale) - gfx::Vector2dF(0.5f, 0);
-  canvas->DrawLine(pixel_aligned_bounds.top_right(),
-                   pixel_aligned_bounds.bottom_right(), separator_color);
+  bounds.set_width(bounds.width() + kSpaceBesideSeparator);
+  canvas->Draw1pxLine(gfx::PointF(bounds.top_right()),
+                      gfx::PointF(bounds.bottom_right()), separator_color);
 }
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index afdd605..0887f380b 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -473,12 +473,15 @@
   // hits ctrl-d).
   const int vertical_padding = GetTotalVerticalPadding();
   const int location_height = std::max(height() - (vertical_padding * 2), 0);
+  // The largest fraction of the omnibox that can be taken by the EV or search
+  // label/chip.
+  const double kLeadingDecorationMaxFraction = 0.5;
 
   location_icon_view_->SetLabel(base::string16());
   if (ShouldShowKeywordBubble()) {
-    leading_decorations.AddDecoration(vertical_padding, location_height, true,
-                                      0, item_padding, item_padding,
-                                      selected_keyword_view_);
+    leading_decorations.AddDecoration(
+        vertical_padding, location_height, false, kLeadingDecorationMaxFraction,
+        item_padding, item_padding, selected_keyword_view_);
     if (selected_keyword_view_->keyword() != keyword) {
       selected_keyword_view_->SetKeyword(keyword);
       const TemplateURL* template_url =
@@ -495,11 +498,9 @@
     }
   } else if (ShouldShowLocationIconText()) {
     location_icon_view_->SetLabel(GetLocationIconText());
-    // The largest fraction of the omnibox that can be taken by the EV bubble.
-    const double kMaxBubbleFraction = 0.5;
-    leading_decorations.AddDecoration(vertical_padding, location_height, false,
-                                      kMaxBubbleFraction, item_padding,
-                                      item_padding, location_icon_view_);
+    leading_decorations.AddDecoration(
+        vertical_padding, location_height, false, kLeadingDecorationMaxFraction,
+        item_padding, item_padding, location_icon_view_);
   } else {
     leading_decorations.AddDecoration(vertical_padding, location_height,
                                       location_icon_view_);
diff --git a/chrome/browser/ui/views/location_bar/selected_keyword_view.cc b/chrome/browser/ui/views/location_bar/selected_keyword_view.cc
index 981a122b..e2e3280a 100644
--- a/chrome/browser/ui/views/location_bar/selected_keyword_view.cc
+++ b/chrome/browser/ui/views/location_bar/selected_keyword_view.cc
@@ -7,7 +7,6 @@
 #include "base/logging.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/ui/location_bar/location_bar_util.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/search_engines/template_url_service.h"
@@ -26,6 +25,7 @@
   full_label_.SetVisible(false);
   partial_label_.SetFontList(font_list);
   partial_label_.SetVisible(false);
+  label()->SetElideBehavior(gfx::FADE_TAIL);
 }
 
 SelectedKeywordView::~SelectedKeywordView() {
@@ -50,12 +50,18 @@
 
 gfx::Size SelectedKeywordView::GetMinimumSize() const {
   // Height will be ignored by the LocationBarView.
-  return GetSizeForLabelWidth(partial_label_.GetMinimumSize().width());
+  return GetSizeForLabelWidth(0);
 }
 
 void SelectedKeywordView::Layout() {
-  SetLabel(((width() == GetPreferredSize().width()) ?
-      full_label_ : partial_label_).text());
+  // Keep showing the full label as long as there's more than enough width for
+  // the partial label. Otherwise there will be empty space displayed next to
+  // the partial label.
+  bool use_full_label =
+      width() >
+      GetSizeForLabelWidth(partial_label_.GetPreferredSize().width()).width();
+  SetLabel(use_full_label ? full_label_.text() : partial_label_.text());
+
   IconLabelBubbleView::Layout();
 }
 
@@ -78,14 +84,7 @@
           : l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT_MD, short_name);
   full_label_.SetText(full_name);
 
-  const base::string16 min_string(
-      location_bar_util::CalculateMinString(short_name));
-  const base::string16 partial_name =
-      is_extension_keyword
-          ? min_string
-          : l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT_MD, min_string);
-  partial_label_.SetText(min_string.empty() ?
-      full_label_.text() : partial_name);
+  partial_label_.SetText(short_name);
 
   // Update the label now so ShouldShowLabel() works correctly when the parent
   // class is calculating the preferred size. It will be updated again in
diff --git a/chrome/browser/ui/views/website_settings/OWNERS b/chrome/browser/ui/views/permission_bubble/OWNERS
similarity index 100%
rename from chrome/browser/ui/views/website_settings/OWNERS
rename to chrome/browser/ui/views/permission_bubble/OWNERS
diff --git a/chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.cc b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.cc
similarity index 97%
rename from chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.cc
rename to chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.cc
index 9addbb1..ef305f7 100644
--- a/chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.cc
+++ b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.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 "chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.h"
+#include "chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.h"
 
 #include <stddef.h>
 
@@ -14,13 +14,13 @@
 #include "chrome/browser/chooser_controller/chooser_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
 #include "chrome/browser/ui/views/device_chooser_content_view.h"
 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/location_bar/location_icon_view.h"
-#include "chrome/browser/ui/website_settings/chooser_bubble_delegate.h"
 #include "components/bubble/bubble_controller.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/link.h"
diff --git a/chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.h b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.h
similarity index 85%
rename from chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.h
rename to chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.h
index 596dbd3..e2385a0a 100644
--- a/chrome/browser/ui/views/website_settings/chooser_bubble_ui_view.h
+++ b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui_view.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 CHROME_BROWSER_UI_VIEWS_WEBSITE_SETTINGS_CHOOSER_BUBBLE_UI_VIEW_H_
-#define CHROME_BROWSER_UI_VIEWS_WEBSITE_SETTINGS_CHOOSER_BUBBLE_UI_VIEW_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_CHOOSER_BUBBLE_UI_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_CHOOSER_BUBBLE_UI_VIEW_H_
 
 #include <memory>
 
@@ -46,4 +46,4 @@
   DISALLOW_COPY_AND_ASSIGN(ChooserBubbleUiView);
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_WEBSITE_SETTINGS_CHOOSER_BUBBLE_UI_VIEW_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_CHOOSER_BUBBLE_UI_VIEW_H_
diff --git a/chrome/browser/ui/views/website_settings/permission_prompt_impl.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
similarity index 99%
rename from chrome/browser/ui/views/website_settings/permission_prompt_impl.cc
rename to chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
index 582c3ba..47835f5 100644
--- a/chrome/browser/ui/views/website_settings/permission_prompt_impl.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.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 "chrome/browser/ui/views/website_settings/permission_prompt_impl.h"
+#include "chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h"
 
 #include <stddef.h>
 
diff --git a/chrome/browser/ui/views/website_settings/permission_prompt_impl.h b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h
similarity index 85%
rename from chrome/browser/ui/views/website_settings/permission_prompt_impl.h
rename to chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h
index 0825619..e84f22d 100644
--- a/chrome/browser/ui/views/website_settings/permission_prompt_impl.h
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_VIEWS_WEBSITE_SETTINGS_PERMISSION_PROMPT_IMPL_H_
-#define CHROME_BROWSER_UI_VIEWS_WEBSITE_SETTINGS_PERMISSION_PROMPT_IMPL_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_PERMISSION_PROMPT_IMPL_H_
+#define CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_PERMISSION_PROMPT_IMPL_H_
 
 #include <memory>
 #include <string>
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "chrome/browser/ui/website_settings/permission_prompt.h"
+#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/views/bubble/bubble_border.h"
 
@@ -64,4 +64,4 @@
   DISALLOW_COPY_AND_ASSIGN(PermissionPromptImpl);
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_WEBSITE_SETTINGS_PERMISSION_PROMPT_IMPL_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_PERMISSION_BUBBLE_PERMISSION_PROMPT_IMPL_H_
diff --git a/chrome/browser/ui/views/website_settings/permission_prompt_impl_views.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl_views.cc
similarity index 96%
rename from chrome/browser/ui/views/website_settings/permission_prompt_impl_views.cc
rename to chrome/browser/ui/views/permission_bubble/permission_prompt_impl_views.cc
index cf1117e..1b688ec 100644
--- a/chrome/browser/ui/views/website_settings/permission_prompt_impl_views.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl_views.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/location_bar/location_icon_view.h"
-#include "chrome/browser/ui/views/website_settings/permission_prompt_impl.h"
+#include "chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/vector2d.h"
diff --git a/chrome/browser/usb/web_usb_chooser_service.cc b/chrome/browser/usb/web_usb_chooser_service.cc
index e7efb5e..7ad5384 100644
--- a/chrome/browser/usb/web_usb_chooser_service.cc
+++ b/chrome/browser/usb/web_usb_chooser_service.cc
@@ -8,7 +8,7 @@
 
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_bubble_manager.h"
-#include "chrome/browser/ui/website_settings/chooser_bubble_delegate.h"
+#include "chrome/browser/ui/permission_bubble/chooser_bubble_delegate.h"
 #include "chrome/browser/usb/usb_chooser_controller.h"
 #include "components/bubble/bubble_controller.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index d480ff03..3734f21e 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -160,8 +160,6 @@
 const base::FilePath::CharType kPreferencesFilename[] = FPL("Preferences");
 const base::FilePath::CharType kPreviewsOptOutDBFilename[] =
     FPL("previews_opt_out.db");
-const base::FilePath::CharType kProtectedPreferencesFilenameDeprecated[] =
-    FPL("Protected Preferences");
 const base::FilePath::CharType kReadmeFilename[] = FPL("README");
 const base::FilePath::CharType kSecurePreferencesFilename[] =
     FPL("Secure Preferences");
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index d738b8e..2e39ef21 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -61,7 +61,6 @@
 extern const base::FilePath::CharType kOfflinePageRequestQueueDirname[];
 extern const base::FilePath::CharType kPreferencesFilename[];
 extern const base::FilePath::CharType kPreviewsOptOutDBFilename[];
-extern const base::FilePath::CharType kProtectedPreferencesFilenameDeprecated[];
 extern const base::FilePath::CharType kReadmeFilename[];
 extern const base::FilePath::CharType kSecurePreferencesFilename[];
 extern const base::FilePath::CharType kServiceStateFileName[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8f42c09..7c1d1be 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -388,7 +388,7 @@
       "../browser/ui/browser_focus_uitest.cc",
       "../browser/ui/cocoa/apps/app_shim_menu_controller_mac_interactive_uitest.mm",
       "../browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.mm",
-      "../browser/ui/cocoa/website_settings/permission_bubble_cocoa_interactive_uitest.mm",
+      "../browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_interactive_uitest.mm",
       "../browser/ui/exclusive_access/flash_fullscreen_interactive_browsertest.cc",
       "../browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc",
       "../browser/ui/exclusive_access/fullscreen_controller_state_interactive_browsertest.cc",
@@ -1775,6 +1775,12 @@
       "../browser/ui/location_bar/location_bar_browsertest.cc",
       "../browser/ui/login/login_handler_browsertest.cc",
       "../browser/ui/native_window_tracker_browsertest.cc",
+      "../browser/ui/permission_bubble/mock_permission_prompt.cc",
+      "../browser/ui/permission_bubble/mock_permission_prompt.h",
+      "../browser/ui/permission_bubble/mock_permission_prompt_factory.cc",
+      "../browser/ui/permission_bubble/mock_permission_prompt_factory.h",
+      "../browser/ui/permission_bubble/permission_bubble_browser_test_util.cc",
+      "../browser/ui/permission_bubble/permission_bubble_browser_test_util.h",
       "../browser/ui/passwords/manage_passwords_test.cc",
       "../browser/ui/prefs/prefs_tab_helper_browsertest.cc",
       "../browser/ui/profile_error_browsertest.cc",
@@ -1798,12 +1804,6 @@
       "../browser/ui/toolbar/mock_component_toolbar_actions_factory.h",
       "../browser/ui/update_chrome_dialog_browsertest.cc",
       "../browser/ui/views/hung_renderer_view_browsertest.cc",
-      "../browser/ui/website_settings/mock_permission_prompt.cc",
-      "../browser/ui/website_settings/mock_permission_prompt.h",
-      "../browser/ui/website_settings/mock_permission_prompt_factory.cc",
-      "../browser/ui/website_settings/mock_permission_prompt_factory.h",
-      "../browser/ui/website_settings/permission_bubble_browser_test_util.cc",
-      "../browser/ui/website_settings/permission_bubble_browser_test_util.h",
       "../browser/ui/webui/bidi_checker_web_ui_test.cc",
       "../browser/ui/webui/bidi_checker_web_ui_test.h",
       "../browser/ui/webui/bookmarks_ui_browsertest.cc",
@@ -2553,13 +2553,13 @@
           "../browser/ui/cocoa/location_bar/zoom_decoration_browsertest.mm",
           "../browser/ui/cocoa/omnibox/omnibox_view_mac_browsertest.mm",
           "../browser/ui/cocoa/passwords/passwords_bubble_browsertest.mm",
+          "../browser/ui/cocoa/permission_bubble/permission_bubble_cocoa_browser_test.mm",
           "../browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller_browsertest.mm",
           "../browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_browsertest.mm",
           "../browser/ui/cocoa/ssl_client_certificate_selector_cocoa_browsertest.mm",
           "../browser/ui/cocoa/task_manager_mac_browsertest.mm",
           "../browser/ui/cocoa/view_id_util_browsertest.mm",
           "../browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac_browsertest.mm",
-          "../browser/ui/cocoa/website_settings/permission_bubble_cocoa_browser_test.mm",
         ]
       }
     }
@@ -3252,6 +3252,7 @@
     "../browser/page_load_metrics/observers/document_write_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/google_captcha_observer_unittest.cc",
+    "../browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc",
     "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h",
     "../browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc",
@@ -3270,7 +3271,6 @@
     "../browser/password_manager/password_store_x_unittest.cc",
     "../browser/password_manager/simple_password_store_mac_unittest.cc",
     "../browser/permissions/chooser_context_base_unittest.cc",
-    "../browser/permissions/delegation_tracker_unittest.cc",
     "../browser/permissions/permission_context_base_unittest.cc",
     "../browser/permissions/permission_decision_auto_blocker_unittest.cc",
     "../browser/permissions/permission_manager_unittest.cc",
@@ -3373,6 +3373,10 @@
     "../browser/ui/find_bar/find_backend_unittest.cc",
     "../browser/ui/login/login_handler_unittest.cc",
     "../browser/ui/page_info/website_settings_unittest.cc",
+    "../browser/ui/permission_bubble/mock_permission_prompt.cc",
+    "../browser/ui/permission_bubble/mock_permission_prompt.h",
+    "../browser/ui/permission_bubble/mock_permission_prompt_factory.cc",
+    "../browser/ui/permission_bubble/mock_permission_prompt_factory.h",
     "../browser/ui/passwords/manage_passwords_state_unittest.cc",
     "../browser/ui/passwords/manage_passwords_view_utils_unittest.cc",
     "../browser/ui/passwords/password_manager_presenter_unittest.cc",
@@ -3381,10 +3385,6 @@
     "../browser/ui/sync/sync_promo_ui_unittest.cc",
     "../browser/ui/tests/ui_gfx_image_unittest.cc",
     "../browser/ui/tests/ui_gfx_image_unittest.mm",
-    "../browser/ui/website_settings/mock_permission_prompt.cc",
-    "../browser/ui/website_settings/mock_permission_prompt.h",
-    "../browser/ui/website_settings/mock_permission_prompt_factory.cc",
-    "../browser/ui/website_settings/mock_permission_prompt_factory.h",
     "../browser/ui/webui/browsing_history_handler_unittest.cc",
     "../browser/ui/webui/fileicon_source_unittest.cc",
     "../browser/ui/webui/local_state/local_state_ui_unittest.cc",
@@ -4358,6 +4358,7 @@
       "../browser/printing/print_preview_dialog_controller_unittest.cc",
       "../browser/printing/print_preview_test.cc",
       "../browser/printing/print_preview_test.h",
+      "../browser/printing/print_view_manager_unittest.cc",
       "../browser/ui/webui/print_preview/extension_printer_handler_unittest.cc",
       "../browser/ui/webui/print_preview/print_preview_ui_unittest.cc",
       "../browser/ui/webui/print_preview/printer_capabilities_unittest.cc",
@@ -4663,6 +4664,7 @@
         "../browser/ui/cocoa/passwords/save_pending_password_view_controller_unittest.mm",
         "../browser/ui/cocoa/passwords/signin_promo_view_controller_unittest.mm",
         "../browser/ui/cocoa/passwords/update_pending_password_view_controller_unittest.mm",
+        "../browser/ui/cocoa/permission_bubble/permission_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/profiles/avatar_button_controller_unittest.mm",
         "../browser/ui/cocoa/profiles/avatar_button_unittest.mm",
         "../browser/ui/cocoa/profiles/avatar_icon_controller_unittest.mm",
@@ -4698,7 +4700,6 @@
         "../browser/ui/cocoa/vertical_gradient_view_unittest.mm",
         "../browser/ui/cocoa/view_resizer_pong.h",
         "../browser/ui/cocoa/view_resizer_pong.mm",
-        "../browser/ui/cocoa/website_settings/permission_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/window_size_autosaver_unittest.mm",
       ]
     }
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 799137f..cd41f3e 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -74,6 +74,10 @@
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=992
     'ChromeDownloadDirTest.testDownloadDirectoryOverridesExistingPreferences',
 ]
+_VERSION_SPECIFIC_FILTER['58'] = [
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1673
+    'ChromeDriverPageLoadTimeoutTest.testPageLoadTimeoutCrossDomain',
+]
 _VERSION_SPECIFIC_FILTER['57'] = [
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=992
     'ChromeDownloadDirTest.testDownloadDirectoryOverridesExistingPreferences',
diff --git a/chrome/test/data/extensions/platform_apps/web_view/ime/guest.html b/chrome/test/data/extensions/platform_apps/web_view/ime/guest.html
new file mode 100644
index 0000000..4253415
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/ime/guest.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<!--
+ * Copyright 2017 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<html>
+<header>
+	<style>
+	#editbox {
+		position: fixed;
+		width: 200px;
+		height: 100px;
+	}
+	</style>
+</header>
+<body>
+  <input id="editbox"/>
+  <script>
+  var parentWindow = null;
+  var input = null;
+
+  window.addEventListener('load', function() {
+    input = document.getElementById('editbox');
+    input.addEventListener('focus', function() {      
+      input.value = 'A B X D';
+      parentWindow.postMessage({type: 'focus'}, '*');
+    });
+  });
+
+  window.addEventListener('message', function(e) {
+    parentWindow = e.source;
+    parentWindow.postMessage({type: 'init'}, '*');		
+  });
+
+  window.addEventListener('input', function(e) {
+   parentWindow.postMessage({
+     type: 'input',
+     value: input.innerText}, '*');
+ });
+  
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/web_view/ime/main.html b/chrome/test/data/extensions/platform_apps/web_view/ime/main.html
new file mode 100644
index 0000000..39006f9
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/ime/main.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<!--
+ * Copyright 2017 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<html>
+<header>
+<style>
+	#webview-tag-container {		
+		position: fixed;	
+		width: 100%;
+		height: 100%;
+	}
+</style>
+</header>
+<body>
+  <div id="webview-tag-container"></div>  
+  <script src="main.js"></script>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/platform_apps/web_view/ime/main.js b/chrome/test/data/extensions/platform_apps/web_view/ime/main.js
new file mode 100644
index 0000000..0464b2f
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/ime/main.js
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+window.addEventListener('load', function() {
+  window.webview = document.createElement('webview');
+  document.querySelector('#webview-tag-container').appendChild(webview);
+
+  webview.style.width = "100%";
+  webview.style.height = "100%";
+  webview.addEventListener('loadstop', function() {
+    webview.contentWindow.postMessage({}, '*');
+  });
+
+  chrome.test.getConfig(function(config) {
+    var url = 'http://localhost:' + config.testServer.port +
+        '/extensions/platform_apps/web_view/ime/guest.html';
+    webview.src = url;
+  });
+});
+
+window.addEventListener('message', function(e) {
+  if (e.data.type === 'init') {
+    chrome.test.sendMessage('WebViewImeTest.Launched');
+  } else if (e.data.type === 'focus') {
+    chrome.test.sendMessage('WebViewImeTest.InputFocused')
+  } else if (e.data.type === 'input') {
+    chrome.test.sendMessage('WebViewImetest.InputReceived')
+  }
+});
+
diff --git a/chrome/test/data/extensions/platform_apps/web_view/ime/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/ime/manifest.json
new file mode 100644
index 0000000..bcf75c5
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/ime/manifest.json
@@ -0,0 +1,12 @@
+{
+  "name": "<webview> IME tests.",
+  "version": "1",
+  "permissions": [
+    "webview"
+  ],
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  }
+}
diff --git a/chrome/test/data/extensions/platform_apps/web_view/ime/test.js b/chrome/test/data/extensions/platform_apps/web_view/ime/test.js
new file mode 100644
index 0000000..40a8908
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/ime/test.js
@@ -0,0 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.app.runtime.onLaunched.addListener(function() {
+  chrome.app.window.create('main.html', {}, function () {});
+});
diff --git a/chrome/test/data/webui/md_bookmarks/reducers_test.js b/chrome/test/data/webui/md_bookmarks/reducers_test.js
index ba605475..02a8b93f 100644
--- a/chrome/test/data/webui/md_bookmarks/reducers_test.js
+++ b/chrome/test/data/webui/md_bookmarks/reducers_test.js
@@ -82,81 +82,78 @@
 
 suite('closed folder state', function() {
   var nodes;
-  // TODO(tsergeant): Remove use of 'initialState' and 'nextState'.
-  var initialState;
+  var state;
+  var action;
 
   setup(function() {
     nodes = testTree(createFolder('1', [
       createFolder('2', []),
     ]));
-    initialState = {};
+    state = {};
   });
 
   test('toggle folder open state', function() {
-    var action = bookmarks.actions.changeFolderOpen('2', false);
-    var nextState = bookmarks.ClosedFolderState.updateClosedFolders(
-        initialState, action, nodes);
-    assertFalse(!!nextState['1']);
-    assertTrue(nextState['2']);
+    action = bookmarks.actions.changeFolderOpen('2', false);
+    state =
+        bookmarks.ClosedFolderState.updateClosedFolders(state, action, nodes);
+    assertFalse(!!state['1']);
+    assertTrue(state['2']);
   });
 
   test('select folder with closed parent', function() {
-    var action;
-    var nextState;
     // Close '1'
     action = bookmarks.actions.changeFolderOpen('1', false);
-    nextState = bookmarks.ClosedFolderState.updateClosedFolders(
-        initialState, action, nodes);
-    assertTrue(nextState['1']);
-    assertFalse(!!nextState['2']);
+    state =
+        bookmarks.ClosedFolderState.updateClosedFolders(state, action, nodes);
+    assertTrue(state['1']);
+    assertFalse(!!state['2']);
 
     // Should re-open when '2' is selected.
     action = bookmarks.actions.selectFolder('2');
-    nextState = bookmarks.ClosedFolderState.updateClosedFolders(
-        nextState, action, nodes);
-    assertFalse(!!nextState['1']);
+    state =
+        bookmarks.ClosedFolderState.updateClosedFolders(state, action, nodes);
+    assertFalse(!!state['1']);
   });
 });
 
 suite('selected folder', function() {
   var nodes;
-  var initialState;
+  var state;
+  var action;
 
   setup(function() {
     nodes = testTree(createFolder('1', [
       createFolder('2', []),
     ]));
 
-    initialState = '1';
+    state = '1';
   });
 
   test('updates from selectFolder action', function() {
-    var action = bookmarks.actions.selectFolder('2');
-    var newState = bookmarks.SelectedFolderState.updateSelectedFolder(
-        initialState, action, nodes);
-    assertEquals('2', newState);
+    action = bookmarks.actions.selectFolder('2');
+    state = bookmarks.SelectedFolderState.updateSelectedFolder(
+        state, action, nodes);
+    assertEquals('2', state);
   });
 
   test('updates when parent of selected folder is closed', function() {
-    var action;
-    var newState;
-
     action = bookmarks.actions.selectFolder('2');
-    newState = bookmarks.SelectedFolderState.updateSelectedFolder(
-        initialState, action, nodes);
+    state = bookmarks.SelectedFolderState.updateSelectedFolder(
+        state, action, nodes);
 
     action = bookmarks.actions.changeFolderOpen('1', false);
-    newState = bookmarks.SelectedFolderState.updateSelectedFolder(
-        newState, action, nodes);
-    assertEquals('1', newState);
+    state = bookmarks.SelectedFolderState.updateSelectedFolder(
+        state, action, nodes);
+    assertEquals('1', state);
   });
 });
 
 suite('node state', function() {
-  var initialState;
+  var state;
+  var action;
 
   setup(function() {
-    initialState = testTree(
+    state = testTree(
         createFolder(
             '1',
             [
@@ -168,37 +165,37 @@
   });
 
   test('updates when a node is edited', function() {
-    var action = bookmarks.actions.editBookmark('2', {title: 'b'});
-    var nextState = bookmarks.NodeState.updateNodes(initialState, action);
+    action = bookmarks.actions.editBookmark('2', {title: 'b'});
+    state = bookmarks.NodeState.updateNodes(state, action);
 
-    assertEquals('b', nextState['2'].title);
-    assertEquals('a.com', nextState['2'].url);
+    assertEquals('b', state['2'].title);
+    assertEquals('a.com', state['2'].url);
 
     action = bookmarks.actions.editBookmark('2', {title: 'c', url: 'c.com'});
-    nextState = bookmarks.NodeState.updateNodes(nextState, action);
+    state = bookmarks.NodeState.updateNodes(state, action);
 
-    assertEquals('c', nextState['2'].title);
-    assertEquals('c.com', nextState['2'].url);
+    assertEquals('c', state['2'].title);
+    assertEquals('c.com', state['2'].url);
 
     action = bookmarks.actions.editBookmark('4', {title: 'folder'});
-    nextState = bookmarks.NodeState.updateNodes(nextState, action);
+    state = bookmarks.NodeState.updateNodes(state, action);
 
-    assertEquals('folder', nextState['4'].title);
-    assertEquals(undefined, nextState['4'].url);
+    assertEquals('folder', state['4'].title);
+    assertEquals(undefined, state['4'].url);
 
     // Cannot edit URL of a folder:
     action = bookmarks.actions.editBookmark('4', {url: 'folder.com'});
-    nextState = bookmarks.NodeState.updateNodes(nextState, action);
+    state = bookmarks.NodeState.updateNodes(state, action);
 
-    assertEquals('folder', nextState['4'].title);
-    assertEquals(undefined, nextState['4'].url);
+    assertEquals('folder', state['4'].title);
+    assertEquals(undefined, state['4'].url);
   });
 
   test('updates when a node is deleted', function() {
-    var action = bookmarks.actions.removeBookmark('3', '1', 1);
-    var nextState = bookmarks.NodeState.updateNodes(initialState, action);
+    action = bookmarks.actions.removeBookmark('3', '1', 1);
+    state = bookmarks.NodeState.updateNodes(state, action);
 
-    assertDeepEquals(['2', '4'], nextState['1'].children);
+    assertDeepEquals(['2', '4'], state['1'].children);
 
     // TODO(tsergeant): Deleted nodes should be removed from the nodes map
     // entirely.
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 565e212..9bb396a 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -759,6 +759,11 @@
   browsePreload: 'chrome://md-settings/privacy_page/privacy_page.html',
 
   /** @override */
+  commandLineSwitches: [{
+    switchName: 'enable-site-settings',
+  }],
+
+  /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     // TODO(dbeam): split these up.
     'category_default_setting_tests.js',
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js
index 852a35a93..ba0bcf6 100644
--- a/chrome/test/data/webui/settings/site_list_tests.js
+++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -650,7 +650,6 @@
         setUpCategory(contentType, settings.PermissionValues.ALLOW, prefs);
         return browserProxy.whenCalled('getExceptionList').then(
             function(actualContentType) {
-              testElement.enableSiteSettings_ = true;
               assertEquals(contentType, actualContentType);
 
               // Required for firstItem to be found below.
@@ -781,7 +780,6 @@
               var resolver = new PromiseResolver();
               testElement.async(resolver.resolve);
               return resolver.promise.then(function() {
-                testElement.enableSiteSettings_ = true;
                 // All Sites calls getExceptionList for all categories, starting
                 // with Cookies.
                 assertEquals(
@@ -825,7 +823,6 @@
               var resolver = new PromiseResolver();
               testElement.async(resolver.resolve);
               return resolver.promise.then(function() {
-                testElement.enableSiteSettings_ = true;
                 // All Sites calls getExceptionList for all categories, starting
                 // with Cookies.
                 assertEquals(
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 00256732..6523e80 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -85,6 +85,10 @@
                        base::File patch_file,
                        base::File output_file,
                        const PatchFileBsdiffCallback& callback) override {
+    DCHECK(input_file.IsValid());
+    DCHECK(patch_file.IsValid());
+    DCHECK(output_file.IsValid());
+
     const int patch_result_status = bsdiff::ApplyBinaryPatch(
         std::move(input_file), std::move(patch_file), std::move(output_file));
     callback.Run(patch_result_status);
@@ -94,6 +98,10 @@
                           base::File patch_file,
                           base::File output_file,
                           const PatchFileCourgetteCallback& callback) override {
+    DCHECK(input_file.IsValid());
+    DCHECK(patch_file.IsValid());
+    DCHECK(output_file.IsValid());
+
     const int patch_result_status = courgette::ApplyEnsemblePatch(
         std::move(input_file), std::move(patch_file), std::move(output_file));
     callback.Run(patch_result_status);
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index aff4184..d88ee0a 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -107,6 +107,15 @@
           "CastMediaBlockerBrowserTest.Audio_BlockUnblock",
         ]
       }
+
+      # TODO(mbjorge): Temporarily disable AudioPlaybackWavPcm because it is
+      # failing on device. As part of an effort to re-green the unittests,
+      # current (2017-03-15) failures are being disabled to get back to a
+      # green state. Re-enable once the tests have been fixed.
+      # b/36239152, hotlist/461351
+      if (enable_assistant && !is_cast_desktop_build) {
+        gtest_excludes += [ "CastNavigationBrowserTest.AudioPlaybackWavPcm" ]
+      }
     }
     filters += [ cast_shell_browsertests_filter ]
 
diff --git a/components/arc/test/fake_app_instance.cc b/components/arc/test/fake_app_instance.cc
index 5ffa05c..e9555094 100644
--- a/components/arc/test/fake_app_instance.cc
+++ b/components/arc/test/fake_app_instance.cc
@@ -119,6 +119,12 @@
   return true;
 }
 
+void FakeAppInstance::GenerateAndSendBadIcon(const mojom::AppInfo& app,
+                                             mojom::ScaleFactor scale_factor) {
+  std::vector<uint8_t> badIcon(10, 1);
+  app_host_->OnAppIcon(app.package_name, app.activity, scale_factor, badIcon);
+}
+
 bool FakeAppInstance::GetFakeIcon(mojom::ScaleFactor scale_factor,
                                   std::string* png_data_as_string) {
   CHECK(png_data_as_string != nullptr);
diff --git a/components/arc/test/fake_app_instance.h b/components/arc/test/fake_app_instance.h
index 4557cad..b4d5a29f 100644
--- a/components/arc/test/fake_app_instance.h
+++ b/components/arc/test/fake_app_instance.h
@@ -125,6 +125,8 @@
   bool GenerateAndSendIcon(const mojom::AppInfo& app,
                            mojom::ScaleFactor scale_factor,
                            std::string* png_data_as_string);
+  void GenerateAndSendBadIcon(const mojom::AppInfo& app,
+                              mojom::ScaleFactor scale_factor);
   void SendInstallShortcut(const mojom::ShortcutInfo& shortcuts);
   void SendInstallShortcuts(const std::vector<mojom::ShortcutInfo>& shortcuts);
   void SetTaskInfo(int32_t task_id,
diff --git a/components/crash/content/app/crashpad.cc b/components/crash/content/app/crashpad.cc
index 76f6734..dcec34684 100644
--- a/components/crash/content/app/crashpad.cc
+++ b/components/crash/content/app/crashpad.cc
@@ -204,6 +204,12 @@
 }
 #endif  // OS_WIN
 
+crashpad::CrashpadClient& GetCrashpadClient() {
+  static crashpad::CrashpadClient* const client =
+      new crashpad::CrashpadClient();
+  return *client;
+}
+
 void SetUploadConsent(bool consent) {
   if (!g_database)
     return;
diff --git a/components/crash/content/app/crashpad.h b/components/crash/content/app/crashpad.h
index eeb65ac..ada0eaec 100644
--- a/components/crash/content/app/crashpad.h
+++ b/components/crash/content/app/crashpad.h
@@ -12,6 +12,15 @@
 #include <vector>
 
 #include "base/files/file_path.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_mach_port.h"
+#endif
+
+namespace crashpad {
+class CrashpadClient;
+}
 
 namespace crash_reporter {
 
@@ -55,6 +64,10 @@
                                            const std::string& process_type);
 #endif  // OS_WIN
 
+// Returns the CrashpadClient for this process. This will lazily create it if
+// it does not already exist. This is called as part of InitializeCrashpad.
+crashpad::CrashpadClient& GetCrashpadClient();
+
 // Enables or disables crash report upload, taking the given consent to upload
 // into account. Consent may be ignored, uploads may not be enabled even with
 // consent, but will only be enabled without consent when policy enforces crash
diff --git a/components/crash/content/app/crashpad_mac.mm b/components/crash/content/app/crashpad_mac.mm
index 7df66ea..2cabccb 100644
--- a/components/crash/content/app/crashpad_mac.mm
+++ b/components/crash/content/app/crashpad_mac.mm
@@ -90,15 +90,9 @@
             "--reset-own-crash-exception-port-to-system-default");
       }
 
-      crashpad::CrashpadClient crashpad_client;
-      bool result = crashpad_client.StartHandler(handler_path,
-                                                 database_path,
-                                                 metrics_path,
-                                                 url,
-                                                 process_annotations,
-                                                 arguments,
-                                                 true,
-                                                 false);
+      bool result = GetCrashpadClient().StartHandler(
+          handler_path, database_path, metrics_path, url, process_annotations,
+          arguments, true, false);
 
       // If this is an initial client that's not the browser process, it's
       // important to sever the connection to any existing handler. If
diff --git a/components/crash/content/app/crashpad_win.cc b/components/crash/content/app/crashpad_win.cc
index 6d01b28..fa50b01 100644
--- a/components/crash/content/app/crashpad_win.cc
+++ b/components/crash/content/app/crashpad_win.cc
@@ -9,7 +9,6 @@
 #include "base/debug/crash_logging.h"
 #include "base/environment.h"
 #include "base/files/file_util.h"
-#include "base/lazy_instance.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
@@ -26,13 +25,6 @@
 namespace crash_reporter {
 namespace internal {
 
-namespace {
-
-base::LazyInstance<crashpad::CrashpadClient>::Leaky g_crashpad_client =
-    LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
 void GetPlatformCrashpadAnnotations(
     std::map<std::string, std::string>* annotations) {
   CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
@@ -116,20 +108,19 @@
       exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
     }
 
-    g_crashpad_client.Get().StartHandler(exe_file, database_path, metrics_path,
-                                         url, process_annotations, arguments,
-                                         false, false);
+    GetCrashpadClient().StartHandler(exe_file, database_path, metrics_path, url,
+                                     process_annotations, arguments, false,
+                                     false);
 
     // If we're the browser, push the pipe name into the environment so child
     // processes can connect to it. If we inherited another crashpad_handler's
     // pipe name, we'll overwrite it here.
     env->SetVar(kPipeNameVar,
-                base::UTF16ToUTF8(g_crashpad_client.Get().GetHandlerIPCPipe()));
+                base::UTF16ToUTF8(GetCrashpadClient().GetHandlerIPCPipe()));
   } else {
     std::string pipe_name_utf8;
     if (env->GetVar(kPipeNameVar, &pipe_name_utf8)) {
-      g_crashpad_client.Get().SetHandlerIPCPipe(
-          base::UTF8ToUTF16(pipe_name_utf8));
+      GetCrashpadClient().SetHandlerIPCPipe(base::UTF8ToUTF16(pipe_name_utf8));
     }
   }
 
@@ -218,7 +209,7 @@
 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so!
 int __declspec(dllexport) CrashForException(
     EXCEPTION_POINTERS* info) {
-  crash_reporter::internal::g_crashpad_client.Get().DumpAndCrash(info);
+  crash_reporter::GetCrashpadClient().DumpAndCrash(info);
   return EXCEPTION_CONTINUE_SEARCH;
 }
 
diff --git a/components/exo/display.cc b/components/exo/display.cc
index ae02686..1fbab0f 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -179,7 +179,6 @@
 
 std::unique_ptr<ShellSurface> Display::CreateRemoteShellSurface(
     Surface* surface,
-    const gfx::Point& origin,
     int container) {
   TRACE_EVENT2("exo", "Display::CreateRemoteShellSurface", "surface",
                surface->AsTracedValue(), "container", container);
@@ -193,7 +192,7 @@
   bool can_minimize = container != ash::kShellWindowId_SystemModalContainer;
 
   return base::MakeUnique<ShellSurface>(
-      surface, nullptr, ShellSurface::BoundsMode::CLIENT, origin,
+      surface, nullptr, ShellSurface::BoundsMode::CLIENT, gfx::Point(),
       true /* activatable */, can_minimize, container);
 }
 
diff --git a/components/exo/display.h b/components/exo/display.h
index 7e9ee4e..780f4040 100644
--- a/components/exo/display.h
+++ b/components/exo/display.h
@@ -76,7 +76,6 @@
   // Creates a remote shell surface for an existing surface using |container|.
   std::unique_ptr<ShellSurface> CreateRemoteShellSurface(
       Surface* surface,
-      const gfx::Point& origin,
       int container);
 
   // Creates a sub-surface for an existing surface. The sub-surface will be
diff --git a/components/exo/display_unittest.cc b/components/exo/display_unittest.cc
index 81792d8..24508ede 100644
--- a/components/exo/display_unittest.cc
+++ b/components/exo/display_unittest.cc
@@ -147,13 +147,12 @@
   // Create a remote shell surface for surface1.
   std::unique_ptr<ShellSurface> shell_surface1 =
       display->CreateRemoteShellSurface(
-          surface1.get(), gfx::Point(),
-          ash::kShellWindowId_SystemModalContainer);
+          surface1.get(), ash::kShellWindowId_SystemModalContainer);
   EXPECT_TRUE(shell_surface1);
 
   // Create a remote shell surface for surface2.
   std::unique_ptr<ShellSurface> shell_surface2 =
-      display->CreateRemoteShellSurface(surface2.get(), gfx::Point(),
+      display->CreateRemoteShellSurface(surface2.get(),
                                         ash::kShellWindowId_DefaultContainer);
   EXPECT_TRUE(shell_surface2);
 }
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index 8b61a9d..7016042 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -10,8 +10,10 @@
 #include "ash/common/shelf/wm_shelf.h"
 #include "ash/common/wm/window_resizer.h"
 #include "ash/common/wm/window_state.h"
+#include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/window_state_aura.h"
 #include "ash/wm/window_util.h"
 #include "base/logging.h"
@@ -29,6 +31,8 @@
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/class_property.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/path.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -143,6 +147,27 @@
   DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
 };
 
+// Minimal WindowResizer that unlike DefaultWindowResizer does not handle
+// dragging and resizing windows.
+class CustomWindowResizer : public ash::WindowResizer {
+ public:
+  explicit CustomWindowResizer(ash::wm::WindowState* window_state)
+      : WindowResizer(window_state), shell_(GetTarget()->GetShell()) {
+    shell_->LockCursor();
+  }
+  ~CustomWindowResizer() override { shell_->UnlockCursor(); }
+
+  // Overridden from ash::WindowResizer:
+  void Drag(const gfx::Point& location, int event_flags) override {}
+  void CompleteDrag() override {}
+  void RevertDrag() override {}
+
+ private:
+  ash::WmShell* const shell_;
+
+  DISALLOW_COPY_AND_ASSIGN(CustomWindowResizer);
+};
+
 class ShellSurfaceWidget : public views::Widget {
  public:
   explicit ShellSurfaceWidget(ShellSurface* shell_surface)
@@ -299,11 +324,14 @@
       surface_(surface),
       parent_(parent ? parent->GetWidget()->GetNativeWindow() : nullptr),
       bounds_mode_(bounds_mode),
+      primary_display_id_(
+          display::Screen::GetScreen()->GetPrimaryDisplay().id()),
       origin_(origin),
       activatable_(activatable),
       can_minimize_(can_minimize),
       container_(container) {
   WMHelper::GetInstance()->AddActivationObserver(this);
+  WMHelper::GetInstance()->AddDisplayConfigurationObserver(this);
   surface_->SetSurfaceDelegate(this);
   surface_->AddSurfaceObserver(this);
   surface_->window()->Show();
@@ -336,6 +364,7 @@
     widget_->CloseNow();
   }
   WMHelper::GetInstance()->RemoveActivationObserver(this);
+  WMHelper::GetInstance()->RemoveDisplayConfigurationObserver(this);
   if (parent_)
     parent_->RemoveObserver(this);
   if (surface_) {
@@ -542,9 +571,9 @@
 
   switch (bounds_mode_) {
     case BoundsMode::SHELL:
+    case BoundsMode::CLIENT:
       AttemptToStartDrag(HTCAPTION);
       return;
-    case BoundsMode::CLIENT:
     case BoundsMode::FIXED:
       return;
   }
@@ -644,29 +673,7 @@
 void ShellSurface::SetOrigin(const gfx::Point& origin) {
   TRACE_EVENT1("exo", "ShellSurface::SetOrigin", "origin", origin.ToString());
 
-  if (origin == origin_)
-    return;
-
-  if (bounds_mode_ != BoundsMode::CLIENT) {
-    origin_ = origin;
-    return;
-  }
-
-  // If the origin changed, give the client a chance to adjust window positions
-  // before switching to the new coordinate system. Retain the old origin by
-  // reverting the origin delta until the next configure is acknowledged.
-  gfx::Vector2d delta = origin - origin_;
-  origin_offset_ -= delta;
-  pending_origin_offset_accumulator_ += delta;
-
   origin_ = origin;
-
-  if (widget_) {
-    UpdateWidgetBounds();
-    UpdateShadow();
-  }
-
-  Configure();
 }
 
 void ShellSurface::SetActivatable(bool activatable) {
@@ -913,8 +920,8 @@
     // the client to inform us that a frame has taken the state change into
     // account and without this cross-fade animations are unreliable.
 
-    // TODO(domlaskowski): For shell surfaces whose bounds are controlled by the
-    // client, the configure callback does not yet support window state changes.
+    // TODO(domlaskowski): For BoundsMode::CLIENT, the configure callback does
+    // not yet support window state changes. See crbug.com/699746.
     if (configure_callback_.is_null() || bounds_mode_ == BoundsMode::CLIENT)
       scoped_animations_disabled_.reset(new ScopedAnimationsDisabled(this));
   }
@@ -947,8 +954,8 @@
 void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
                                          const gfx::Rect& old_bounds,
                                          const gfx::Rect& new_bounds) {
-  // TODO(domlaskowski): For shell surfaces whose bounds are controlled by the
-  // client, the configure callback does not yet support resizing.
+  // TODO(domlaskowski): For BoundsMode::CLIENT, the configure callback does not
+  // yet support resizing. See crbug.com/699746.
   if (bounds_mode_ == BoundsMode::CLIENT)
     return;
 
@@ -1013,6 +1020,39 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// WMHelper::DisplayConfigurationObserver overrides:
+
+void ShellSurface::OnDisplayConfigurationChanged() {
+  if (bounds_mode_ != BoundsMode::CLIENT)
+    return;
+
+  const display::Screen* screen = display::Screen::GetScreen();
+  int64_t primary_display_id = screen->GetPrimaryDisplay().id();
+  if (primary_display_id == primary_display_id_)
+    return;
+
+  display::Display old_primary_display;
+  if (screen->GetDisplayWithDisplayId(primary_display_id_,
+                                      &old_primary_display)) {
+    // Give the client a chance to adjust window positions before switching to
+    // the new coordinate system. Retain the old origin by reverting the origin
+    // delta until the next configure is acknowledged.
+    gfx::Vector2d delta = gfx::Point() - old_primary_display.bounds().origin();
+    origin_offset_ -= delta;
+    pending_origin_offset_accumulator_ += delta;
+
+    if (widget_) {
+      UpdateWidgetBounds();
+      UpdateShadow();
+    }
+
+    Configure();
+  }
+
+  primary_display_id_ = primary_display_id;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // ui::EventHandler overrides:
 
 void ShellSurface::OnKeyEvent(ui::KeyEvent* event) {
@@ -1021,6 +1061,9 @@
     return;
   }
 
+  // TODO(domlaskowski): For BoundsMode::CLIENT, synchronize the revert with the
+  // client, instead of having the client destroy the window on VKEY_ESCAPE. See
+  // crbug.com/699746.
   if (event->type() == ui::ET_KEY_PRESSED &&
       event->key_code() == ui::VKEY_ESCAPE) {
     EndDrag(true /* revert */);
@@ -1050,6 +1093,9 @@
 
   switch (event->type()) {
     case ui::ET_MOUSE_DRAGGED: {
+      if (bounds_mode_ == BoundsMode::CLIENT)
+        break;
+
       gfx::Point location(event->location());
       aura::Window::ConvertPointToTarget(widget_->GetNativeWindow(),
                                          widget_->GetNativeWindow()->parent(),
@@ -1203,11 +1249,11 @@
       serial = configure_callback_.Run(
           non_client_view->frame_view()->GetBoundsForClientView().size(),
           ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(),
-          IsResizing(), widget_->IsActive(), origin_);
+          IsResizing(), widget_->IsActive(), origin_offset);
     } else {
       serial = configure_callback_.Run(gfx::Size(),
                                        ash::wm::WINDOW_STATE_TYPE_NORMAL, false,
-                                       false, origin_);
+                                       false, origin_offset);
     }
   }
 
@@ -1225,6 +1271,22 @@
       << pending_configs_.size();
 }
 
+aura::Window* ShellSurface::GetDragWindow() const {
+  switch (bounds_mode_) {
+    case BoundsMode::SHELL:
+      return widget_->GetNativeWindow();
+
+    case BoundsMode::CLIENT:
+      return surface_ ? surface_->window() : nullptr;
+
+    case BoundsMode::FIXED:
+      return nullptr;
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
 void ShellSurface::AttemptToStartDrag(int component) {
   DCHECK(widget_);
 
@@ -1232,69 +1294,81 @@
   if (resizer_)
     return;
 
-  if (widget_->GetNativeWindow()->HasCapture())
+  aura::Window* window = GetDragWindow();
+  if (!window || window->HasCapture())
     return;
 
-  aura::Window* root_window = widget_->GetNativeWindow()->GetRootWindow();
-  gfx::Point drag_location =
-      root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot();
-  aura::Window::ConvertPointToTarget(
-      root_window, widget_->GetNativeWindow()->parent(), &drag_location);
+  if (bounds_mode_ == BoundsMode::SHELL) {
+    // Set the cursor before calling CreateWindowResizer(), as that will
+    // eventually call LockCursor() and prevent the cursor from changing.
+    aura::client::CursorClient* cursor_client =
+        aura::client::GetCursorClient(window->GetRootWindow());
+    DCHECK(cursor_client);
 
-  // Set the cursor before calling CreateWindowResizer(), as that will
-  // eventually call LockCursor() and prevent the cursor from changing.
-  aura::client::CursorClient* cursor_client =
-      aura::client::GetCursorClient(root_window);
-  DCHECK(cursor_client);
+    switch (component) {
+      case HTCAPTION:
+        cursor_client->SetCursor(ui::kCursorPointer);
+        break;
+      case HTTOP:
+        cursor_client->SetCursor(ui::kCursorNorthResize);
+        break;
+      case HTTOPRIGHT:
+        cursor_client->SetCursor(ui::kCursorNorthEastResize);
+        break;
+      case HTRIGHT:
+        cursor_client->SetCursor(ui::kCursorEastResize);
+        break;
+      case HTBOTTOMRIGHT:
+        cursor_client->SetCursor(ui::kCursorSouthEastResize);
+        break;
+      case HTBOTTOM:
+        cursor_client->SetCursor(ui::kCursorSouthResize);
+        break;
+      case HTBOTTOMLEFT:
+        cursor_client->SetCursor(ui::kCursorSouthWestResize);
+        break;
+      case HTLEFT:
+        cursor_client->SetCursor(ui::kCursorWestResize);
+        break;
+      case HTTOPLEFT:
+        cursor_client->SetCursor(ui::kCursorNorthWestResize);
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
 
-  switch (component) {
-    case HTCAPTION:
-      cursor_client->SetCursor(ui::kCursorPointer);
-      break;
-    case HTTOP:
-      cursor_client->SetCursor(ui::kCursorNorthResize);
-      break;
-    case HTTOPRIGHT:
-      cursor_client->SetCursor(ui::kCursorNorthEastResize);
-      break;
-    case HTRIGHT:
-      cursor_client->SetCursor(ui::kCursorEastResize);
-      break;
-    case HTBOTTOMRIGHT:
-      cursor_client->SetCursor(ui::kCursorSouthEastResize);
-      break;
-    case HTBOTTOM:
-      cursor_client->SetCursor(ui::kCursorSouthResize);
-      break;
-    case HTBOTTOMLEFT:
-      cursor_client->SetCursor(ui::kCursorSouthWestResize);
-      break;
-    case HTLEFT:
-      cursor_client->SetCursor(ui::kCursorWestResize);
-      break;
-    case HTTOPLEFT:
-      cursor_client->SetCursor(ui::kCursorNorthWestResize);
-      break;
-    default:
-      NOTREACHED();
-      break;
+    resizer_ = ash::CreateWindowResizer(ash::WmWindow::Get(window),
+                                        GetMouseLocation(), component,
+                                        aura::client::WINDOW_MOVE_SOURCE_MOUSE);
+    if (!resizer_)
+      return;
+
+    // Apply pending origin offsets and resize direction before starting a
+    // new resize operation. These can still be pending if the client has
+    // acknowledged the configure request but not yet called Commit().
+    origin_offset_ += pending_origin_offset_;
+    pending_origin_offset_ = gfx::Vector2d();
+    resize_component_ = pending_resize_component_;
+  } else {
+    DCHECK(bounds_mode_ == BoundsMode::CLIENT);
+
+    ash::wm::WindowState* window_state =
+        ash::wm::GetWindowState(widget_->GetNativeWindow());
+    DCHECK(!window_state->drag_details());
+    DCHECK(component == HTCAPTION);
+    window_state->CreateDragDetails(GetMouseLocation(), component,
+                                    aura::client::WINDOW_MOVE_SOURCE_MOUSE);
+
+    // Chained with a CustomWindowResizer, DragWindowResizer does not handle
+    // dragging. It only renders phantom windows and moves the window to the
+    // target root window when dragging ends.
+    resizer_.reset(ash::DragWindowResizer::Create(
+        new CustomWindowResizer(window_state), window_state));
   }
 
-  resizer_ = ash::CreateWindowResizer(
-      ash::WmWindow::Get(widget_->GetNativeWindow()), drag_location, component,
-      aura::client::WINDOW_MOVE_SOURCE_MOUSE);
-  if (!resizer_)
-    return;
-
-  // Apply pending origin offsets and resize direction before starting a new
-  // resize operation. These can still be pending if the client has acknowledged
-  // the configure request but not yet called Commit().
-  origin_offset_ += pending_origin_offset_;
-  pending_origin_offset_ = gfx::Vector2d();
-  resize_component_ = pending_resize_component_;
-
   WMHelper::GetInstance()->AddPreTargetHandler(this);
-  widget_->GetNativeWindow()->SetCapture();
+  window->SetCapture();
 
   // Notify client that resizing state has changed.
   if (IsResizing())
@@ -1305,6 +1379,10 @@
   DCHECK(widget_);
   DCHECK(resizer_);
 
+  aura::Window* window = GetDragWindow();
+  DCHECK(window);
+  DCHECK(window->HasCapture());
+
   bool was_resizing = IsResizing();
 
   if (revert)
@@ -1313,7 +1391,7 @@
     resizer_->CompleteDrag();
 
   WMHelper::GetInstance()->RemovePreTargetHandler(this);
-  widget_->GetNativeWindow()->ReleaseCapture();
+  window->ReleaseCapture();
   resizer_.reset();
 
   // Notify client that resizing state has changed.
@@ -1426,8 +1504,32 @@
   // should not result in a configure request.
   DCHECK(!ignore_window_bounds_changes_);
   ignore_window_bounds_changes_ = true;
-  if (widget_->GetWindowBoundsInScreen() != new_widget_bounds)
-    widget_->SetBounds(new_widget_bounds);
+  const gfx::Rect widget_bounds = widget_->GetWindowBoundsInScreen();
+  if (widget_bounds != new_widget_bounds) {
+    if (bounds_mode_ != BoundsMode::CLIENT || !resizer_) {
+      widget_->SetBounds(new_widget_bounds);
+      UpdateSurfaceBounds();
+    } else {
+      // TODO(domlaskowski): Synchronize window state transitions with the
+      // client, and abort client-side dragging on transition to fullscreen. See
+      // crbug.com/699746.
+      DLOG_IF(ERROR, widget_bounds.size() != new_widget_bounds.size())
+          << "Window size changed during client-driven drag";
+
+      // Convert from screen to display coordinates.
+      gfx::Point origin = new_widget_bounds.origin();
+      wm::ConvertPointFromScreen(widget_->GetNativeWindow()->parent(), &origin);
+      new_widget_bounds.set_origin(origin);
+
+      // Move the window relative to the current display.
+      widget_->GetNativeWindow()->SetBounds(new_widget_bounds);
+      UpdateSurfaceBounds();
+
+      // Render phantom windows when beyond the current display.
+      resizer_->Drag(GetMouseLocation(), 0);
+    }
+  }
+
   ignore_window_bounds_changes_ = false;
 }
 
@@ -1591,4 +1693,13 @@
   }
 }
 
+gfx::Point ShellSurface::GetMouseLocation() const {
+  aura::Window* const root_window = widget_->GetNativeWindow()->GetRootWindow();
+  gfx::Point location =
+      root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot();
+  aura::Window::ConvertPointToTarget(
+      root_window, widget_->GetNativeWindow()->parent(), &location);
+  return location;
+}
+
 }  // namespace exo
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
index b090cae..c22947d 100644
--- a/components/exo/shell_surface.h
+++ b/components/exo/shell_surface.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_EXO_SHELL_SURFACE_H_
 #define COMPONENTS_EXO_SHELL_SURFACE_H_
 
+#include <cstdint>
 #include <deque>
 #include <memory>
 #include <string>
@@ -45,7 +46,8 @@
                      public ash::wm::WindowStateObserver,
                      public aura::WindowObserver,
                      public WMHelper::ActivationObserver,
-                     public WMHelper::AccessibilityObserver {
+                     public WMHelper::AccessibilityObserver,
+                     public WMHelper::DisplayConfigurationObserver {
  public:
   enum class BoundsMode { SHELL, CLIENT, FIXED };
 
@@ -98,7 +100,7 @@
                               ash::wm::WindowStateType state_type,
                               bool resizing,
                               bool activated,
-                              const gfx::Point& origin)>;
+                              const gfx::Vector2d& origin_offset)>;
   void set_configure_callback(const ConfigureCallback& configure_callback) {
     configure_callback_ = configure_callback;
   }
@@ -250,6 +252,9 @@
   // Overridden from WMHelper::AccessibilityObserver:
   void OnAccessibilityModeChanged() override;
 
+  // Overridden from WMHelper::DisplayConfigurationObserver:
+  void OnDisplayConfigurationChanged() override;
+
   // Overridden from ui::EventHandler:
   void OnKeyEvent(ui::KeyEvent* event) override;
   void OnMouseEvent(ui::MouseEvent* event) override;
@@ -280,6 +285,9 @@
   // Asks the client to configure its surface.
   void Configure();
 
+  // Returns the window that has capture during dragging.
+  aura::Window* GetDragWindow() const;
+
   // Attempt to start a drag operation. The type of drag operation to start is
   // determined by |component|.
   void AttemptToStartDrag(int component);
@@ -310,10 +318,14 @@
   // Applies |system_modal_| to |widget_|.
   void UpdateSystemModal();
 
+  // In the coordinate system of the parent root window.
+  gfx::Point GetMouseLocation() const;
+
   views::Widget* widget_ = nullptr;
   Surface* surface_;
   aura::Window* parent_;
   const BoundsMode bounds_mode_;
+  int64_t primary_display_id_;
   gfx::Point origin_;
   bool activatable_ = true;
   const bool can_minimize_;
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc
index 988a680..a7d018e 100644
--- a/components/exo/shell_surface_unittest.cc
+++ b/components/exo/shell_surface_unittest.cc
@@ -43,7 +43,7 @@
                              ash::wm::WindowStateType state_type,
                              bool resizing,
                              bool activated,
-                             const gfx::Point& origin) {
+                             const gfx::Vector2d& origin_offset) {
   EXPECT_EQ(ash::wm::WINDOW_STATE_TYPE_FULLSCREEN, state_type);
   return serial;
 }
@@ -370,7 +370,7 @@
                    ash::wm::WindowStateType state_type,
                    bool resizing,
                    bool activated,
-                   const gfx::Point& origin) {
+                   const gfx::Vector2d& origin_offset) {
   *suggested_size = size;
   *has_state_type = state_type;
   *is_resizing = resizing;
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index b7a37dc..85a4b9a 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -1008,7 +1008,7 @@
     ash::wm::WindowStateType state_type,
     bool resizing,
     bool activated,
-    const gfx::Point& origin) {
+    const gfx::Vector2d& origin_offset) {
   wl_shell_surface_send_configure(resource, WL_SHELL_SURFACE_RESIZE_NONE,
                                   size.width(), size.height());
   wl_client_flush(wl_resource_get_client(resource));
@@ -1534,7 +1534,7 @@
     ash::wm::WindowStateType state_type,
     bool resizing,
     bool activated,
-    const gfx::Point& origin) {
+    const gfx::Vector2d& origin_offset) {
   wl_array states;
   wl_array_init(&states);
   if (state_type == ash::wm::WINDOW_STATE_TYPE_MAXIMIZED)
@@ -1672,7 +1672,7 @@
     ash::wm::WindowStateType state_type,
     bool resizing,
     bool activated,
-    const gfx::Point& origin) {
+    const gfx::Vector2d& origin_offset) {
   wl_array states;
   wl_array_init(&states);
   if (state_type == ash::wm::WINDOW_STATE_TYPE_MAXIMIZED)
@@ -1985,7 +1985,7 @@
 }
 
 void remote_surface_move(wl_client* client, wl_resource* resource) {
-  NOTIMPLEMENTED();
+  GetUserDataAs<ShellSurface>(resource)->Move();
 }
 
 const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
@@ -2059,7 +2059,7 @@
 
   std::unique_ptr<ShellSurface> CreateShellSurface(Surface* surface,
                                                    int container) {
-    return display_->CreateRemoteShellSurface(surface, gfx::Point(), container);
+    return display_->CreateRemoteShellSurface(surface, container);
   }
 
   std::unique_ptr<NotificationSurface> CreateNotificationSurface(
@@ -2069,9 +2069,20 @@
   }
 
   // Overridden from display::DisplayObserver:
+  void OnDisplayAdded(const display::Display& new_display) override {
+    if (IsMultiDisplaySupported())
+      ScheduleSendDisplayMetrics(0);
+  }
+
+  void OnDisplayRemoved(const display::Display& old_display) override {
+    if (IsMultiDisplaySupported())
+      ScheduleSendDisplayMetrics(0);
+  }
+
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override {
-    if (display::Screen::GetScreen()->GetPrimaryDisplay().id() != display.id())
+    if (!IsMultiDisplaySupported() &&
+        display::Screen::GetScreen()->GetPrimaryDisplay().id() != display.id())
       return;
 
     // No need to update when a primary display has changed without bounds
@@ -2116,9 +2127,31 @@
     needs_send_display_metrics_ = false;
 
     const display::Screen* screen = display::Screen::GetScreen();
-    const display::Display primary_display = screen->GetPrimaryDisplay();
+    display::Display primary_display = screen->GetPrimaryDisplay();
 
-    const gfx::Insets& work_area_insets = primary_display.GetWorkAreaInsets();
+    if (IsMultiDisplaySupported()) {
+      for (const auto& display : screen->GetAllDisplays()) {
+        const gfx::Rect& bounds = display.bounds();
+        const gfx::Insets& insets = display.GetWorkAreaInsets();
+
+        zcr_remote_shell_v1_send_workspace(
+            remote_shell_resource_,
+            static_cast<uint32_t>(display.id() >> 32),
+            static_cast<uint32_t>(display.id()),
+            bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+            insets.left(), insets.top(), insets.right(), insets.bottom());
+      }
+
+      zcr_remote_shell_v1_send_configure(
+          remote_shell_resource_,
+          static_cast<uint32_t>(primary_display.id() >> 32),
+          static_cast<uint32_t>(primary_display.id()),
+          OutputTransform(primary_display.rotation()),
+          wl_fixed_from_double(primary_display.device_scale_factor()),
+          layout_mode_);
+    }
+
+    const gfx::Insets& insets = primary_display.GetWorkAreaInsets();
 
     zcr_remote_shell_v1_send_configuration_changed(
         remote_shell_resource_,
@@ -2126,8 +2159,8 @@
         primary_display.size().height(),
         OutputTransform(primary_display.rotation()),
         wl_fixed_from_double(primary_display.device_scale_factor()),
-        work_area_insets.left(), work_area_insets.top(),
-        work_area_insets.right(), work_area_insets.bottom(), layout_mode_);
+        insets.left(), insets.top(), insets.right(), insets.bottom(),
+        layout_mode_);
     wl_client_flush(wl_resource_get_client(remote_shell_resource_));
   }
 
@@ -2237,12 +2270,14 @@
     ash::wm::WindowStateType state_type,
     bool resizing,
     bool activated,
-    const gfx::Point& origin) {
+    const gfx::Vector2d& origin_offset) {
   wl_array states;
   wl_array_init(&states);
   uint32_t serial = wl_display_next_serial(
       wl_client_get_display(wl_resource_get_client(resource)));
-  zcr_remote_surface_v1_send_configure(resource, origin.x(), origin.y(),
+  zcr_remote_surface_v1_send_configure(resource,
+                                       origin_offset.x(),
+                                       origin_offset.y(),
                                        &states, serial);
   wl_client_flush(wl_resource_get_client(resource));
   wl_array_release(&states);
@@ -2316,7 +2351,7 @@
     remote_shell_destroy, remote_shell_get_remote_surface,
     remote_shell_get_notification_surface};
 
-const uint32_t remote_shell_version = 2;
+const uint32_t remote_shell_version = 3;
 
 void bind_remote_shell(wl_client* client,
                        void* data,
diff --git a/components/exo/wm_helper.cc b/components/exo/wm_helper.cc
index 009d4cc..7f0cfcf 100644
--- a/components/exo/wm_helper.cc
+++ b/components/exo/wm_helper.cc
@@ -79,6 +79,16 @@
   input_device_event_observers_.RemoveObserver(observer);
 }
 
+void WMHelper::AddDisplayConfigurationObserver(
+    DisplayConfigurationObserver* observer) {
+  display_config_observers_.AddObserver(observer);
+}
+
+void WMHelper::RemoveDisplayConfigurationObserver(
+    DisplayConfigurationObserver* observer) {
+  display_config_observers_.RemoveObserver(observer);
+}
+
 void WMHelper::NotifyWindowActivated(aura::Window* gained_active,
                                      aura::Window* lost_active) {
   for (ActivationObserver& observer : activation_observers_)
@@ -126,4 +136,9 @@
     observer.OnKeyboardDeviceConfigurationChanged();
 }
 
+void WMHelper::NotifyDisplayConfigurationChanged() {
+  for (DisplayConfigurationObserver& observer : display_config_observers_)
+    observer.OnDisplayConfigurationChanged();
+}
+
 }  // namespace exo
diff --git a/components/exo/wm_helper.h b/components/exo/wm_helper.h
index 9ce432b8..ad0aeb6a 100644
--- a/components/exo/wm_helper.h
+++ b/components/exo/wm_helper.h
@@ -79,6 +79,14 @@
     virtual ~InputDeviceEventObserver() {}
   };
 
+  class DisplayConfigurationObserver {
+   public:
+    virtual void OnDisplayConfigurationChanged() = 0;
+
+   protected:
+    virtual ~DisplayConfigurationObserver() {}
+  };
+
   virtual ~WMHelper();
 
   static void SetInstance(WMHelper* helper);
@@ -96,6 +104,9 @@
   void RemoveAccessibilityObserver(AccessibilityObserver* observer);
   void AddInputDeviceEventObserver(InputDeviceEventObserver* observer);
   void RemoveInputDeviceEventObserver(InputDeviceEventObserver* observer);
+  void AddDisplayConfigurationObserver(DisplayConfigurationObserver* observer);
+  void RemoveDisplayConfigurationObserver(
+      DisplayConfigurationObserver* observer);
 
   virtual const display::ManagedDisplayInfo GetDisplayInfo(
       int64_t display_id) const = 0;
@@ -126,6 +137,7 @@
   void NotifyMaximizeModeEnded();
   void NotifyAccessibilityModeChanged();
   void NotifyKeyboardDeviceConfigurationChanged();
+  void NotifyDisplayConfigurationChanged();
 
  private:
   base::ObserverList<ActivationObserver> activation_observers_;
@@ -134,6 +146,7 @@
   base::ObserverList<MaximizeModeObserver> maximize_mode_observers_;
   base::ObserverList<AccessibilityObserver> accessibility_observers_;
   base::ObserverList<InputDeviceEventObserver> input_device_event_observers_;
+  base::ObserverList<DisplayConfigurationObserver> display_config_observers_;
 
   DISALLOW_COPY_AND_ASSIGN(WMHelper);
 };
diff --git a/components/exo/wm_helper_ash.cc b/components/exo/wm_helper_ash.cc
index 6c305117..4fefebd5 100644
--- a/components/exo/wm_helper_ash.cc
+++ b/components/exo/wm_helper_ash.cc
@@ -23,6 +23,7 @@
 WMHelperAsh::WMHelperAsh() {
   ash::Shell::GetInstance()->AddShellObserver(this);
   ash::Shell::GetInstance()->activation_client()->AddObserver(this);
+  ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
   aura::client::FocusClient* focus_client =
       aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
   focus_client->AddObserver(this);
@@ -36,6 +37,7 @@
   aura::client::FocusClient* focus_client =
       aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
   focus_client->RemoveObserver(this);
+  ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
   ash::Shell::GetInstance()->activation_client()->RemoveObserver(this);
   ash::Shell::GetInstance()->RemoveShellObserver(this);
   ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
@@ -145,6 +147,10 @@
   NotifyMaximizeModeEnded();
 }
 
+void WMHelperAsh::OnDisplayConfigurationChanged() {
+  NotifyDisplayConfigurationChanged();
+}
+
 void WMHelperAsh::OnKeyboardDeviceConfigurationChanged() {
   NotifyKeyboardDeviceConfigurationChanged();
 }
diff --git a/components/exo/wm_helper_ash.h b/components/exo/wm_helper_ash.h
index b41e750..8b07843d 100644
--- a/components/exo/wm_helper_ash.h
+++ b/components/exo/wm_helper_ash.h
@@ -7,6 +7,7 @@
 
 #include "ash/common/shell_observer.h"
 #include "ash/common/system/accessibility_observer.h"
+#include "ash/display/window_tree_host_manager.h"
 #include "base/macros.h"
 #include "components/exo/wm_helper.h"
 #include "ui/aura/client/cursor_client_observer.h"
@@ -23,6 +24,7 @@
                     public aura::client::CursorClientObserver,
                     public ash::AccessibilityObserver,
                     public ash::ShellObserver,
+                    public ash::WindowTreeHostManager::Observer,
                     public ui::InputDeviceEventObserver {
  public:
   WMHelperAsh();
@@ -67,6 +69,9 @@
   void OnMaximizeModeEnding() override;
   void OnMaximizeModeEnded() override;
 
+  // Overriden from ash::WindowTreeHostManager::Observer:
+  void OnDisplayConfigurationChanged() override;
+
   // Overriden from ui::InputDeviceEventObserver:
   void OnKeyboardDeviceConfigurationChanged() override;
 
diff --git a/components/metrics/metrics_state_manager.cc b/components/metrics/metrics_state_manager.cc
index 4cae20d3..83d62b5f 100644
--- a/components/metrics/metrics_state_manager.cc
+++ b/components/metrics/metrics_state_manager.cc
@@ -92,16 +92,10 @@
     client_id_.swap(client_id_from_prefs);
   }
 
-  if (!client_id_.empty()) {
-    // It is technically sufficient to only save a backup of the client id when
-    // it is initially generated below, but since the backup was only introduced
-    // in M38, seed it explicitly from here for some time.
-    BackUpCurrentClientInfo();
+  if (!client_id_.empty())
     return;
-  }
 
-  const std::unique_ptr<ClientInfo> client_info_backup =
-      LoadClientInfoAndMaybeMigrate();
+  const std::unique_ptr<ClientInfo> client_info_backup = LoadClientInfo();
   if (client_info_backup) {
     client_id_ = client_info_backup->client_id;
 
@@ -233,36 +227,12 @@
   store_client_info_.Run(client_info);
 }
 
-std::unique_ptr<ClientInfo>
-MetricsStateManager::LoadClientInfoAndMaybeMigrate() {
+std::unique_ptr<ClientInfo> MetricsStateManager::LoadClientInfo() {
   std::unique_ptr<ClientInfo> client_info = load_client_info_.Run();
 
-  // Prior to 2014-07, the client ID was stripped of its dashes before being
-  // saved. Migrate back to a proper GUID if this is the case. This migration
-  // code can be removed in M41+.
-  const size_t kGUIDLengthWithoutDashes = 32U;
-  if (client_info &&
-      client_info->client_id.length() == kGUIDLengthWithoutDashes) {
-    DCHECK(client_info->client_id.find('-') == std::string::npos);
-
-    std::string client_id_with_dashes;
-    client_id_with_dashes.reserve(kGUIDLengthWithoutDashes + 4U);
-    std::string::const_iterator client_id_it = client_info->client_id.begin();
-    for (size_t i = 0; i < kGUIDLengthWithoutDashes + 4U; ++i) {
-      if (i == 8U || i == 13U || i == 18U || i == 23U) {
-        client_id_with_dashes.push_back('-');
-      } else {
-        client_id_with_dashes.push_back(*client_id_it);
-        ++client_id_it;
-      }
-    }
-    DCHECK(client_id_it == client_info->client_id.end());
-    client_info->client_id.assign(client_id_with_dashes);
-  }
-
-  // The GUID retrieved (and possibly fixed above) should be valid unless
-  // retrieval failed. If not, return nullptr. This will result in a new GUID
-  // being generated by the calling function ForceClientIdCreation().
+  // The GUID retrieved should be valid unless retrieval failed.
+  // If not, return nullptr. This will result in a new GUID being generated by
+  // the calling function ForceClientIdCreation().
   if (client_info && !base::IsValidGUID(client_info->client_id))
     return nullptr;
 
diff --git a/components/metrics/metrics_state_manager.h b/components/metrics/metrics_state_manager.h
index afa146b9..ca60fd4 100644
--- a/components/metrics/metrics_state_manager.h
+++ b/components/metrics/metrics_state_manager.h
@@ -118,9 +118,8 @@
   // Backs up the current client info via |store_client_info_|.
   void BackUpCurrentClientInfo();
 
-  // Loads the client info via |load_client_info_| and potentially migrates it
-  // before returning it if it comes back in its old form.
-  std::unique_ptr<ClientInfo> LoadClientInfoAndMaybeMigrate();
+  // Loads the client info via |load_client_info_|.
+  std::unique_ptr<ClientInfo> LoadClientInfo();
 
   // Returns the low entropy source for this client. This is a random value
   // that is non-identifying amongst browser clients. This method will
diff --git a/components/metrics/metrics_state_manager_unittest.cc b/components/metrics/metrics_state_manager_unittest.cc
index bbef26b1..a55c2fa 100644
--- a/components/metrics/metrics_state_manager_unittest.cc
+++ b/components/metrics/metrics_state_manager_unittest.cc
@@ -28,7 +28,8 @@
 class MetricsStateManagerTest : public testing::Test {
  public:
   MetricsStateManagerTest()
-      : enabled_state_provider_(new TestEnabledStateProvider(false, false)) {
+      : test_begin_time_(base::Time::Now().ToTimeT()),
+        enabled_state_provider_(new TestEnabledStateProvider(false, false)) {
     MetricsService::RegisterPrefs(prefs_.registry());
   }
 
@@ -47,6 +48,21 @@
     enabled_state_provider_->set_enabled(true);
   }
 
+  void SetClientInfoPrefs(const ClientInfo& client_info) {
+    prefs_.SetString(prefs::kMetricsClientID, client_info.client_id);
+    prefs_.SetInt64(prefs::kInstallDate, client_info.installation_date);
+    prefs_.SetInt64(prefs::kMetricsReportingEnabledTimestamp,
+                    client_info.reporting_enabled_date);
+  }
+
+  void SetFakeClientInfoBackup(const ClientInfo& client_info) {
+    fake_client_info_backup_.reset(new ClientInfo);
+    fake_client_info_backup_->client_id = client_info.client_id;
+    fake_client_info_backup_->installation_date = client_info.installation_date;
+    fake_client_info_backup_->reporting_enabled_date =
+        client_info.reporting_enabled_date;
+  }
+
  protected:
   TestingPrefServiceSimple prefs_;
 
@@ -58,6 +74,8 @@
   // MetricsStateManager.
   std::unique_ptr<ClientInfo> fake_client_info_backup_;
 
+  const int64_t test_begin_time_;
+
  private:
   // Stores the |client_info| in |stored_client_info_backup_| for verification
   // by the tests later.
@@ -222,11 +240,6 @@
   const int64_t kFakeInstallationDate = 12345;
   prefs_.SetInt64(prefs::kInstallDate, kFakeInstallationDate);
 
-  const int64_t test_begin_time = base::Time::Now().ToTimeT();
-
-  // Holds ClientInfo from previous scoped test for extra checks.
-  std::unique_ptr<ClientInfo> previous_client_info;
-
   {
     std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
 
@@ -241,7 +254,7 @@
     state_manager->ForceClientIdCreation();
     EXPECT_NE(std::string(), state_manager->client_id());
     EXPECT_GE(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp),
-              test_begin_time);
+              test_begin_time_);
 
     ASSERT_TRUE(stored_client_info_backup_);
     EXPECT_EQ(state_manager->client_id(),
@@ -250,70 +263,82 @@
               stored_client_info_backup_->installation_date);
     EXPECT_EQ(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp),
               stored_client_info_backup_->reporting_enabled_date);
-
-    previous_client_info = std::move(stored_client_info_backup_);
   }
+}
+
+TEST_F(MetricsStateManagerTest, LoadPrefs) {
+  ClientInfo client_info;
+  client_info.client_id = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEF";
+  client_info.installation_date = 1112;
+  client_info.reporting_enabled_date = 2223;
+  SetClientInfoPrefs(client_info);
 
   EnableMetricsReporting();
-
   {
+    EXPECT_FALSE(fake_client_info_backup_);
     EXPECT_FALSE(stored_client_info_backup_);
 
     std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
 
     // client_id should be auto-obtained from the constructor when metrics
     // reporting is enabled.
-    EXPECT_EQ(previous_client_info->client_id, state_manager->client_id());
+    EXPECT_EQ(client_info.client_id, state_manager->client_id());
 
-    // The backup should also be refreshed when the client id re-initialized.
-    ASSERT_TRUE(stored_client_info_backup_);
-    EXPECT_EQ(previous_client_info->client_id,
-              stored_client_info_backup_->client_id);
-    EXPECT_EQ(kFakeInstallationDate,
-              stored_client_info_backup_->installation_date);
-    EXPECT_EQ(previous_client_info->reporting_enabled_date,
-              stored_client_info_backup_->reporting_enabled_date);
+    // The backup should not be modified.
+    ASSERT_FALSE(stored_client_info_backup_);
 
     // Re-forcing client id creation shouldn't cause another backup and
     // shouldn't affect the existing client id.
-    stored_client_info_backup_.reset();
     state_manager->ForceClientIdCreation();
     EXPECT_FALSE(stored_client_info_backup_);
-    EXPECT_EQ(previous_client_info->client_id, state_manager->client_id());
+    EXPECT_EQ(client_info.client_id, state_manager->client_id());
   }
+}
 
-  const int64_t kBackupInstallationDate = 1111;
-  const int64_t kBackupReportingEnabledDate = 2222;
-  const char kBackupClientId[] = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
-  fake_client_info_backup_.reset(new ClientInfo);
-  fake_client_info_backup_->client_id = kBackupClientId;
-  fake_client_info_backup_->installation_date = kBackupInstallationDate;
-  fake_client_info_backup_->reporting_enabled_date =
-      kBackupReportingEnabledDate;
+TEST_F(MetricsStateManagerTest, PreferPrefs) {
+  ClientInfo client_info;
+  client_info.client_id = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEF";
+  client_info.installation_date = 1112;
+  client_info.reporting_enabled_date = 2223;
+  SetClientInfoPrefs(client_info);
 
+  ClientInfo client_info2;
+  client_info2.client_id = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
+  client_info2.installation_date = 1111;
+  client_info2.reporting_enabled_date = 2222;
+  SetFakeClientInfoBackup(client_info2);
+
+  EnableMetricsReporting();
   {
-    // The existence of a backup should result in the same behaviour as
-    // before if we already have a client id.
+    // The backup should be ignored if we already have a client id.
 
     EXPECT_FALSE(stored_client_info_backup_);
 
     std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
-    EXPECT_EQ(previous_client_info->client_id, state_manager->client_id());
+    EXPECT_EQ(client_info.client_id, state_manager->client_id());
 
-    // The backup should also be refreshed when the client id re-initialized.
-    ASSERT_TRUE(stored_client_info_backup_);
-    EXPECT_EQ(previous_client_info->client_id,
-              stored_client_info_backup_->client_id);
-    EXPECT_EQ(kFakeInstallationDate,
-              stored_client_info_backup_->installation_date);
-    EXPECT_EQ(previous_client_info->reporting_enabled_date,
-              stored_client_info_backup_->reporting_enabled_date);
-    stored_client_info_backup_.reset();
+    // The backup should not be modified.
+    ASSERT_FALSE(stored_client_info_backup_);
   }
+}
+
+TEST_F(MetricsStateManagerTest, RestoreBackup) {
+  ClientInfo client_info;
+  client_info.client_id = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEF";
+  client_info.installation_date = 1112;
+  client_info.reporting_enabled_date = 2223;
+  SetClientInfoPrefs(client_info);
+
+  ClientInfo client_info2;
+  client_info2.client_id = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
+  client_info2.installation_date = 1111;
+  client_info2.reporting_enabled_date = 2222;
+  SetFakeClientInfoBackup(client_info2);
 
   prefs_.ClearPref(prefs::kMetricsClientID);
   prefs_.ClearPref(prefs::kMetricsReportingEnabledTimestamp);
 
+  EnableMetricsReporting();
   {
     // The backup should kick in if the client id has gone missing. It should
     // replace remaining and missing dates as well.
@@ -321,62 +346,46 @@
     EXPECT_FALSE(stored_client_info_backup_);
 
     std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
-    EXPECT_EQ(kBackupClientId, state_manager->client_id());
-    EXPECT_EQ(kBackupInstallationDate, prefs_.GetInt64(prefs::kInstallDate));
-    EXPECT_EQ(kBackupReportingEnabledDate,
+    EXPECT_EQ(client_info2.client_id, state_manager->client_id());
+    EXPECT_EQ(client_info2.installation_date,
+              prefs_.GetInt64(prefs::kInstallDate));
+    EXPECT_EQ(client_info2.reporting_enabled_date,
               prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp));
 
     EXPECT_TRUE(stored_client_info_backup_);
-    stored_client_info_backup_.reset();
   }
+}
 
-  const char kNoDashesBackupClientId[] = "AAAAAAAABBBBCCCCDDDDEEEEEEEEEEEE";
-  fake_client_info_backup_.reset(new ClientInfo);
-  fake_client_info_backup_->client_id = kNoDashesBackupClientId;
+TEST_F(MetricsStateManagerTest, ResetBackup) {
+  ClientInfo client_info;
+  client_info.client_id = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
+  client_info.installation_date = 1111;
+  client_info.reporting_enabled_date = 2222;
 
-  prefs_.ClearPref(prefs::kInstallDate);
-  prefs_.ClearPref(prefs::kMetricsClientID);
-  prefs_.ClearPref(prefs::kMetricsReportingEnabledTimestamp);
-
-  {
-    // When running the backup from old-style client ids, dashes should be
-    // re-added. And missing dates in backup should be replaced by Time::Now().
-
-    EXPECT_FALSE(stored_client_info_backup_);
-
-    std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
-    EXPECT_EQ(kBackupClientId, state_manager->client_id());
-    EXPECT_GE(prefs_.GetInt64(prefs::kInstallDate), test_begin_time);
-    EXPECT_GE(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp),
-              test_begin_time);
-
-    EXPECT_TRUE(stored_client_info_backup_);
-    previous_client_info = std::move(stored_client_info_backup_);
-  }
+  SetFakeClientInfoBackup(client_info);
+  SetClientInfoPrefs(client_info);
 
   prefs_.SetBoolean(prefs::kMetricsResetIds, true);
 
+  EnableMetricsReporting();
   {
     // Upon request to reset metrics ids, the existing backup should not be
     // restored.
 
-    EXPECT_FALSE(stored_client_info_backup_);
-
     std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
 
     // A brand new client id should have been generated.
     EXPECT_NE(std::string(), state_manager->client_id());
-    EXPECT_NE(previous_client_info->client_id, state_manager->client_id());
+    EXPECT_NE(client_info.client_id, state_manager->client_id());
+    EXPECT_TRUE(stored_client_info_backup_);
 
     // The installation date should not have been affected.
-    EXPECT_EQ(previous_client_info->installation_date,
+    EXPECT_EQ(client_info.installation_date,
               prefs_.GetInt64(prefs::kInstallDate));
 
     // The metrics-reporting-enabled date will be reset to Now().
     EXPECT_GE(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp),
-              previous_client_info->reporting_enabled_date);
-
-    stored_client_info_backup_.reset();
+              test_begin_time_);
   }
 }
 
diff --git a/components/prefs/json_pref_store.cc b/components/prefs/json_pref_store.cc
index a9a3922..cf4d2a3 100644
--- a/components/prefs/json_pref_store.cc
+++ b/components/prefs/json_pref_store.cc
@@ -111,13 +111,7 @@
 }
 
 std::unique_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk(
-    const base::FilePath& path,
-    const base::FilePath& alternate_path) {
-  if (!base::PathExists(path) && !alternate_path.empty() &&
-      base::PathExists(alternate_path)) {
-    base::Move(alternate_path, path);
-  }
-
+    const base::FilePath& path) {
   int error_code;
   std::string error_msg;
   std::unique_ptr<JsonPrefStore::ReadResult> read_result(
@@ -151,18 +145,7 @@
     const base::FilePath& pref_filename,
     const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
     std::unique_ptr<PrefFilter> pref_filter)
-    : JsonPrefStore(pref_filename,
-                    base::FilePath(),
-                    sequenced_task_runner,
-                    std::move(pref_filter)) {}
-
-JsonPrefStore::JsonPrefStore(
-    const base::FilePath& pref_filename,
-    const base::FilePath& pref_alternate_filename,
-    const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
-    std::unique_ptr<PrefFilter> pref_filter)
     : path_(pref_filename),
-      alternate_path_(pref_alternate_filename),
       sequenced_task_runner_(sequenced_task_runner),
       prefs_(new base::DictionaryValue()),
       read_only_(false),
@@ -283,7 +266,7 @@
 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
   DCHECK(CalledOnValidThread());
 
-  OnFileRead(ReadPrefsFromDisk(path_, alternate_path_));
+  OnFileRead(ReadPrefsFromDisk(path_));
   return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE
                                 : read_error_;
 }
@@ -296,9 +279,8 @@
 
   // Weakly binds the read task so that it doesn't kick in during shutdown.
   base::PostTaskAndReplyWithResult(
-      sequenced_task_runner_.get(),
-      FROM_HERE,
-      base::Bind(&ReadPrefsFromDisk, path_, alternate_path_),
+      sequenced_task_runner_.get(), FROM_HERE,
+      base::Bind(&ReadPrefsFromDisk, path_),
       base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr()));
 }
 
diff --git a/components/prefs/json_pref_store.h b/components/prefs/json_pref_store.h
index 8c1164c..6284b8d5 100644
--- a/components/prefs/json_pref_store.h
+++ b/components/prefs/json_pref_store.h
@@ -62,22 +62,11 @@
       const base::FilePath& pref_filename,
       base::SequencedWorkerPool* worker_pool);
 
-  // Same as the constructor below with no alternate filename.
-  JsonPrefStore(
-      const base::FilePath& pref_filename,
-      const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
-      std::unique_ptr<PrefFilter> pref_filter);
-
   // |sequenced_task_runner| must be a shutdown-blocking task runner, ideally
   // created by the GetTaskRunnerForFile() method above.
   // |pref_filename| is the path to the file to read prefs from.
-  // |pref_alternate_filename| is the path to an alternate file which the
-  // desired prefs may have previously been written to. If |pref_filename|
-  // doesn't exist and |pref_alternate_filename| does, |pref_alternate_filename|
-  // will be moved to |pref_filename| before the read occurs.
   JsonPrefStore(
       const base::FilePath& pref_filename,
-      const base::FilePath& pref_alternate_filename,
       const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
       std::unique_ptr<PrefFilter> pref_filter);
 
@@ -231,7 +220,6 @@
   void ScheduleWrite(uint32_t flags);
 
   const base::FilePath path_;
-  const base::FilePath alternate_path_;
   const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
 
   std::unique_ptr<base::DictionaryValue> prefs_;
diff --git a/components/prefs/json_pref_store_unittest.cc b/components/prefs/json_pref_store_unittest.cc
index 0164ad6..cae79d3 100644
--- a/components/prefs/json_pref_store_unittest.cc
+++ b/components/prefs/json_pref_store_unittest.cc
@@ -166,21 +166,6 @@
   EXPECT_FALSE(pref_store->ReadOnly());
 }
 
-// Test fallback behavior for a nonexistent file and alternate file.
-TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
-  base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
-  base::FilePath bogus_alternate_input_file =
-      temp_dir_.GetPath().AppendASCII("read_alternate.txt");
-  ASSERT_FALSE(PathExists(bogus_input_file));
-  ASSERT_FALSE(PathExists(bogus_alternate_input_file));
-  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
-      bogus_input_file, bogus_alternate_input_file, message_loop_.task_runner(),
-      std::unique_ptr<PrefFilter>());
-  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
-            pref_store->ReadPrefs());
-  EXPECT_FALSE(pref_store->ReadOnly());
-}
-
 // Test fallback behavior for an invalid file.
 TEST_F(JsonPrefStoreTest, InvalidFile) {
   base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
@@ -518,171 +503,6 @@
   RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
 }
 
-TEST_F(JsonPrefStoreTest, AlternateFile) {
-  base::FilePath alternate_input_file =
-      temp_dir_.GetPath().AppendASCII("alternate.json");
-  ASSERT_LT(0, base::WriteFile(alternate_input_file,
-                               kReadJson, arraysize(kReadJson) - 1));
-
-  // Test that the alternate file is moved to the main file and read as-is from
-  // there.
-  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_FALSE(PathExists(input_file));
-  ASSERT_TRUE(PathExists(alternate_input_file));
-  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
-      input_file, alternate_input_file, message_loop_.task_runner(),
-      std::unique_ptr<PrefFilter>());
-
-  ASSERT_FALSE(PathExists(input_file));
-  ASSERT_TRUE(PathExists(alternate_input_file));
-  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
-
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_FALSE(PathExists(alternate_input_file));
-
-  EXPECT_FALSE(pref_store->ReadOnly());
-  EXPECT_TRUE(pref_store->IsInitializationComplete());
-
-  // The JSON file looks like this:
-  // {
-  //   "homepage": "http://www.cnn.com",
-  //   "some_directory": "/usr/local/",
-  //   "tabs": {
-  //     "new_windows_in_tabs": true,
-  //     "max_tabs": 20
-  //   }
-  // }
-
-  RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
-}
-
-TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) {
-  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0, base::WriteFile(input_file,
-                               kReadJson, arraysize(kReadJson) - 1));
-
-  base::FilePath alternate_input_file =
-      temp_dir_.GetPath().AppendASCII("alternate.json");
-  ASSERT_LT(0, base::WriteFile(alternate_input_file,
-                               kInvalidJson, arraysize(kInvalidJson) - 1));
-
-  // Test that the alternate file is ignored and that the read occurs from the
-  // existing main file. There is no attempt at even deleting the alternate
-  // file as this scenario should never happen in normal user-data-dirs.
-  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
-      input_file, alternate_input_file, message_loop_.task_runner(),
-      std::unique_ptr<PrefFilter>());
-
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_TRUE(PathExists(alternate_input_file));
-  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
-
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_TRUE(PathExists(alternate_input_file));
-
-  EXPECT_FALSE(pref_store->ReadOnly());
-  EXPECT_TRUE(pref_store->IsInitializationComplete());
-
-  // The JSON file looks like this:
-  // {
-  //   "homepage": "http://www.cnn.com",
-  //   "some_directory": "/usr/local/",
-  //   "tabs": {
-  //     "new_windows_in_tabs": true,
-  //     "max_tabs": 20
-  //   }
-  // }
-
-  RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
-}
-
-TEST_F(JsonPrefStoreTest, AlternateFileDNE) {
-  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0, base::WriteFile(input_file,
-                               kReadJson, arraysize(kReadJson) - 1));
-
-  // Test that the basic read works fine when an alternate file is specified but
-  // does not exist.
-  base::FilePath alternate_input_file =
-      temp_dir_.GetPath().AppendASCII("alternate.json");
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_FALSE(PathExists(alternate_input_file));
-  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
-      input_file, alternate_input_file, message_loop_.task_runner(),
-      std::unique_ptr<PrefFilter>());
-
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_FALSE(PathExists(alternate_input_file));
-  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
-
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_FALSE(PathExists(alternate_input_file));
-
-  EXPECT_FALSE(pref_store->ReadOnly());
-  EXPECT_TRUE(pref_store->IsInitializationComplete());
-
-  // The JSON file looks like this:
-  // {
-  //   "homepage": "http://www.cnn.com",
-  //   "some_directory": "/usr/local/",
-  //   "tabs": {
-  //     "new_windows_in_tabs": true,
-  //     "max_tabs": 20
-  //   }
-  // }
-
-  RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
-}
-
-TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) {
-  base::FilePath alternate_input_file =
-      temp_dir_.GetPath().AppendASCII("alternate.json");
-  ASSERT_LT(0, base::WriteFile(alternate_input_file,
-                               kReadJson, arraysize(kReadJson) - 1));
-
-  // Test that the alternate file is moved to the main file and read as-is from
-  // there even when the read is made asynchronously.
-  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
-      input_file, alternate_input_file, message_loop_.task_runner(),
-      std::unique_ptr<PrefFilter>());
-
-  ASSERT_FALSE(PathExists(input_file));
-  ASSERT_TRUE(PathExists(alternate_input_file));
-
-  {
-    MockPrefStoreObserver mock_observer;
-    pref_store->AddObserver(&mock_observer);
-
-    MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
-    pref_store->ReadPrefsAsync(mock_error_delegate);
-
-    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
-    EXPECT_CALL(*mock_error_delegate,
-                OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
-    RunLoop().RunUntilIdle();
-    pref_store->RemoveObserver(&mock_observer);
-
-    EXPECT_FALSE(pref_store->ReadOnly());
-    EXPECT_TRUE(pref_store->IsInitializationComplete());
-  }
-
-  ASSERT_TRUE(PathExists(input_file));
-  ASSERT_FALSE(PathExists(alternate_input_file));
-
-  // The JSON file looks like this:
-  // {
-  //   "homepage": "http://www.cnn.com",
-  //   "some_directory": "/usr/local/",
-  //   "tabs": {
-  //     "new_windows_in_tabs": true,
-  //     "max_tabs": 20
-  //   }
-  // }
-
-  RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
-}
-
 TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) {
   base::HistogramTester histogram_tester;
 
diff --git a/components/sync/android/java/src/org/chromium/components/sync/notifier/RandomizedInvalidationClientNameGenerator.java b/components/sync/android/java/src/org/chromium/components/sync/notifier/RandomizedInvalidationClientNameGenerator.java
index faf597c..bbcbaf6 100644
--- a/components/sync/android/java/src/org/chromium/components/sync/notifier/RandomizedInvalidationClientNameGenerator.java
+++ b/components/sync/android/java/src/org/chromium/components/sync/notifier/RandomizedInvalidationClientNameGenerator.java
@@ -32,6 +32,7 @@
      * However, as bad as it is, this ID is better than a hard-coded default or none at all.  See
      * the class description for more details.
      */
+    @Override
     public byte[] generateInvalidatorClientName() {
         byte[] randomBytes = new byte[8];
         RANDOM.nextBytes(randomBytes);
diff --git a/components/translate/core/browser/translate_ranker.cc b/components/translate/core/browser/translate_ranker.cc
index 1341652..dd934110 100644
--- a/components/translate/core/browser/translate_ranker.cc
+++ b/components/translate/core/browser/translate_ranker.cc
@@ -80,7 +80,7 @@
     "TranslateRankerEnforcement", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kTranslateRankerLogging{"TranslateRankerLogging",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
 TranslateRanker::~TranslateRanker() {}
 
diff --git a/components/translate/core/browser/translate_ranker_unittest.cc b/components/translate/core/browser/translate_ranker_unittest.cc
index 495a1b861..3a48529 100644
--- a/components/translate/core/browser/translate_ranker_unittest.cc
+++ b/components/translate/core/browser/translate_ranker_unittest.cc
@@ -143,7 +143,7 @@
 TEST_F(TranslateRankerTest, EnableQueryAndEnforcement) {
   InitFeatures({kTranslateRankerQuery, kTranslateRankerEnforcement}, {});
   EXPECT_TRUE(TranslateRanker::IsEnabled());
-  EXPECT_FALSE(TranslateRanker::IsLoggingEnabled());
+  EXPECT_TRUE(TranslateRanker::IsLoggingEnabled());
 }
 
 TEST_F(TranslateRankerTest, EnableLogging) {
@@ -186,7 +186,6 @@
 }
 
 TEST_F(TranslateRankerTest, RecordAndFlushEvents) {
-  InitFeatures({kTranslateRankerLogging}, {});
   std::unique_ptr<translate::TranslateRanker> ranker = GetRankerForTest(0.0f);
   std::vector<metrics::TranslateEventProto> flushed_events;
 
diff --git a/components/user_prefs/tracked/mock_validation_delegate.cc b/components/user_prefs/tracked/mock_validation_delegate.cc
index 4362f991..c82deb5 100644
--- a/components/user_prefs/tracked/mock_validation_delegate.cc
+++ b/components/user_prefs/tracked/mock_validation_delegate.cc
@@ -63,7 +63,7 @@
     bool is_personal) {
   record_->RecordValidation(pref_path, value_state,
                             external_validation_value_state, is_personal,
-                            PrefHashFilter::TRACKING_STRATEGY_ATOMIC);
+                            PrefHashFilter::PrefTrackingStrategy::ATOMIC);
 }
 
 void MockValidationDelegate::OnSplitPreferenceValidation(
@@ -75,5 +75,5 @@
     bool is_personal) {
   record_->RecordValidation(pref_path, value_state,
                             external_validation_value_state, is_personal,
-                            PrefHashFilter::TRACKING_STRATEGY_SPLIT);
+                            PrefHashFilter::PrefTrackingStrategy::SPLIT);
 }
diff --git a/components/user_prefs/tracked/pref_hash_filter.cc b/components/user_prefs/tracked/pref_hash_filter.cc
index 8056b749..44a8ec5 100644
--- a/components/user_prefs/tracked/pref_hash_filter.cc
+++ b/components/user_prefs/tracked/pref_hash_filter.cc
@@ -78,7 +78,7 @@
 
     std::unique_ptr<TrackedPreference> tracked_preference;
     switch (metadata.strategy) {
-      case TRACKING_STRATEGY_ATOMIC:
+      case PrefTrackingStrategy::ATOMIC:
         tracked_preference.reset(
             new TrackedAtomicPreference(metadata.name,
                                         metadata.reporting_id,
@@ -87,7 +87,7 @@
                                         metadata.value_type,
                                         delegate));
         break;
-      case TRACKING_STRATEGY_SPLIT:
+      case PrefTrackingStrategy::SPLIT:
         tracked_preference.reset(
             new TrackedSplitPreference(metadata.name,
                                        metadata.reporting_id,
diff --git a/components/user_prefs/tracked/pref_hash_filter.h b/components/user_prefs/tracked/pref_hash_filter.h
index 87c3f03..d7addb9 100644
--- a/components/user_prefs/tracked/pref_hash_filter.h
+++ b/components/user_prefs/tracked/pref_hash_filter.h
@@ -45,21 +45,21 @@
 // are changed.
 class PrefHashFilter : public InterceptablePrefFilter {
  public:
-  enum EnforcementLevel { NO_ENFORCEMENT, ENFORCE_ON_LOAD };
+  enum class EnforcementLevel { NO_ENFORCEMENT, ENFORCE_ON_LOAD };
 
-  enum PrefTrackingStrategy {
+  enum class PrefTrackingStrategy {
     // Atomic preferences are tracked as a whole.
-    TRACKING_STRATEGY_ATOMIC,
+    ATOMIC,
     // Split preferences are dictionaries for which each top-level entry is
     // tracked independently. Note: preferences using this strategy must be kept
     // in sync with TrackedSplitPreferences in histograms.xml.
-    TRACKING_STRATEGY_SPLIT,
+    SPLIT,
   };
 
-  enum ValueType {
-    VALUE_IMPERSONAL,
+  enum class ValueType {
+    IMPERSONAL,
     // The preference value may contain personal information.
-    VALUE_PERSONAL,
+    PERSONAL,
   };
 
   struct TrackedPreferenceMetadata {
diff --git a/components/user_prefs/tracked/pref_hash_filter_unittest.cc b/components/user_prefs/tracked/pref_hash_filter_unittest.cc
index 604471f..a3c701b 100644
--- a/components/user_prefs/tracked/pref_hash_filter_unittest.cc
+++ b/components/user_prefs/tracked/pref_hash_filter_unittest.cc
@@ -33,6 +33,10 @@
 
 namespace {
 
+using EnforcementLevel = PrefHashFilter::EnforcementLevel;
+using PrefTrackingStrategy = PrefHashFilter::PrefTrackingStrategy;
+using ValueType = PrefHashFilter::ValueType;
+
 const char kAtomicPref[] = "atomic_pref";
 const char kAtomicPref2[] = "atomic_pref2";
 const char kAtomicPref3[] = "pref3";
@@ -42,41 +46,20 @@
 const char kSplitPref[] = "split_pref";
 
 const PrefHashFilter::TrackedPreferenceMetadata kTestTrackedPrefs[] = {
-    {0,
-     kAtomicPref,
-     PrefHashFilter::ENFORCE_ON_LOAD,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-     PrefHashFilter::VALUE_PERSONAL},
-    {1,
-     kReportOnlyPref,
-     PrefHashFilter::NO_ENFORCEMENT,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-     PrefHashFilter::VALUE_IMPERSONAL},
-    {2,
-     kSplitPref,
-     PrefHashFilter::ENFORCE_ON_LOAD,
-     PrefHashFilter::TRACKING_STRATEGY_SPLIT,
-     PrefHashFilter::VALUE_IMPERSONAL},
-    {3,
-     kReportOnlySplitPref,
-     PrefHashFilter::NO_ENFORCEMENT,
-     PrefHashFilter::TRACKING_STRATEGY_SPLIT,
-     PrefHashFilter::VALUE_IMPERSONAL},
-    {4,
-     kAtomicPref2,
-     PrefHashFilter::ENFORCE_ON_LOAD,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-     PrefHashFilter::VALUE_IMPERSONAL},
-    {5,
-     kAtomicPref3,
-     PrefHashFilter::ENFORCE_ON_LOAD,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-     PrefHashFilter::VALUE_IMPERSONAL},
-    {6,
-     kAtomicPref4,
-     PrefHashFilter::ENFORCE_ON_LOAD,
-     PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-     PrefHashFilter::VALUE_IMPERSONAL},
+    {0, kAtomicPref, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::PERSONAL},
+    {1, kReportOnlyPref, EnforcementLevel::NO_ENFORCEMENT,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {2, kSplitPref, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::SPLIT, ValueType::IMPERSONAL},
+    {3, kReportOnlySplitPref, EnforcementLevel::NO_ENFORCEMENT,
+     PrefTrackingStrategy::SPLIT, ValueType::IMPERSONAL},
+    {4, kAtomicPref2, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {5, kAtomicPref3, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
+    {6, kAtomicPref4, EnforcementLevel::ENFORCE_ON_LOAD,
+     PrefTrackingStrategy::ATOMIC, ValueType::IMPERSONAL},
 };
 
 }  // namespace
@@ -311,15 +294,13 @@
 MockPrefHashStore::MockPrefHashStoreTransaction::CheckValue(
     const std::string& path,
     const base::Value* value) const {
-  return outer_->RecordCheckValue(path, value,
-                                  PrefHashFilter::TRACKING_STRATEGY_ATOMIC);
+  return outer_->RecordCheckValue(path, value, PrefTrackingStrategy::ATOMIC);
 }
 
 void MockPrefHashStore::MockPrefHashStoreTransaction::StoreHash(
     const std::string& path,
     const base::Value* new_value) {
-  outer_->RecordStoreHash(path, new_value,
-                          PrefHashFilter::TRACKING_STRATEGY_ATOMIC);
+  outer_->RecordStoreHash(path, new_value, PrefTrackingStrategy::ATOMIC);
 }
 
 PrefHashStoreTransaction::ValueState
@@ -338,14 +319,13 @@
   }
 
   return outer_->RecordCheckValue(path, initial_split_value,
-                                  PrefHashFilter::TRACKING_STRATEGY_SPLIT);
+                                  PrefTrackingStrategy::SPLIT);
 }
 
 void MockPrefHashStore::MockPrefHashStoreTransaction::StoreSplitHash(
     const std::string& path,
     const base::DictionaryValue* new_value) {
-  outer_->RecordStoreHash(path, new_value,
-                          PrefHashFilter::TRACKING_STRATEGY_SPLIT);
+  outer_->RecordStoreHash(path, new_value, PrefTrackingStrategy::SPLIT);
 }
 
 bool MockPrefHashStore::MockPrefHashStoreTransaction::HasHash(
@@ -719,7 +699,7 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_value =
       mock_pref_hash_store_->stored_value(kAtomicPref);
   ASSERT_EQ(string_value, stored_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC, stored_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_value.second);
 
   ASSERT_EQ(1u, mock_pref_hash_store_->transactions_performed());
   VerifyRecordedReset(false);
@@ -779,7 +759,7 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
   ASSERT_EQ(dict_value, stored_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_value.second);
 
   ASSERT_EQ(1u, mock_pref_hash_store_->transactions_performed());
   VerifyRecordedReset(false);
@@ -835,20 +815,18 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_value_atomic1 =
       mock_pref_hash_store_->stored_value(kAtomicPref);
   ASSERT_EQ(int_value1, stored_value_atomic1.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_value_atomic1.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_value_atomic1.second);
   ASSERT_EQ(1u, mock_pref_hash_store_->transactions_performed());
 
   MockPrefHashStore::ValuePtrStrategyPair stored_value_atomic3 =
       mock_pref_hash_store_->stored_value(kAtomicPref3);
   ASSERT_EQ(int_value5, stored_value_atomic3.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_value_atomic3.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_value_atomic3.second);
 
   MockPrefHashStore::ValuePtrStrategyPair stored_value_split =
       mock_pref_hash_store_->stored_value(kSplitPref);
   ASSERT_EQ(dict_value, stored_value_split.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_value_split.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_value_split.second);
 }
 
 TEST_P(PrefHashFilterTest, UnknownNullValue) {
@@ -868,13 +846,12 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
       mock_pref_hash_store_->stored_value(kAtomicPref);
   ASSERT_EQ(NULL, stored_atomic_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_atomic_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_atomic_value.second);
 
   MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
   ASSERT_EQ(NULL, stored_split_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_split_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_split_value.second);
 
   // Delegate saw all prefs, two of which had the expected value_state.
   ASSERT_EQ(arraysize(kTestTrackedPrefs),
@@ -887,13 +864,11 @@
 
   const MockValidationDelegateRecord::ValidationEvent* validated_split_pref =
       mock_validation_delegate_record_->GetEventForPath(kSplitPref);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
-            validated_split_pref->strategy);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, validated_split_pref->strategy);
   ASSERT_FALSE(validated_split_pref->is_personal);
   const MockValidationDelegateRecord::ValidationEvent* validated_atomic_pref =
       mock_validation_delegate_record_->GetEventForPath(kAtomicPref);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            validated_atomic_pref->strategy);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, validated_atomic_pref->strategy);
   ASSERT_TRUE(validated_atomic_pref->is_personal);
 }
 
@@ -915,7 +890,7 @@
   mock_pref_hash_store_->SetCheckResult(
       kSplitPref, PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE);
   // If we are enforcing, expect this to report changes.
-  DoFilterOnLoad(GetParam() >= PrefHashFilter::ENFORCE_ON_LOAD);
+  DoFilterOnLoad(GetParam() >= EnforcementLevel::ENFORCE_ON_LOAD);
   ASSERT_EQ(arraysize(kTestTrackedPrefs),
             mock_pref_hash_store_->checked_paths_count());
   ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
@@ -934,10 +909,9 @@
       mock_pref_hash_store_->stored_value(kAtomicPref);
   MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_atomic_value.second);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_split_value.second);
-  if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_atomic_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_split_value.second);
+  if (GetParam() == EnforcementLevel::ENFORCE_ON_LOAD) {
     // Ensure the prefs were cleared and the hashes for NULL were restored if
     // the current enforcement level denies seeding.
     ASSERT_FALSE(pref_store_contents_->Get(kAtomicPref, NULL));
@@ -1003,8 +977,7 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
       mock_pref_hash_store_->stored_value(kAtomicPref);
   ASSERT_EQ(string_value, stored_atomic_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_atomic_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_atomic_value.second);
 
   const base::Value* split_value_in_store;
   ASSERT_TRUE(pref_store_contents_->Get(kSplitPref, &split_value_in_store));
@@ -1012,7 +985,7 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
   ASSERT_EQ(dict_value, stored_split_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_split_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_split_value.second);
 }
 
 TEST_P(PrefHashFilterTest, InitialValueChanged) {
@@ -1040,7 +1013,7 @@
   mock_invalid_keys.push_back("c");
   mock_pref_hash_store_->SetInvalidKeysResult(kSplitPref, mock_invalid_keys);
 
-  DoFilterOnLoad(GetParam() >= PrefHashFilter::ENFORCE_ON_LOAD);
+  DoFilterOnLoad(GetParam() >= EnforcementLevel::ENFORCE_ON_LOAD);
   ASSERT_EQ(arraysize(kTestTrackedPrefs),
             mock_pref_hash_store_->checked_paths_count());
   ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
@@ -1050,10 +1023,9 @@
       mock_pref_hash_store_->stored_value(kAtomicPref);
   MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_atomic_value.second);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_split_value.second);
-  if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_atomic_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_split_value.second);
+  if (GetParam() == EnforcementLevel::ENFORCE_ON_LOAD) {
     // Ensure the atomic pref was cleared and the hash for NULL was restored if
     // the current enforcement level prevents changes.
     ASSERT_FALSE(pref_store_contents_->Get(kAtomicPref, NULL));
@@ -1121,14 +1093,13 @@
   MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
       mock_pref_hash_store_->stored_value(kAtomicPref);
   ASSERT_EQ(NULL, stored_atomic_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_atomic_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_atomic_value.second);
 
   ASSERT_FALSE(pref_store_contents_->Get(kSplitPref, NULL));
   MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
   ASSERT_EQ(NULL, stored_split_value.first);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_split_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_split_value.second);
 }
 
 TEST_P(PrefHashFilterTest, InitialValueUnchangedLegacyId) {
@@ -1170,8 +1141,7 @@
 
   MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
       mock_pref_hash_store_->stored_value(kAtomicPref);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
-            stored_atomic_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_atomic_value.second);
   const base::Value* atomic_value_in_store;
   ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref, &atomic_value_in_store));
   ASSERT_EQ(string_value, atomic_value_in_store);
@@ -1179,7 +1149,7 @@
 
   MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
       mock_pref_hash_store_->stored_value(kSplitPref);
-  ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_split_value.second);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_split_value.second);
   const base::Value* split_value_in_store;
   ASSERT_TRUE(pref_store_contents_->Get(kSplitPref, &split_value_in_store));
   ASSERT_EQ(dict_value, split_value_in_store);
@@ -1214,7 +1184,7 @@
   mock_pref_hash_store_->SetCheckResult(kReportOnlySplitPref,
                                         PrefHashStoreTransaction::CHANGED);
 
-  DoFilterOnLoad(GetParam() >= PrefHashFilter::ENFORCE_ON_LOAD);
+  DoFilterOnLoad(GetParam() >= EnforcementLevel::ENFORCE_ON_LOAD);
   // All prefs should be checked and a new hash should be stored for each tested
   // pref.
   ASSERT_EQ(arraysize(kTestTrackedPrefs),
@@ -1241,7 +1211,7 @@
             mock_pref_hash_store_->stored_value(kReportOnlySplitPref).first);
 
   // All other prefs should have been reset if the enforcement level allows it.
-  if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
+  if (GetParam() == EnforcementLevel::ENFORCE_ON_LOAD) {
     ASSERT_FALSE(pref_store_contents_->Get(kAtomicPref, NULL));
     ASSERT_FALSE(pref_store_contents_->Get(kAtomicPref2, NULL));
     ASSERT_EQ(NULL, mock_pref_hash_store_->stored_value(kAtomicPref).first);
@@ -1393,5 +1363,5 @@
 
 INSTANTIATE_TEST_CASE_P(PrefHashFilterTestInstance,
                         PrefHashFilterTest,
-                        testing::Values(PrefHashFilter::NO_ENFORCEMENT,
-                                        PrefHashFilter::ENFORCE_ON_LOAD));
+                        testing::Values(EnforcementLevel::NO_ENFORCEMENT,
+                                        EnforcementLevel::ENFORCE_ON_LOAD));
diff --git a/components/user_prefs/tracked/tracked_preference_helper.cc b/components/user_prefs/tracked/tracked_preference_helper.cc
index fb36a2a..b0415c4 100644
--- a/components/user_prefs/tracked/tracked_preference_helper.cc
+++ b/components/user_prefs/tracked/tracked_preference_helper.cc
@@ -18,9 +18,9 @@
     : pref_path_(pref_path),
       reporting_id_(reporting_id),
       reporting_ids_count_(reporting_ids_count),
-      enforce_(enforcement_level == PrefHashFilter::ENFORCE_ON_LOAD),
-      personal_(value_type == PrefHashFilter::VALUE_PERSONAL) {
-}
+      enforce_(enforcement_level ==
+               PrefHashFilter::EnforcementLevel::ENFORCE_ON_LOAD),
+      personal_(value_type == PrefHashFilter::ValueType::PERSONAL) {}
 
 TrackedPreferenceHelper::ResetAction TrackedPreferenceHelper::GetAction(
     PrefHashStoreTransaction::ValueState value_state) const {
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index f226027..dfc904bb 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -311,10 +311,6 @@
   child_process_host_->ForceShutdown();
 }
 
-void BrowserChildProcessHostImpl::SetBackgrounded(bool backgrounded) {
-  child_process_->SetProcessBackgrounded(backgrounded);
-}
-
 void BrowserChildProcessHostImpl::AddFilter(BrowserMessageFilter* filter) {
   child_process_host_->AddFilter(filter->GetFilter());
 }
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h
index 3c78e6a..bb654dbf 100644
--- a/content/browser/browser_child_process_host_impl.h
+++ b/content/browser/browser_child_process_host_impl.h
@@ -101,9 +101,6 @@
   // Removes this host from the host list. Calls ChildProcessHost::ForceShutdown
   void ForceShutdown();
 
-  // Callers can reduce the BrowserChildProcess' priority.
-  void SetBackgrounded(bool backgrounded);
-
   // Adds an IPC message filter.
   void AddFilter(BrowserMessageFilter* filter);
 
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 7b85e54..0ea3dd16 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -896,24 +896,20 @@
 
 void BrowserPluginGuest::OnImeSetComposition(
     int browser_plugin_instance_id,
-    const std::string& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
-    int selection_start,
-    int selection_end) {
-  Send(new InputMsg_ImeSetComposition(routing_id(),
-                                      base::UTF8ToUTF16(text), underlines,
-                                      gfx::Range::InvalidRange(),
-                                      selection_start, selection_end));
+    const BrowserPluginHostMsg_SetComposition_Params& params) {
+  Send(new InputMsg_ImeSetComposition(
+      routing_id(), params.text, params.underlines, params.replacement_range,
+      params.selection_start, params.selection_end));
 }
 
 void BrowserPluginGuest::OnImeCommitText(
     int browser_plugin_instance_id,
-    const std::string& text,
+    const base::string16& text,
     const std::vector<blink::WebCompositionUnderline>& underlines,
+    const gfx::Range& replacement_range,
     int relative_cursor_pos) {
-  Send(new InputMsg_ImeCommitText(routing_id(), base::UTF8ToUTF16(text),
-                                  underlines, gfx::Range::InvalidRange(),
-                                  relative_cursor_pos));
+  Send(new InputMsg_ImeCommitText(routing_id(), text, underlines,
+                                  replacement_range, relative_cursor_pos));
 }
 
 void BrowserPluginGuest::OnImeFinishComposingText(bool keep_selection) {
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index f1218cd..fedd4a7 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -45,6 +45,7 @@
 #include "ui/gfx/geometry/rect.h"
 
 struct BrowserPluginHostMsg_Attach_Params;
+struct BrowserPluginHostMsg_SetComposition_Params;
 
 #if defined(OS_MACOSX)
 struct FrameHostMsg_ShowPopup_Params;
@@ -342,14 +343,12 @@
   void OnTextInputStateChanged(const TextInputState& params);
   void OnImeSetComposition(
       int instance_id,
-      const std::string& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
-      int selection_start,
-      int selection_end);
+      const BrowserPluginHostMsg_SetComposition_Params& params);
   void OnImeCommitText(
       int instance_id,
-      const std::string& text,
+      const base::string16& text,
       const std::vector<blink::WebCompositionUnderline>& underlines,
+      const gfx::Range& replacement_range,
       int relative_cursor_pos);
   void OnImeFinishComposingText(bool keep_selection);
   void OnExtendSelectionAndDelete(int instance_id, int before, int after);
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 11a508b..de70cc65 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -269,7 +269,7 @@
                                    size_t approximate_event_count) {
   // TODO(crbug426117): remove set_value once all clients have switched to
   // the new interface of the event.
-  frontend_->BufferUsage(percent_full, percent_full, approximate_event_count);
+  frontend_->BufferUsage(percent_full, approximate_event_count, percent_full);
 }
 
 void TracingHandler::OnCategoriesReceived(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 29517e0..77fdcb7b 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -376,6 +376,9 @@
   SetUpMojoIfNeeded();
   swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind(
       &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr())));
+  beforeunload_timeout_.reset(
+      new TimeoutMonitor(base::Bind(&RenderFrameHostImpl::BeforeUnloadTimeout,
+                                    weak_ptr_factory_.GetWeakPtr())));
 
   if (widget_routing_id != MSG_ROUTING_NONE) {
     // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, the main
@@ -1481,8 +1484,7 @@
   }
   // Resets beforeunload waiting state.
   is_waiting_for_beforeunload_ack_ = false;
-  render_view_host_->GetWidget()->decrement_in_flight_event_count();
-  render_view_host_->GetWidget()->StopHangMonitorTimeout();
+  beforeunload_timeout_->Stop();
   send_before_unload_start_time_ = base::TimeTicks();
 
   // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to
@@ -1704,6 +1706,13 @@
   // shouldn't process input events.
   GetProcess()->SetIgnoreInputEvents(true);
   render_view_host_->GetWidget()->StopHangMonitorTimeout();
+
+  // The beforeunload dialog for this frame may have been triggered by a
+  // browser-side request to this frame or a frame up in the frame hierarchy.
+  // Stop any timers that are waiting.
+  for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent())
+    frame->beforeunload_timeout_->Stop();
+
   delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg);
 }
 
@@ -2420,8 +2429,7 @@
   // navigations to be ignored in OnDidCommitProvisionalLoad.
   if (is_waiting_for_beforeunload_ack_) {
     is_waiting_for_beforeunload_ack_ = false;
-    render_view_host_->GetWidget()->decrement_in_flight_event_count();
-    render_view_host_->GetWidget()->StopHangMonitorTimeout();
+    beforeunload_timeout_->Stop();
   }
   send_before_unload_start_time_ = base::TimeTicks();
   render_view_host_->is_waiting_for_close_ack_ = false;
@@ -2567,12 +2575,8 @@
       // reply from the dialog.
       SimulateBeforeUnloadAck();
     } else {
-      // Increment the in-flight event count, to ensure that input events won't
-      // cancel the timeout timer.
-      render_view_host_->GetWidget()->increment_in_flight_event_count();
-      render_view_host_->GetWidget()->StartHangMonitorTimeout(
-          TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS),
-          blink::WebInputEvent::Undefined);
+      beforeunload_timeout_->Start(
+          TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
       send_before_unload_start_time_ = base::TimeTicks::Now();
       Send(new FrameMsg_BeforeUnload(routing_id_, is_reload));
     }
@@ -2637,33 +2641,27 @@
     IPC::Message* reply_msg,
     bool success,
     const base::string16& user_input,
-    bool is_before_unload_dialog,
     bool dialog_was_suppressed) {
   GetProcess()->SetIgnoreInputEvents(false);
 
-  // If we are executing as part of beforeunload event handling, we don't
-  // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
-  // leave the current page. In this case, use the regular timeout value used
-  // during the beforeunload handling.
-  if (is_before_unload_dialog) {
-    render_view_host_->GetWidget()->StartHangMonitorTimeout(
-        success
-            ? TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)
-            : render_view_host_->GetWidget()->hung_renderer_delay(),
-        blink::WebInputEvent::Undefined);
-  }
-
   SendJavaScriptDialogReply(reply_msg, success, user_input);
 
-  // If we are waiting for a beforeunload ack and the user has suppressed
-  // messages, kill the tab immediately; a page that's spamming alerts in
-  // onbeforeunload is presumably malicious, so there's no point in continuing
-  // to run its script and dragging out the process. This must be done after
-  // sending the reply since RenderView can't close correctly while waiting for
-  // a response.
-  if (is_before_unload_dialog && dialog_was_suppressed) {
-    render_view_host_->GetWidget()->delegate()->RendererUnresponsive(
-        render_view_host_->GetWidget());
+  // If executing as part of beforeunload event handling, there may have been
+  // timers stopped in this frame or a frame up in the frame hierarchy. Restart
+  // any timers that were stopped in OnRunBeforeUnloadConfirm().
+  for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent()) {
+    if (frame->is_waiting_for_beforeunload_ack_) {
+      // If we are waiting for a beforeunload ack and the user has suppressed
+      // messages, kill the tab immediately. A page that's spamming is
+      // presumably malicious, so there's no point in continuing to run its
+      // script and dragging out the process.
+      if (dialog_was_suppressed) {
+        frame->SimulateBeforeUnloadAck();
+      } else {
+        frame->beforeunload_timeout_->Start(
+            TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
+      }
+    }
   }
 }
 
@@ -3490,6 +3488,13 @@
       entry_id_for_data_nav, false);  // started_from_context_menu
 }
 
+void RenderFrameHostImpl::BeforeUnloadTimeout() {
+  if (render_view_host_->GetDelegate()->ShouldIgnoreUnresponsiveRenderer())
+    return;
+
+  SimulateBeforeUnloadAck();
+}
+
 #if defined(OS_ANDROID)
 base::android::ScopedJavaLocalRef<jobject>
 RenderFrameHostImpl::GetJavaRenderFrameHost() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 4b10798..b5c7fe6 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -459,7 +459,6 @@
   void JavaScriptDialogClosed(IPC::Message* reply_msg,
                               bool success,
                               const base::string16& user_input,
-                              bool is_before_unload_dialog,
                               bool dialog_was_suppressed);
 
   // Get the accessibility mode from the delegate and Send a message to the
@@ -889,6 +888,9 @@
   std::unique_ptr<NavigationHandleImpl> TakeNavigationHandleForCommit(
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
 
+  // Called by |beforeunload_timeout_| when the beforeunload timeout fires.
+  void BeforeUnloadTimeout();
+
   // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a
   // refcount that calls Shutdown when it reaches zero.  This allows each
   // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring
@@ -993,6 +995,11 @@
   // PlzNavigate: all navigations require a beforeUnload ACK.
   bool unload_ack_is_for_navigation_;
 
+  // The timeout monitor that runs from when the beforeunload is started in
+  // DispatchBeforeUnload() until either the render process ACKs it with an IPC
+  // to OnBeforeUnloadACK(), or until the timeout triggers.
+  std::unique_ptr<TimeoutMonitor> beforeunload_timeout_;
+
   // Indicates whether this RenderFrameHost is in the process of loading a
   // document or not.
   bool is_loading_;
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index 6547788..5f46755 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -6,9 +6,12 @@
 
 #include "base/macros.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/frame_messages.h"
+#include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_client.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -156,4 +159,126 @@
   SetBrowserClientForTesting(old_client);
 }
 
+namespace {
+
+class TestJavaScriptDialogManager : public JavaScriptDialogManager,
+                                    public WebContentsDelegate {
+ public:
+  TestJavaScriptDialogManager() : message_loop_runner_(new MessageLoopRunner) {}
+  ~TestJavaScriptDialogManager() override {}
+
+  void Wait() {
+    message_loop_runner_->Run();
+    message_loop_runner_ = new MessageLoopRunner;
+  }
+
+  DialogClosedCallback& callback() { return callback_; }
+
+  // WebContentsDelegate
+
+  JavaScriptDialogManager* GetJavaScriptDialogManager(
+      WebContents* source) override {
+    return this;
+  }
+
+  // JavaScriptDialogManager
+
+  void RunJavaScriptDialog(WebContents* web_contents,
+                           const GURL& origin_url,
+                           JavaScriptDialogType dialog_type,
+                           const base::string16& message_text,
+                           const base::string16& default_prompt_text,
+                           const DialogClosedCallback& callback,
+                           bool* did_suppress_message) override {}
+
+  void RunBeforeUnloadDialog(WebContents* web_contents,
+                             bool is_reload,
+                             const DialogClosedCallback& callback) override {
+    callback_ = callback;
+    message_loop_runner_->Quit();
+  }
+
+  bool HandleJavaScriptDialog(WebContents* web_contents,
+                              bool accept,
+                              const base::string16* prompt_override) override {
+    return true;
+  }
+
+  void CancelDialogs(WebContents* web_contents, bool reset_state) override {}
+
+ private:
+  DialogClosedCallback callback_;
+
+  // The MessageLoopRunner used to spin the message loop.
+  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager);
+};
+
+class DropBeforeUnloadACKFilter : public BrowserMessageFilter {
+ public:
+  DropBeforeUnloadACKFilter() : BrowserMessageFilter(FrameMsgStart) {}
+
+ protected:
+  ~DropBeforeUnloadACKFilter() override {}
+
+ private:
+  // BrowserMessageFilter:
+  bool OnMessageReceived(const IPC::Message& message) override {
+    return message.type() == FrameHostMsg_BeforeUnload_ACK::ID;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(DropBeforeUnloadACKFilter);
+};
+
+}  // namespace
+
+// Tests that a beforeunload dialog in an iframe doesn't stop the beforeunload
+// timer of a parent frame.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       IframeBeforeUnloadParentHang) {
+  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
+  TestJavaScriptDialogManager dialog_manager;
+  wc->SetDelegate(&dialog_manager);
+
+  EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
+  // Make an iframe with a beforeunload handler.
+  std::string script =
+      "var iframe = document.createElement('iframe');"
+      "document.body.appendChild(iframe);"
+      "iframe.contentWindow.onbeforeunload=function(e){return 'x'};";
+  EXPECT_TRUE(content::ExecuteScript(wc, script));
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+
+  // Force a process switch by going to a privileged page. The beforeunload
+  // timer will be started on the top-level frame but will be paused while the
+  // beforeunload dialog is shown by the subframe.
+  GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
+                   std::string(kChromeUIGpuHost));
+  shell()->LoadURL(web_ui_page);
+  dialog_manager.Wait();
+
+  RenderFrameHostImpl* main_frame =
+      static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
+  EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_ack());
+
+  // Set up a filter to make sure that when the dialog is answered below and the
+  // renderer sends the beforeunload ACK, it gets... ahem... lost.
+  scoped_refptr<DropBeforeUnloadACKFilter> filter =
+      new DropBeforeUnloadACKFilter();
+  main_frame->GetProcess()->AddFilter(filter.get());
+
+  // Answer the dialog.
+  dialog_manager.callback().Run(true, base::string16());
+
+  // There will be no beforeunload ACK, so if the beforeunload ACK timer isn't
+  // functioning then the navigation will hang forever and this test will time
+  // out. If this waiting for the load stop works, this test won't time out.
+  EXPECT_TRUE(WaitForLoadStop(wc));
+  EXPECT_EQ(web_ui_page, wc->GetLastCommittedURL());
+
+  wc->SetDelegate(nullptr);
+  wc->SetJavaScriptDialogManagerForTesting(nullptr);
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index e61eb44b..bb52257 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -2134,11 +2134,16 @@
                "FrameTreeNode id", frame_tree_node_->frame_tree_node_id());
   DCHECK(pending_render_frame_host_ || speculative_render_frame_host_);
 
+  bool is_main_frame = frame_tree_node_->IsMainFrame();
+
   // First check whether we're going to want to focus the location bar after
   // this commit.  We do this now because the navigation hasn't formally
   // committed yet, so if we've already cleared the pending WebUI the call chain
-  // this triggers won't be able to figure out what's going on.
-  bool will_focus_location_bar = delegate_->FocusLocationBarByDefault();
+  // this triggers won't be able to figure out what's going on.  Note that
+  // subframe commits should not be allowed to steal focus from the main frame
+  // by focusing the location bar (see https://crbug.com/700124).
+  bool will_focus_location_bar =
+      is_main_frame && delegate_->FocusLocationBarByDefault();
 
   // Remember if the page was focused so we can focus the new renderer in
   // that case.
@@ -2146,8 +2151,6 @@
                            render_frame_host_->GetView() &&
                            render_frame_host_->GetView()->HasFocus();
 
-  bool is_main_frame = frame_tree_node_->IsMainFrame();
-
   // While the old frame is still current, remove its children from the tree.
   frame_tree_node_->ResetForNewProcess();
 
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index 772aad36..938e3ba 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -84,7 +84,8 @@
     : RenderWidgetHostViewChildFrame(widget_host),
       // |guest| is NULL during test.
       guest_(guest ? guest->AsWeakPtr() : base::WeakPtr<BrowserPluginGuest>()),
-      platform_view_(platform_view) {
+      platform_view_(platform_view),
+      should_forward_text_selection_(false) {
   gfx::NativeView view = GetNativeView();
   if (view)
     UpdateScreenInfo(view);
@@ -372,6 +373,9 @@
     return;
   // Forward the information to embedding RWHV.
   rwhv->TextInputStateChanged(params);
+
+  should_forward_text_selection_ =
+      (params.type != ui::TEXT_INPUT_TYPE_NONE) && guest_ && guest_->focused();
 }
 
 void RenderWidgetHostViewGuest::ImeCancelComposition() {
@@ -409,7 +413,11 @@
 void RenderWidgetHostViewGuest::SelectionChanged(const base::string16& text,
                                                  size_t offset,
                                                  const gfx::Range& range) {
-  platform_view_->SelectionChanged(text, offset, range);
+  RenderWidgetHostViewBase* view = should_forward_text_selection_
+                                       ? GetOwnerRenderWidgetHostView()
+                                       : platform_view_.get();
+  if (view)
+    view->SelectionChanged(text, offset, range);
 }
 
 void RenderWidgetHostViewGuest::SelectionBoundsChanged(
diff --git a/content/browser/frame_host/render_widget_host_view_guest.h b/content/browser/frame_host/render_widget_host_view_guest.h
index e5c20e6..8371712 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.h
+++ b/content/browser/frame_host/render_widget_host_view_guest.h
@@ -161,6 +161,12 @@
   // RenderWidgetHostViewGuest mostly only cares about stuff related to
   // compositing, the rest are directly forwarded to this |platform_view_|.
   base::WeakPtr<RenderWidgetHostViewBase> platform_view_;
+
+  // When true the guest will forward its selection updates to the owner RWHV.
+  // The guest may forward its updates only when there is an ongoing IME
+  // session.
+  bool should_forward_text_selection_;
+
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewGuest);
 };
 
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index a835ff8..ae0025e 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -1153,9 +1153,8 @@
       crashed_before_ = true;
       last_gpu_crash_time = current_time;
 
-      if ((gpu_recent_crash_count_ >= kGpuMaxCrashCount &&
-           !disable_crash_limit) ||
-          !initialized_) {
+      if ((gpu_recent_crash_count_ >= kGpuMaxCrashCount || !initialized_) &&
+          !disable_crash_limit) {
 #if !defined(OS_CHROMEOS)
         // The GPU process is too unstable to use. Disable it for current
         // session.
diff --git a/content/browser/loader/mojo_async_resource_handler.cc b/content/browser/loader/mojo_async_resource_handler.cc
index 20a9bf1..1c4fc78 100644
--- a/content/browser/loader/mojo_async_resource_handler.cc
+++ b/content/browser/loader/mojo_async_resource_handler.cc
@@ -121,7 +121,7 @@
     : ResourceHandler(request),
       rdh_(rdh),
       binding_(this, std::move(mojo_request)),
-      handle_watcher_(FROM_HERE),
+      handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       url_loader_client_(std::move(url_loader_client)),
       weak_factory_(this) {
   DCHECK(url_loader_client_);
@@ -245,9 +245,10 @@
 
     response_body_consumer_handle_ = std::move(data_pipe.consumer_handle);
     shared_writer_ = new SharedWriter(std::move(data_pipe.producer_handle));
-    handle_watcher_.Start(shared_writer_->writer(), MOJO_HANDLE_SIGNAL_WRITABLE,
+    handle_watcher_.Watch(shared_writer_->writer(), MOJO_HANDLE_SIGNAL_WRITABLE,
                           base::Bind(&MojoAsyncResourceHandler::OnWritable,
                                      base::Unretained(this)));
+    handle_watcher_.ArmOrNotify();
 
     bool defer = false;
     scoped_refptr<net::IOBufferWithSize> buffer;
@@ -388,11 +389,16 @@
       shared_writer_->writer(), data, available, MOJO_WRITE_DATA_FLAG_NONE);
   if (result == MOJO_RESULT_OK)
     *available = std::min(*available, static_cast<uint32_t>(kMaxChunkSize));
+  else if (result == MOJO_RESULT_SHOULD_WAIT)
+    handle_watcher_.ArmOrNotify();
   return result;
 }
 
 MojoResult MojoAsyncResourceHandler::EndWrite(uint32_t written) {
-  return mojo::EndWriteDataRaw(shared_writer_->writer(), written);
+  MojoResult result = mojo::EndWriteDataRaw(shared_writer_->writer(), written);
+  if (result == MOJO_RESULT_OK)
+    handle_watcher_.ArmOrNotify();
+  return result;
 }
 
 net::IOBufferWithSize* MojoAsyncResourceHandler::GetResponseMetadata(
diff --git a/content/browser/loader/mojo_async_resource_handler.h b/content/browser/loader/mojo_async_resource_handler.h
index b8cd1e5..ce84d64 100644
--- a/content/browser/loader/mojo_async_resource_handler.h
+++ b/content/browser/loader/mojo_async_resource_handler.h
@@ -20,7 +20,7 @@
 #include "content/public/common/resource_type.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/io_buffer.h"
 #include "net/base/request_priority.h"
 
@@ -137,7 +137,7 @@
   base::TimeTicks response_started_ticks_;
   int64_t reported_total_received_bytes_ = 0;
 
-  mojo::Watcher handle_watcher_;
+  mojo::SimpleWatcher handle_watcher_;
   std::unique_ptr<mojom::URLLoader> url_loader_;
   mojom::URLLoaderClientPtr url_loader_client_;
   scoped_refptr<net::IOBufferWithSize> buffer_;
diff --git a/content/browser/loader/reload_cache_control_browsertest.cc b/content/browser/loader/reload_cache_control_browsertest.cc
index 7d7a6818..05e8481 100644
--- a/content/browser/loader/reload_cache_control_browsertest.cc
+++ b/content/browser/loader/reload_cache_control_browsertest.cc
@@ -27,8 +27,16 @@
 using net::test_server::HttpRequest;
 using net::test_server::HttpResponse;
 
-const char kReloadTestPath[] = "/loader/reload.html";
+const char kReloadTestPath[] = "/loader/reload_test.html";
+const char kReloadFramePath[] = "/loader/simple_frame.html";
 const char kReloadImagePath[] = "/loader/empty16x16.png";
+// The test page should request resources as the content structure is described
+// below. Reload and the same page navigation will affect only the top frame
+// resource, reload_test.html. But bypassing reload will affect all resources.
+// +- reload_test.html
+//     +- empty16x16.png
+//     +- simple_frame.html
+//         +- empty16x16.png
 
 const char kNoCacheControl[] = "";
 const char kMaxAgeCacheControl[] = "max-age=0";
@@ -80,6 +88,7 @@
   DISALLOW_COPY_AND_ASSIGN(ReloadCacheControlBrowserTest);
 };
 
+// Test if reload issues requests with proper cache control flags.
 IN_PROC_BROWSER_TEST_F(ReloadCacheControlBrowserTest, NormalReload) {
   GURL url(embedded_test_server()->GetURL(kReloadTestPath));
 
@@ -88,16 +97,25 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(4UL, request_log_.size());
+    ASSERT_EQ(8UL, request_log_.size());
     EXPECT_EQ(kReloadTestPath, request_log_[0].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[0].cache_control);
     EXPECT_EQ(kReloadImagePath, request_log_[1].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[1].cache_control);
-
-    EXPECT_EQ(kReloadTestPath, request_log_[2].relative_url);
-    EXPECT_EQ(kMaxAgeCacheControl, request_log_[2].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[2].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[2].cache_control);
     EXPECT_EQ(kReloadImagePath, request_log_[3].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[3].cache_control);
+
+    // Only the top main resource should be requested with kMaxAgeCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[4].relative_url);
+    EXPECT_EQ(kMaxAgeCacheControl, request_log_[4].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[5].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[5].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[6].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[6].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[7].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[7].cache_control);
   }
 
   shell()->ShowDevTools();
@@ -105,11 +123,17 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(6UL, request_log_.size());
-    EXPECT_EQ(kReloadTestPath, request_log_[4].relative_url);
-    EXPECT_EQ(kMaxAgeCacheControl, request_log_[4].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[5].relative_url);
-    EXPECT_EQ(kNoCacheControl, request_log_[5].cache_control);
+    ASSERT_EQ(12UL, request_log_.size());
+
+    // Only the top main resource should be requested with kMaxAgeCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[8].relative_url);
+    EXPECT_EQ(kMaxAgeCacheControl, request_log_[8].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[9].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[9].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[10].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[10].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[11].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[11].cache_control);
   }
 
   shell()->CloseDevTools();
@@ -117,14 +141,21 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(8UL, request_log_.size());
-    EXPECT_EQ(kReloadTestPath, request_log_[6].relative_url);
-    EXPECT_EQ(kMaxAgeCacheControl, request_log_[6].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[7].relative_url);
-    EXPECT_EQ(kNoCacheControl, request_log_[7].cache_control);
+    ASSERT_EQ(16UL, request_log_.size());
+
+    // Only the top main resource should be requested with kMaxAgeCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[12].relative_url);
+    EXPECT_EQ(kMaxAgeCacheControl, request_log_[12].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[13].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[13].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[14].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[14].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[15].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[15].cache_control);
   }
 }
 
+// Test if bypassing reload issues requests with proper cache control flags.
 IN_PROC_BROWSER_TEST_F(ReloadCacheControlBrowserTest, BypassingReload) {
   GURL url(embedded_test_server()->GetURL(kReloadTestPath));
 
@@ -133,16 +164,25 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(4UL, request_log_.size());
+    ASSERT_EQ(8UL, request_log_.size());
     EXPECT_EQ(kReloadTestPath, request_log_[0].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[0].cache_control);
     EXPECT_EQ(kReloadImagePath, request_log_[1].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[1].cache_control);
-
-    EXPECT_EQ(kReloadTestPath, request_log_[2].relative_url);
-    EXPECT_EQ(kNoCacheCacheControl, request_log_[2].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[2].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[2].cache_control);
     EXPECT_EQ(kReloadImagePath, request_log_[3].relative_url);
-    EXPECT_EQ(kNoCacheCacheControl, request_log_[3].cache_control);
+    EXPECT_EQ(kNoCacheControl, request_log_[3].cache_control);
+
+    // Only the top main resource should be requested with kNoCacheCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[4].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[4].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[5].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[5].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[6].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[6].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[7].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[7].cache_control);
   }
 
   shell()->ShowDevTools();
@@ -150,11 +190,17 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(6UL, request_log_.size());
-    EXPECT_EQ(kReloadTestPath, request_log_[4].relative_url);
-    EXPECT_EQ(kNoCacheCacheControl, request_log_[4].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[5].relative_url);
-    EXPECT_EQ(kNoCacheCacheControl, request_log_[5].cache_control);
+    ASSERT_EQ(12UL, request_log_.size());
+
+    // All resources should be requested with kNoCacheCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[8].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[8].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[9].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[9].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[10].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[10].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[11].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[11].cache_control);
   }
 
   shell()->CloseDevTools();
@@ -162,14 +208,22 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(8UL, request_log_.size());
-    EXPECT_EQ(kReloadTestPath, request_log_[6].relative_url);
-    EXPECT_EQ(kNoCacheCacheControl, request_log_[6].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[7].relative_url);
-    EXPECT_EQ(kNoCacheCacheControl, request_log_[7].cache_control);
+    ASSERT_EQ(16UL, request_log_.size());
+
+    // All resources should be requested with kNoCacheCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[12].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[12].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[13].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[13].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[14].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[14].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[15].relative_url);
+    EXPECT_EQ(kNoCacheCacheControl, request_log_[15].cache_control);
   }
 }
 
+// Test if the same page navigation issues requests with proper cache control
+// flags.
 IN_PROC_BROWSER_TEST_F(ReloadCacheControlBrowserTest, NavigateToSame) {
   GURL url(embedded_test_server()->GetURL(kReloadTestPath));
 
@@ -179,11 +233,15 @@
   // The first navigation is just a normal load.
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(4UL, request_log_.size());
+    ASSERT_EQ(8UL, request_log_.size());
     EXPECT_EQ(kReloadTestPath, request_log_[0].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[0].cache_control);
     EXPECT_EQ(kReloadImagePath, request_log_[1].relative_url);
     EXPECT_EQ(kNoCacheControl, request_log_[1].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[2].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[3].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[3].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[3].cache_control);
   }
 
   // TODO(crbug.com/671545): This test does not work correctly if browser-side
@@ -196,10 +254,16 @@
   // for others.
   {
     base::AutoLock lock(request_log_lock_);
-    EXPECT_EQ(kReloadTestPath, request_log_[2].relative_url);
-    EXPECT_EQ(kMaxAgeCacheControl, request_log_[2].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[3].relative_url);
-    EXPECT_EQ(kNoCacheControl, request_log_[3].cache_control);
+
+    // Only the top main resource should be requested with kMaxAgeCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[4].relative_url);
+    EXPECT_EQ(kMaxAgeCacheControl, request_log_[4].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[5].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[5].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[6].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[6].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[7].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[7].cache_control);
   }
 
   shell()->ShowDevTools();
@@ -207,11 +271,17 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(6UL, request_log_.size());
-    EXPECT_EQ(kReloadTestPath, request_log_[4].relative_url);
-    EXPECT_EQ(kMaxAgeCacheControl, request_log_[4].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[5].relative_url);
-    EXPECT_EQ(kNoCacheControl, request_log_[5].cache_control);
+    ASSERT_EQ(12UL, request_log_.size());
+
+    // Only the top main resource should be requested with kMaxAgeCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[8].relative_url);
+    EXPECT_EQ(kMaxAgeCacheControl, request_log_[8].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[9].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[9].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[10].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[10].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[11].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[11].cache_control);
   }
 
   shell()->CloseDevTools();
@@ -219,11 +289,17 @@
 
   {
     base::AutoLock lock(request_log_lock_);
-    ASSERT_EQ(8UL, request_log_.size());
-    EXPECT_EQ(kReloadTestPath, request_log_[6].relative_url);
-    EXPECT_EQ(kMaxAgeCacheControl, request_log_[6].cache_control);
-    EXPECT_EQ(kReloadImagePath, request_log_[7].relative_url);
-    EXPECT_EQ(kNoCacheControl, request_log_[7].cache_control);
+    ASSERT_EQ(16UL, request_log_.size());
+
+    // Only the top main resource should be requested with kMaxAgeCacheControl.
+    EXPECT_EQ(kReloadTestPath, request_log_[12].relative_url);
+    EXPECT_EQ(kMaxAgeCacheControl, request_log_[12].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[13].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[13].cache_control);
+    EXPECT_EQ(kReloadFramePath, request_log_[14].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[14].cache_control);
+    EXPECT_EQ(kReloadImagePath, request_log_[15].relative_url);
+    EXPECT_EQ(kNoCacheControl, request_log_[15].cache_control);
   }
 }
 
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 752c352e..27f9a45f 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -316,6 +316,15 @@
   params->page_zoom_level = delegate_->GetPendingPageZoomLevel();
   params->image_decode_color_space = gfx::ICCProfile::FromBestMonitor();
 
+  // Pretend that HDR displays are sRGB so that we do not have inconsistent
+  // coloring.
+  // TODO(ccameron): Disable this once color correct rasterization is functional
+  // https://crbug.com/701942
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableHDR)) {
+    gfx::ColorSpace::CreateSRGB().GetICCProfile(
+        &params->image_decode_color_space);
+  }
+
   GetWidget()->GetResizeParams(&params->initial_size);
   GetWidget()->SetInitialRenderSizeParams(params->initial_size);
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index e054001..175f1687 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -82,6 +82,7 @@
 #include "storage/browser/fileapi/isolated_context.h"
 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -667,6 +668,16 @@
   *resize_params = ResizeParams();
 
   GetScreenInfo(&resize_params->screen_info);
+
+  // Pretend that HDR displays are sRGB so that we do not have inconsistent
+  // coloring.
+  // TODO(ccameron): Disable this once color correct rasterization is functional
+  // https://crbug.com/701942
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableHDR)) {
+    gfx::ColorSpace::CreateSRGB().GetICCProfile(
+        &resize_params->screen_info.icc_profile);
+  }
+
   if (delegate_) {
     resize_params->is_fullscreen_granted =
         delegate_->IsFullscreenForCurrentTab();
@@ -2106,14 +2117,15 @@
 
 void RenderWidgetHostImpl::IncrementInFlightEventCount(
     blink::WebInputEvent::Type event_type) {
-  increment_in_flight_event_count();
+  ++in_flight_event_count_;
   if (!is_hidden_)
     StartHangMonitorTimeout(hung_renderer_delay_, event_type);
 }
 
 void RenderWidgetHostImpl::DecrementInFlightEventCount(
     InputEventAckSource ack_source) {
-  if (decrement_in_flight_event_count() <= 0) {
+  --in_flight_event_count_;
+  if (in_flight_event_count_ <= 0) {
     // Cancel pending hung renderer checks since the renderer is responsive.
     StopHangMonitorTimeout();
   } else {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 3ea94419..e98b0e4 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -553,14 +553,6 @@
                         uint32_t offset,
                         const gfx::Range& range);
 
-  // Expose increment/decrement of the in-flight event count, so
-  // RenderViewHostImpl can account for in-flight beforeunload/unload events.
-  int increment_in_flight_event_count() { return ++in_flight_event_count_; }
-  int decrement_in_flight_event_count() {
-    DCHECK_GT(in_flight_event_count_, 0);
-    return --in_flight_event_count_;
-  }
-
   size_t in_flight_event_count() const { return in_flight_event_count_; }
   blink::WebInputEvent::Type hang_monitor_event_type() const {
     return hang_monitor_event_type_;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 6266020..0cb516b 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1431,6 +1431,10 @@
     cc::CompositorFrame frame) {
   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
 
+  // Override the compositor background color. See RenderWidgetHostViewAura
+  // for more details.
+  SetBackgroundColor(frame.metadata.root_background_color);
+
   last_scroll_offset_ = frame.metadata.root_scroll_offset;
 
   page_at_minimum_scale_ =
diff --git a/content/browser/shared_worker/shared_worker_service_impl_unittest.cc b/content/browser/shared_worker/shared_worker_service_impl_unittest.cc
index db5c128..6048190 100644
--- a/content/browser/shared_worker/shared_worker_service_impl_unittest.cc
+++ b/content/browser/shared_worker/shared_worker_service_impl_unittest.cc
@@ -15,10 +15,10 @@
 #include "base/atomic_sequence_num.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
 #include "content/browser/shared_worker/shared_worker_message_filter.h"
 #include "content/browser/shared_worker/worker_storage_partition.h"
 #include "content/common/view_messages.h"
@@ -112,11 +112,9 @@
 static const int kRenderFrameRouteIDs[] = {300, 301, 302};
 
 void BlockingReadFromMessagePort(MessagePort port, base::string16* message) {
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
-  port.SetCallback(
-      base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
-  event.Wait();
+  base::RunLoop run_loop;
+  port.SetCallback(run_loop.QuitClosure());
+  run_loop.Run();
 
   std::vector<MessagePort> should_be_empty;
   EXPECT_TRUE(port.GetMessage(message, &should_be_empty));
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a8e41e5..d8bdf2f 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4226,9 +4226,7 @@
       delegate_->ShouldSuppressDialogs(this) ||
       !delegate_->GetJavaScriptDialogManager(this);
   if (suppress_this_message) {
-    rfhi->JavaScriptDialogClosed(reply_msg, true, base::string16(),
-                                 /*is_before_unload_dialog=*/true,
-                                 /*dialog_was_suppressed=*/true);
+    rfhi->JavaScriptDialogClosed(reply_msg, true, base::string16(), true);
     return;
   }
 
@@ -4846,16 +4844,6 @@
   if (ShouldIgnoreUnresponsiveRenderer())
     return;
 
-  RenderFrameHostImpl* rfhi =
-      static_cast<RenderFrameHostImpl*>(GetRenderViewHost()->GetMainFrame());
-  if (rfhi->is_waiting_for_beforeunload_ack()) {
-    // If the hang is in the beforeunload handler, pretend the beforeunload
-    // listeners have all fired and allow the delegate to continue closing;
-    // the user will not have the option of cancelling the close.
-    rfhi->SimulateBeforeUnloadAck();
-    return;
-  }
-
   if (!GetRenderViewHost() || !GetRenderViewHost()->IsRenderViewLive())
     return;
 
@@ -5117,7 +5105,6 @@
 
   if (rfh) {
     rfh->JavaScriptDialogClosed(reply_msg, success, user_input,
-                                is_showing_before_unload_dialog_,
                                 dialog_was_suppressed);
   } else {
     // Don't leak the sync IPC reply if the RFH or process is gone.
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index be6e06b..4ea1053 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -843,6 +843,8 @@
                            CrossSiteIframeAccessibility);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
                            JavaScriptDialogsInMainAndSubframes);
+  FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest,
+                           IframeBeforeUnloadParentHang);
 
   // So |find_request_manager_| can be accessed for testing.
   friend class FindRequestManagerTest;
diff --git a/content/child/url_response_body_consumer.cc b/content/child/url_response_body_consumer.cc
index a51b4f7..1095ea15c 100644
--- a/content/child/url_response_body_consumer.cc
+++ b/content/child/url_response_body_consumer.cc
@@ -48,15 +48,15 @@
     : request_id_(request_id),
       resource_dispatcher_(resource_dispatcher),
       handle_(std::move(handle)),
-      handle_watcher_(FROM_HERE, task_runner),
+      handle_watcher_(FROM_HERE,
+                      mojo::SimpleWatcher::ArmingPolicy::MANUAL,
+                      task_runner),
       task_runner_(task_runner),
       has_seen_end_of_data_(!handle_.is_valid()) {
-  handle_watcher_.Start(
+  handle_watcher_.Watch(
       handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::Bind(&URLResponseBodyConsumer::OnReadable, base::Unretained(this)));
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(&URLResponseBodyConsumer::OnReadable, AsWeakPtr(),
-                            MOJO_RESULT_OK));
+  handle_watcher_.ArmOrNotify();
 }
 
 URLResponseBodyConsumer::~URLResponseBodyConsumer() {}
@@ -91,9 +91,7 @@
   if (is_in_on_readable_)
     return;
 
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(&URLResponseBodyConsumer::OnReadable, AsWeakPtr(),
-                            MOJO_RESULT_OK));
+  handle_watcher_.ArmOrNotify();
 }
 
 void URLResponseBodyConsumer::OnReadable(MojoResult unused) {
@@ -112,7 +110,11 @@
     uint32_t available = 0;
     MojoResult result = mojo::BeginReadDataRaw(
         handle_.get(), &buffer, &available, MOJO_READ_DATA_FLAG_NONE);
-    if (result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_BUSY)
+    if (result == MOJO_RESULT_SHOULD_WAIT) {
+      handle_watcher_.ArmOrNotify();
+      return;
+    }
+    if (result == MOJO_RESULT_BUSY)
       return;
     if (result == MOJO_RESULT_FAILED_PRECONDITION) {
       has_seen_end_of_data_ = true;
@@ -134,9 +136,7 @@
       // to the next task.
       result = mojo::EndReadDataRaw(handle_.get(), 0);
       DCHECK_EQ(result, MOJO_RESULT_OK);
-      task_runner_->PostTask(FROM_HERE,
-                             base::Bind(&URLResponseBodyConsumer::OnReadable,
-                                        AsWeakPtr(), MOJO_RESULT_OK));
+      handle_watcher_.ArmOrNotify();
       return;
     }
     num_bytes_consumed += available;
diff --git a/content/child/url_response_body_consumer.h b/content/child/url_response_body_consumer.h
index 1c33aaa..ce6b88a9 100644
--- a/content/child/url_response_body_consumer.h
+++ b/content/child/url_response_body_consumer.h
@@ -18,7 +18,7 @@
 #include "content/common/content_export.h"
 #include "content/common/url_loader.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace content {
 
@@ -71,7 +71,7 @@
   const int request_id_;
   ResourceDispatcher* resource_dispatcher_;
   mojo::ScopedDataPipeConsumerHandle handle_;
-  mojo::Watcher handle_watcher_;
+  mojo::SimpleWatcher handle_watcher_;
   ResourceRequestCompletionStatus completion_status_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
diff --git a/content/child/web_data_consumer_handle_impl.cc b/content/child/web_data_consumer_handle_impl.cc
index f81221d8..b40f479 100644
--- a/content/child/web_data_consumer_handle_impl.cc
+++ b/content/child/web_data_consumer_handle_impl.cc
@@ -37,7 +37,9 @@
 WebDataConsumerHandleImpl::ReaderImpl::ReaderImpl(
     scoped_refptr<Context> context,
     Client* client)
-    : context_(context), handle_watcher_(FROM_HERE), client_(client) {
+    : context_(context),
+      handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC),
+      client_(client) {
   if (client_)
     StartWatching();
 }
@@ -130,7 +132,7 @@
 }
 
 void WebDataConsumerHandleImpl::ReaderImpl::StartWatching() {
-  handle_watcher_.Start(
+  handle_watcher_.Watch(
       context_->handle().get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::Bind(&ReaderImpl::OnHandleGotReadable, base::Unretained(this)));
 }
diff --git a/content/child/web_data_consumer_handle_impl.h b/content/child/web_data_consumer_handle_impl.h
index fb814a2..16e61d26 100644
--- a/content/child/web_data_consumer_handle_impl.h
+++ b/content/child/web_data_consumer_handle_impl.h
@@ -11,7 +11,7 @@
 
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/system/data_pipe.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "third_party/WebKit/public/platform/WebDataConsumerHandle.h"
 
 namespace content {
@@ -41,7 +41,7 @@
     void OnHandleGotReadable(MojoResult);
 
     scoped_refptr<Context> context_;
-    mojo::Watcher handle_watcher_;
+    mojo::SimpleWatcher handle_watcher_;
     Client* client_;
 
     DISALLOW_COPY_AND_ASSIGN(ReaderImpl);
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 4a7001d..38664d97 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -306,7 +306,6 @@
     "sandbox_win.cc",
     "sandbox_win.h",
     "savable_subframe.h",
-    "screen_orientation_messages.h",
     "send_zygote_child_ping_linux.cc",
     "service_manager/embedded_service_runner.cc",
     "service_manager/embedded_service_runner.h",
diff --git a/content/common/browser_plugin/browser_plugin_messages.h b/content/common/browser_plugin/browser_plugin_messages.h
index 8c4ebd94..b4e635e9 100644
--- a/content/common/browser_plugin/browser_plugin_messages.h
+++ b/content/common/browser_plugin/browser_plugin_messages.h
@@ -26,6 +26,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/ipc/gfx_param_traits.h"
 #include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
+#include "ui/gfx/range/range.h"
 
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
@@ -44,6 +45,14 @@
   IPC_STRUCT_MEMBER(bool, is_full_page_plugin)
 IPC_STRUCT_END()
 
+IPC_STRUCT_BEGIN(BrowserPluginHostMsg_SetComposition_Params)
+  IPC_STRUCT_MEMBER(base::string16, text)
+  IPC_STRUCT_MEMBER(std::vector<blink::WebCompositionUnderline>, underlines)
+  IPC_STRUCT_MEMBER(gfx::Range, replacement_range)
+  IPC_STRUCT_MEMBER(int, selection_start)
+  IPC_STRUCT_MEMBER(int, selection_end)
+IPC_STRUCT_END()
+
 // Browser plugin messages
 
 // -----------------------------------------------------------------------------
@@ -65,21 +74,18 @@
 
 // This message is sent from BrowserPlugin to BrowserPluginGuest whenever IME
 // composition state is updated.
-IPC_MESSAGE_CONTROL5(
-    BrowserPluginHostMsg_ImeSetComposition,
-    int /* browser_plugin_instance_id */,
-    std::string /* text */,
-    std::vector<blink::WebCompositionUnderline> /* underlines */,
-    int /* selectiont_start */,
-    int /* selection_end */)
+IPC_MESSAGE_CONTROL2(BrowserPluginHostMsg_ImeSetComposition,
+                     int /* browser_plugin_instance_id */,
+                     BrowserPluginHostMsg_SetComposition_Params /* params */)
 
 // This message is sent from BrowserPlugin to BrowserPluginGuest to notify that
 // deleting the current composition and inserting specified text is requested.
-IPC_MESSAGE_CONTROL4(
+IPC_MESSAGE_CONTROL5(
     BrowserPluginHostMsg_ImeCommitText,
     int /* browser_plugin_instance_id */,
-    std::string /* text */,
+    base::string16 /* text */,
     std::vector<blink::WebCompositionUnderline> /* underlines */,
+    gfx::Range /* replacement_range */,
     int /* relative_cursor_pos */)
 
 // This message is sent from BrowserPlugin to BrowserPluginGuest to notify that
diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h
index 84ea1c0..c8794ab6 100644
--- a/content/common/content_message_generator.h
+++ b/content/common/content_message_generator.h
@@ -37,7 +37,6 @@
 #include "content/common/quota_messages.h"
 #include "content/common/render_process_messages.h"
 #include "content/common/resource_messages.h"
-#include "content/common/screen_orientation_messages.h"
 #include "content/common/service_worker/embedded_worker_messages.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/speech_recognition_messages.h"
diff --git a/content/common/frame_owner_properties.cc b/content/common/frame_owner_properties.cc
index 8e0f7ead..a29dfc2 100644
--- a/content/common/frame_owner_properties.cc
+++ b/content/common/frame_owner_properties.cc
@@ -25,8 +25,6 @@
          allow_fullscreen == other.allow_fullscreen &&
          allow_payment_request == other.allow_payment_request &&
          required_csp == other.required_csp &&
-         std::equal(delegated_permissions.begin(), delegated_permissions.end(),
-                    other.delegated_permissions.begin()) &&
          std::equal(allowed_features.begin(), allowed_features.end(),
                     other.allowed_features.begin());
 }
diff --git a/content/common/frame_owner_properties.h b/content/common/frame_owner_properties.h
index 20d3510..6f48476 100644
--- a/content/common/frame_owner_properties.h
+++ b/content/common/frame_owner_properties.h
@@ -9,7 +9,6 @@
 
 #include "content/common/content_export.h"
 #include "third_party/WebKit/public/platform/WebFeaturePolicy.h"
-#include "third_party/WebKit/public/platform/modules/permissions/permission.mojom.h"
 #include "third_party/WebKit/public/web/WebFrameOwnerProperties.h"
 
 namespace content {
@@ -41,7 +40,6 @@
   // https://www.w3.org/TR/csp-embedded-enforcement/#required-csp
   std::string required_csp;
 
-  std::vector<blink::mojom::PermissionName> delegated_permissions;
   std::vector<blink::WebFeaturePolicyFeature> allowed_features;
 };
 
diff --git a/content/common/manifest_manager_messages.h b/content/common/manifest_manager_messages.h
index ba06774..8619d99a 100644
--- a/content/common/manifest_manager_messages.h
+++ b/content/common/manifest_manager_messages.h
@@ -8,12 +8,16 @@
 #include "content/common/content_export.h"
 #include "content/public/common/manifest.h"
 #include "ipc/ipc_message_macros.h"
+#include "third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h"
 
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
 
 #define IPC_MESSAGE_START ManifestManagerMsgStart
 
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScreenOrientationLockType,
+                              blink::WebScreenOrientationLockDefault,
+                              blink::WebScreenOrientationLockNatural)
 IPC_ENUM_TRAITS_MAX_VALUE(
     content::Manifest::Icon::IconPurpose,
     content::Manifest::Icon::IconPurpose::ICON_PURPOSE_LAST)
diff --git a/content/common/message_port.cc b/content/common/message_port.cc
index c89d7b5a..428162e 100644
--- a/content/common/message_port.cc
+++ b/content/common/message_port.cc
@@ -4,7 +4,9 @@
 
 #include "content/common/message_port.h"
 
+#include "base/bind.h"
 #include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
 
 namespace content {
 
@@ -147,40 +149,86 @@
   if (!callback_)
     return;
 
+  DCHECK(!watcher_handle_.is_valid());
+  MojoResult rv = CreateWatcher(&State::CallOnHandleReady, &watcher_handle_);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+  // We use a scoped_refptr<State> instance as the watch context. This is owned
+  // by the watch and deleted upon receiving a cancellation notification.
+  scoped_refptr<State>* state_ref = new scoped_refptr<State>(this);
+  context_ = reinterpret_cast<uintptr_t>(state_ref);
+
   // NOTE: An HTML MessagePort does not receive an event to tell it when the
   // peer has gone away, so we only watch for readability here.
-  MojoResult rv = MojoWatch(handle_.get().value(),
-                            MOJO_HANDLE_SIGNAL_READABLE,
-                            &MessagePort::State::OnHandleReady,
-                            reinterpret_cast<uintptr_t>(this));
-  if (rv != MOJO_RESULT_OK)
-    DVLOG(1) << this << " MojoWatch failed: " << rv;
+  rv = MojoWatch(watcher_handle_.get().value(), handle_.get().value(),
+                 MOJO_HANDLE_SIGNAL_READABLE, context_);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+  ArmWatcher();
 }
 
 void MessagePort::State::CancelWatch() {
-  if (!callback_)
-    return;
-
-  // NOTE: This synchronizes with the thread where OnHandleReady runs so we are
-  // sure to not be racing with it.
-  MojoCancelWatch(handle_.get().value(), reinterpret_cast<uintptr_t>(this));
+  watcher_handle_.reset();
+  context_ = 0;
 }
 
-// static
-void MessagePort::State::OnHandleReady(
-    uintptr_t context,
-    MojoResult result,
-    MojoHandleSignalsState signals_state,
-    MojoWatchNotificationFlags flags) {
-  if (result == MOJO_RESULT_OK) {
-    reinterpret_cast<MessagePort::State*>(context)->callback_.Run();
+MessagePort::State::~State() = default;
+
+void MessagePort::State::ArmWatcher() {
+  if (!watcher_handle_.is_valid())
+    return;
+
+  uint32_t num_ready_contexts = 1;
+  uintptr_t ready_context;
+  MojoResult ready_result;
+  MojoHandleSignalsState ready_state;
+  MojoResult rv =
+      MojoArmWatcher(watcher_handle_.get().value(), &num_ready_contexts,
+                     &ready_context, &ready_result, &ready_state);
+  if (rv == MOJO_RESULT_OK)
+    return;
+
+  // The watcher could not be armed because it would notify immediately.
+  DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
+  DCHECK_EQ(1u, num_ready_contexts);
+  DCHECK_EQ(context_, ready_context);
+
+  if (ready_result == MOJO_RESULT_OK) {
+    // The handle is already signaled, so we trigger a callback now.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&State::OnHandleReady, this, MOJO_RESULT_OK));
+    return;
+  }
+
+  if (ready_result == MOJO_RESULT_FAILED_PRECONDITION) {
+    DVLOG(1) << this << " MojoArmWatcher failed because of a broken pipe.";
+    return;
+  }
+
+  NOTREACHED();
+}
+
+void MessagePort::State::OnHandleReady(MojoResult result) {
+  if (result == MOJO_RESULT_OK && callback_) {
+    callback_.Run();
+    ArmWatcher();
   } else {
     // And now his watch is ended.
   }
 }
 
-MessagePort::State::~State() {
-  CancelWatch();
+// static
+void MessagePort::State::CallOnHandleReady(uintptr_t context,
+                                           MojoResult result,
+                                           MojoHandleSignalsState signals_state,
+                                           MojoWatcherNotificationFlags flags) {
+  auto* state_ref = reinterpret_cast<scoped_refptr<State>*>(context);
+  if (result == MOJO_RESULT_CANCELLED) {
+    // Last notification. Delete the watch's owned State ref.
+    delete state_ref;
+  } else {
+    (*state_ref)->OnHandleReady(result);
+  }
 }
 
 }  // namespace content
diff --git a/content/common/message_port.h b/content/common/message_port.h
index e63d020..fbd7280a 100644
--- a/content/common/message_port.h
+++ b/content/common/message_port.h
@@ -13,6 +13,7 @@
 #include "base/strings/string16.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/cpp/system/watcher.h"
 
 namespace content {
 
@@ -82,17 +83,25 @@
 
     void AddWatch();
     void CancelWatch();
-    static void OnHandleReady(uintptr_t context,
-                              MojoResult result,
-                              MojoHandleSignalsState signals_state,
-                              MojoWatchNotificationFlags flags);
 
+    mojo::ScopedWatcherHandle watcher_handle_;
     mojo::ScopedMessagePipeHandle handle_;
     base::Closure callback_;
 
    private:
     friend class base::RefCountedThreadSafe<State>;
+
     ~State();
+
+    void ArmWatcher();
+    void OnHandleReady(MojoResult result);
+
+    static void CallOnHandleReady(uintptr_t context,
+                                  MojoResult result,
+                                  MojoHandleSignalsState signals_state,
+                                  MojoWatcherNotificationFlags flags);
+
+    uintptr_t context_;
   };
   mutable scoped_refptr<State> state_;
 };
diff --git a/content/common/screen_orientation_messages.h b/content/common/screen_orientation_messages.h
deleted file mode 100644
index c8d1177a..0000000
--- a/content/common/screen_orientation_messages.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// IPC messages for screen orientation.
-// Multiply-included message file, hence no include guard.
-
-#include "content/common/content_export.h"
-#include "ipc/ipc_message_macros.h"
-#include "third_party/WebKit/public/platform/modules/screen_orientation/WebLockOrientationError.h"
-#include "third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h"
-
-#undef IPC_MESSAGE_EXPORT
-#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
-
-// Only used by content/common/manifest_manager_messages.h.
-IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScreenOrientationLockType,
-                              blink::WebScreenOrientationLockDefault,
-                              blink::WebScreenOrientationLockNatural)
diff --git a/content/gpu/in_process_gpu_thread.cc b/content/gpu/in_process_gpu_thread.cc
index eeb6811d..f92d9cb 100644
--- a/content/gpu/in_process_gpu_thread.cc
+++ b/content/gpu/in_process_gpu_thread.cc
@@ -18,6 +18,10 @@
 #include "base/android/jni_android.h"
 #endif
 
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 namespace content {
 
 InProcessGpuThread::InProcessGpuThread(
@@ -51,6 +55,12 @@
 
   gpu_process_ = new GpuProcess(io_thread_priority);
 
+#if defined(USE_OZONE)
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForGPU(params);
+#endif
+
   gpu::GPUInfo gpu_info;
   if (!gl::init::InitializeGLOneOff())
     VLOG(1) << "gl::init::InitializeGLOneOff failed";
diff --git a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java b/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
index 0cc7719..451abdbb 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
@@ -20,6 +20,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Manages oom bindings used to bound child services.
@@ -158,8 +159,8 @@
         }
     }
 
-    private final Object mModerateBindingPoolLock = new Object();
-    private ModerateBindingPool mModerateBindingPool;
+    private final AtomicReference<ModerateBindingPool> mModerateBindingPool =
+            new AtomicReference<>();
 
     /**
      * Wraps ChildProcessConnection keeping track of additional information needed to manage the
@@ -201,10 +202,7 @@
             if (connection == null) return;
 
             connection.addStrongBinding();
-            ModerateBindingPool moderateBindingPool;
-            synchronized (mModerateBindingPoolLock) {
-                moderateBindingPool = mModerateBindingPool;
-            }
+            ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
             if (moderateBindingPool != null) moderateBindingPool.removeConnection(this);
         }
 
@@ -242,10 +240,7 @@
          * @param connection The ChildProcessConnection to add to the moderate binding pool.
          */
         private void addConnectionToModerateBindingPool(ChildProcessConnection connection) {
-            ModerateBindingPool moderateBindingPool;
-            synchronized (mModerateBindingPoolLock) {
-                moderateBindingPool = mModerateBindingPool;
-            }
+            ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
             if (moderateBindingPool != null && !connection.isStrongBindingBound()) {
                 moderateBindingPool.addConnection(ManagedConnection.this);
             }
@@ -332,7 +327,7 @@
 
         void clearConnection() {
             mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied();
-            ModerateBindingPool moderateBindingPool = mModerateBindingPool;
+            ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
             if (moderateBindingPool != null) moderateBindingPool.removeConnection(this);
             mConnection = null;
         }
@@ -449,10 +444,7 @@
                 mBoundForBackgroundPeriod = mLastInForeground;
             }
         }
-        ModerateBindingPool moderateBindingPool;
-        synchronized (mModerateBindingPoolLock) {
-            moderateBindingPool = mModerateBindingPool;
-        }
+        ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
         if (moderateBindingPool != null) moderateBindingPool.onSentToBackground(mOnTesting);
     }
 
@@ -462,10 +454,7 @@
             mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false);
             mBoundForBackgroundPeriod = null;
         }
-        ModerateBindingPool moderateBindingPool;
-        synchronized (mModerateBindingPoolLock) {
-            moderateBindingPool = mModerateBindingPool;
-        }
+        ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
         if (moderateBindingPool != null) moderateBindingPool.onBroughtToForeground();
     }
 
@@ -501,23 +490,19 @@
     @Override
     public void startModerateBindingManagement(
             Context context, int maxSize, boolean moderateBindingTillBackgrounded) {
-        synchronized (mModerateBindingPoolLock) {
-            if (mIsLowMemoryDevice || mModerateBindingPool != null) return;
-
+        if (mIsLowMemoryDevice) return;
+        ModerateBindingPool pool = new ModerateBindingPool(maxSize);
+        if (mModerateBindingPool.compareAndSet(null, pool)) {
             mModerateBindingTillBackgrounded = moderateBindingTillBackgrounded;
 
             Log.i(TAG, "Moderate binding enabled: maxSize=%d", maxSize);
-            mModerateBindingPool = new ModerateBindingPool(maxSize);
-            if (context != null) context.registerComponentCallbacks(mModerateBindingPool);
+            if (context != null) context.registerComponentCallbacks(pool);
         }
     }
 
     @Override
     public void releaseAllModerateBindings() {
-        ModerateBindingPool moderateBindingPool;
-        synchronized (mModerateBindingPoolLock) {
-            moderateBindingPool = mModerateBindingPool;
-        }
+        ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
         if (moderateBindingPool != null) {
             Log.i(TAG, "Release all moderate bindings: %d", moderateBindingPool.size());
             moderateBindingPool.evictAll();
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnection.java
index 7807b619..ea6f619 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnection.java
@@ -65,6 +65,7 @@
     /**
      * @return The {@link Handler} used for this InputConnection.
      */
+    @Override
     @VisibleForTesting
     Handler getHandler();
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
index c5413d3..2ecd31d 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
@@ -409,6 +409,7 @@
     /**
      * @see InputConnection#deleteSurroundingTextInCodePoints(int, int)
      */
+    @Override
     public boolean deleteSurroundingTextInCodePoints(
             final int beforeLength, final int afterLength) {
         if (DEBUG_LOGS) {
@@ -647,6 +648,7 @@
      * @see InputConnection#closeConnection()
      */
     // TODO(crbug.com/635567): Fix this properly.
+    @Override
     @SuppressLint("MissingSuperCall")
     public void closeConnection() {
         if (DEBUG_LOGS) Log.w(TAG, "closeConnection");
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 3050b79..0f8738e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -311,6 +311,7 @@
                 mNativeWebContentsAndroid, enableHiding, enableShowing, animate);
     }
 
+    @Override
     public void scrollFocusedEditableNodeIntoView() {
         // The native side keeps track of whether the zoom and scroll actually occurred. It is
         // more efficient to do it this way and sometimes fire an unnecessary message rather
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index a6ef29c..6cda4685 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -57,7 +57,7 @@
 // Enables faster location.reload() to use a reload mode that revalidates only
 // main resource forcibly. https://crbug.com/670237.
 const base::Feature kFasterLocationReload{"FasterLocationReload",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables the Feature Policy framework for granting and removing access to
 // other features through HTTP headers.
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 99a7745e1..3497d167 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -584,6 +584,8 @@
       "media/media_stream_constraints_util.h",
       "media/media_stream_constraints_util_sets.cc",
       "media/media_stream_constraints_util_sets.h",
+      "media/media_stream_constraints_util_video_content.cc",
+      "media/media_stream_constraints_util_video_content.h",
       "media/media_stream_constraints_util_video_device.cc",
       "media/media_stream_constraints_util_video_device.h",
       "media/media_stream_dispatcher.cc",
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 035f3779..b3c3250f 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -548,22 +548,28 @@
 bool BrowserPlugin::setComposition(
     const blink::WebString& text,
     const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+    const blink::WebRange& replacementRange,
     int selectionStart,
     int selectionEnd) {
   if (!attached())
     return false;
 
-  std::vector<blink::WebCompositionUnderline> std_underlines;
+  BrowserPluginHostMsg_SetComposition_Params params;
+  params.text = text.utf16();
   for (size_t i = 0; i < underlines.size(); ++i) {
-    std_underlines.push_back(underlines[i]);
+    params.underlines.push_back(underlines[i]);
   }
 
+  params.replacement_range =
+      replacementRange.isNull()
+          ? gfx::Range::InvalidRange()
+          : gfx::Range(static_cast<uint32_t>(replacementRange.startOffset()),
+                       static_cast<uint32_t>(replacementRange.endOffset()));
+  params.selection_start = selectionStart;
+  params.selection_end = selectionEnd;
+
   BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ImeSetComposition(
-      browser_plugin_instance_id_,
-      text.utf8(),
-      std_underlines,
-      selectionStart,
-      selectionEnd));
+      browser_plugin_instance_id_, params));
   // TODO(kochi): This assumes the IPC handling always succeeds.
   return true;
 }
@@ -571,6 +577,7 @@
 bool BrowserPlugin::commitText(
     const blink::WebString& text,
     const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+    const blink::WebRange& replacementRange,
     int relative_cursor_pos) {
   if (!attached())
     return false;
@@ -579,10 +586,15 @@
   for (size_t i = 0; i < underlines.size(); ++i) {
     std_underlines.push_back(std_underlines[i]);
   }
+  gfx::Range replacement_range =
+      replacementRange.isNull()
+          ? gfx::Range::InvalidRange()
+          : gfx::Range(static_cast<uint32_t>(replacementRange.startOffset()),
+                       static_cast<uint32_t>(replacementRange.endOffset()));
 
   BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ImeCommitText(
-      browser_plugin_instance_id_, text.utf8(), std_underlines,
-      relative_cursor_pos));
+      browser_plugin_instance_id_, text.utf16(), std_underlines,
+      replacement_range, relative_cursor_pos));
   // TODO(kochi): This assumes the IPC handling always succeeds.
   return true;
 }
diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h
index 47900ad..9893064 100644
--- a/content/renderer/browser_plugin/browser_plugin.h
+++ b/content/renderer/browser_plugin/browser_plugin.h
@@ -113,11 +113,13 @@
   bool setComposition(
       const blink::WebString& text,
       const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+      const blink::WebRange& replacementRange,
       int selectionStart,
       int selectionEnd) override;
   bool commitText(
       const blink::WebString& text,
       const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+      const blink::WebRange& replacementRange,
       int relative_cursor_pos) override;
   bool finishComposingText(
       blink::WebInputMethodController::ConfirmCompositionBehavior
diff --git a/content/renderer/frame_owner_properties.cc b/content/renderer/frame_owner_properties.cc
index bd0b4ce..9941cec 100644
--- a/content/renderer/frame_owner_properties.cc
+++ b/content/renderer/frame_owner_properties.cc
@@ -23,9 +23,6 @@
   result.allow_fullscreen = web_frame_owner_properties.allowFullscreen;
   result.allow_payment_request = web_frame_owner_properties.allowPaymentRequest;
   result.required_csp = web_frame_owner_properties.requiredCsp.utf8();
-  std::copy(web_frame_owner_properties.delegatedPermissions.begin(),
-            web_frame_owner_properties.delegatedPermissions.end(),
-            std::back_inserter(result.delegated_permissions));
   std::copy(web_frame_owner_properties.allowedFeatures.begin(),
             web_frame_owner_properties.allowedFeatures.end(),
             std::back_inserter(result.allowed_features));
@@ -46,8 +43,6 @@
   result.allowPaymentRequest = frame_owner_properties.allow_payment_request;
   result.requiredCsp =
       blink::WebString::fromUTF8(frame_owner_properties.required_csp);
-  result.delegatedPermissions = blink::WebVector<blink::mojom::PermissionName>(
-      frame_owner_properties.delegated_permissions);
   result.allowedFeatures = blink::WebVector<blink::WebFeaturePolicyFeature>(
       frame_owner_properties.allowed_features);
 
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.cc b/content/renderer/media/media_stream_constraints_util_video_content.cc
new file mode 100644
index 0000000..e13c2439
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_video_content.cc
@@ -0,0 +1,331 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/media_stream_constraints_util_video_content.h"
+
+#include <cmath>
+#include <utility>
+#include <vector>
+
+#include "content/renderer/media/media_stream_constraints_util_sets.h"
+#include "content/renderer/media/media_stream_video_source.h"
+#include "media/base/limits.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace content {
+
+namespace {
+
+using Point = ResolutionSet::Point;
+using StringSet = DiscreteSet<std::string>;
+using BoolSet = DiscreteSet<bool>;
+
+// Hard upper and lower bound frame rates for tab/desktop capture.
+constexpr double kMaxScreenCastFrameRate = 120.0;
+constexpr double kMinScreenCastFrameRate = 1.0 / 60.0;
+
+constexpr double kDefaultFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
+
+constexpr int kMinScreenCastDimension = 1;
+constexpr int kMaxScreenCastDimension = media::limits::kMaxDimension - 1;
+constexpr double kMinScreenCastAspectRatio =
+    static_cast<double>(kMinScreenCastDimension) /
+    static_cast<double>(kMaxScreenCastDimension);
+constexpr double kMaxScreenCastAspectRatio =
+    static_cast<double>(kMaxScreenCastDimension) /
+    static_cast<double>(kMinScreenCastDimension);
+
+StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) {
+  if (!constraint.hasExact())
+    return StringSet::UniversalSet();
+
+  std::vector<std::string> elements;
+  for (const auto& entry : constraint.exact())
+    elements.push_back(entry.ascii());
+
+  return StringSet(std::move(elements));
+}
+
+BoolSet BoolSetFromConstraint(const blink::BooleanConstraint& constraint) {
+  if (!constraint.hasExact())
+    return BoolSet::UniversalSet();
+
+  return BoolSet({constraint.exact()});
+}
+
+using DoubleRangeSet = NumericRangeSet<double>;
+
+class VideoContentCaptureCandidates {
+ public:
+  VideoContentCaptureCandidates()
+      : device_id_set(StringSet::UniversalSet()),
+        noise_reduction_set(BoolSet::UniversalSet()) {}
+  explicit VideoContentCaptureCandidates(
+      const blink::WebMediaTrackConstraintSet& constraint_set)
+      : resolution_set(ResolutionSet::FromConstraintSet(constraint_set)),
+        frame_rate_set(
+            DoubleRangeSet::FromConstraint(constraint_set.frameRate)),
+        device_id_set(StringSetFromConstraint(constraint_set.deviceId)),
+        noise_reduction_set(
+            BoolSetFromConstraint(constraint_set.googNoiseReduction)) {}
+
+  VideoContentCaptureCandidates(VideoContentCaptureCandidates&& other) =
+      default;
+  VideoContentCaptureCandidates& operator=(
+      VideoContentCaptureCandidates&& other) = default;
+
+  bool IsEmpty() const {
+    return resolution_set.IsEmpty() || frame_rate_set.IsEmpty() ||
+           device_id_set.IsEmpty() || noise_reduction_set.IsEmpty();
+  }
+
+  VideoContentCaptureCandidates Intersection(
+      const VideoContentCaptureCandidates& other) {
+    VideoContentCaptureCandidates intersection;
+    intersection.resolution_set =
+        resolution_set.Intersection(other.resolution_set);
+    intersection.frame_rate_set =
+        frame_rate_set.Intersection(other.frame_rate_set);
+    intersection.device_id_set =
+        device_id_set.Intersection(other.device_id_set);
+    intersection.noise_reduction_set =
+        noise_reduction_set.Intersection(other.noise_reduction_set);
+    return intersection;
+  }
+
+  ResolutionSet resolution_set;
+  DoubleRangeSet frame_rate_set;
+  StringSet device_id_set;
+  BoolSet noise_reduction_set;
+};
+
+ResolutionSet ScreenCastResolutionCapabilities() {
+  return ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension,
+                       kMinScreenCastDimension, kMaxScreenCastDimension,
+                       kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio);
+}
+
+// TODO(guidou): Update this policy to better match the way
+// WebContentsCaptureMachine::ComputeOptimalViewSize() interprets
+// resolution-change policies. See http://crbug.com/701302.
+media::ResolutionChangePolicy SelectResolutionPolicyFromCandidates(
+    const ResolutionSet& resolution_set) {
+  ResolutionSet capabilities = ScreenCastResolutionCapabilities();
+  bool can_adjust_resolution =
+      resolution_set.min_height() <= capabilities.min_height() &&
+      resolution_set.max_height() >= capabilities.max_height() &&
+      resolution_set.min_width() <= capabilities.min_width() &&
+      resolution_set.max_width() >= capabilities.max_width() &&
+      resolution_set.min_aspect_ratio() <= capabilities.min_aspect_ratio() &&
+      resolution_set.max_aspect_ratio() >= capabilities.max_aspect_ratio();
+
+  return can_adjust_resolution ? media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
+                               : media::RESOLUTION_POLICY_FIXED_RESOLUTION;
+}
+
+int RoundToInt(double d) {
+  return static_cast<int>(std::round(d));
+}
+
+gfx::Size ToGfxSize(const Point& point) {
+  return gfx::Size(RoundToInt(point.width()), RoundToInt(point.height()));
+}
+
+double SelectFrameRateFromCandidates(
+    const DoubleRangeSet& candidate_set,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+  double frame_rate = basic_constraint_set.frameRate.hasIdeal()
+                          ? basic_constraint_set.frameRate.ideal()
+                          : kDefaultFrameRate;
+  if (frame_rate > candidate_set.Max())
+    frame_rate = candidate_set.Max();
+  else if (frame_rate < candidate_set.Min())
+    frame_rate = candidate_set.Min();
+
+  return frame_rate;
+}
+
+media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates(
+    const VideoContentCaptureCandidates& candidates,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+  double requested_frame_rate = SelectFrameRateFromCandidates(
+      candidates.frame_rate_set, basic_constraint_set);
+  Point requested_resolution =
+      candidates.resolution_set.SelectClosestPointToIdeal(basic_constraint_set);
+  media::VideoCaptureParams params;
+  params.requested_format = media::VideoCaptureFormat(
+      ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
+      media::PIXEL_FORMAT_I420);
+  params.resolution_change_policy =
+      SelectResolutionPolicyFromCandidates(candidates.resolution_set);
+  // Content capture always uses default power-line frequency.
+  DCHECK(params.IsValid());
+
+  return params;
+}
+
+std::string SelectDeviceIDFromCandidates(
+    const StringSet& candidates,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+  DCHECK(!candidates.IsEmpty());
+  if (basic_constraint_set.deviceId.hasIdeal()) {
+    // If there are multiple elements specified by ideal, break ties by choosing
+    // the first one that satisfies the constraints.
+    for (const auto& ideal_entry : basic_constraint_set.deviceId.ideal()) {
+      std::string ideal_value = ideal_entry.ascii();
+      if (candidates.Contains(ideal_value)) {
+        return ideal_value;
+      }
+    }
+  }
+
+  // Return the empty string if nothing is specified in the constraints.
+  // The empty string is treated as a default device ID by the browser.
+  if (candidates.is_universal()) {
+    return std::string();
+  }
+
+  // If there are multiple elements that satisfy the constraints, break ties by
+  // using the element that was specified first.
+  return candidates.FirstElement();
+}
+
+rtc::Optional<bool> SelectNoiseReductionFromCandidates(
+    const BoolSet& candidates,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+  DCHECK(!candidates.IsEmpty());
+  if (basic_constraint_set.googNoiseReduction.hasIdeal() &&
+      candidates.Contains(basic_constraint_set.googNoiseReduction.ideal())) {
+    return rtc::Optional<bool>(basic_constraint_set.googNoiseReduction.ideal());
+  }
+
+  if (candidates.is_universal())
+    return rtc::Optional<bool>();
+
+  // A non-universal BoolSet can have at most one element.
+  return rtc::Optional<bool>(candidates.FirstElement());
+}
+
+VideoContentCaptureSourceSelectionResult SelectResultFromCandidates(
+    const VideoContentCaptureCandidates& candidates,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+  std::string device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
+                                                       basic_constraint_set);
+  media::VideoCaptureParams capture_params =
+      SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set);
+
+  rtc::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates(
+      candidates.noise_reduction_set, basic_constraint_set);
+
+  return VideoContentCaptureSourceSelectionResult(
+      std::move(device_id), noise_reduction, capture_params);
+}
+
+VideoContentCaptureSourceSelectionResult UnsatisfiedConstraintsResult(
+    const VideoContentCaptureCandidates& candidates,
+    const blink::WebMediaTrackConstraintSet& constraint_set) {
+  DCHECK(candidates.IsEmpty());
+  if (candidates.resolution_set.IsHeightEmpty()) {
+    return VideoContentCaptureSourceSelectionResult(
+        constraint_set.height.name());
+  } else if (candidates.resolution_set.IsWidthEmpty()) {
+    return VideoContentCaptureSourceSelectionResult(
+        constraint_set.width.name());
+  } else if (candidates.resolution_set.IsAspectRatioEmpty()) {
+    return VideoContentCaptureSourceSelectionResult(
+        constraint_set.aspectRatio.name());
+  } else if (candidates.frame_rate_set.IsEmpty()) {
+    return VideoContentCaptureSourceSelectionResult(
+        constraint_set.frameRate.name());
+  } else if (candidates.noise_reduction_set.IsEmpty()) {
+    return VideoContentCaptureSourceSelectionResult(
+        constraint_set.googNoiseReduction.name());
+  } else {
+    DCHECK(candidates.device_id_set.IsEmpty());
+    return VideoContentCaptureSourceSelectionResult(
+        constraint_set.deviceId.name());
+  }
+}
+
+}  // namespace
+
+VideoContentCaptureSourceSelectionResult::
+    VideoContentCaptureSourceSelectionResult(const char* failed_constraint_name)
+    : failed_constraint_name_(failed_constraint_name) {}
+
+VideoContentCaptureSourceSelectionResult::
+    VideoContentCaptureSourceSelectionResult(
+        std::string device_id,
+        const rtc::Optional<bool>& noise_reduction,
+        media::VideoCaptureParams capture_params)
+    : failed_constraint_name_(nullptr),
+      device_id_(std::move(device_id)),
+      noise_reduction_(noise_reduction),
+      capture_params_(capture_params) {}
+
+VideoContentCaptureSourceSelectionResult::
+    VideoContentCaptureSourceSelectionResult(
+        const VideoContentCaptureSourceSelectionResult& other) = default;
+VideoContentCaptureSourceSelectionResult::
+    VideoContentCaptureSourceSelectionResult(
+        VideoContentCaptureSourceSelectionResult&& other) = default;
+VideoContentCaptureSourceSelectionResult::
+    ~VideoContentCaptureSourceSelectionResult() = default;
+VideoContentCaptureSourceSelectionResult&
+VideoContentCaptureSourceSelectionResult::operator=(
+    const VideoContentCaptureSourceSelectionResult& other) = default;
+VideoContentCaptureSourceSelectionResult&
+VideoContentCaptureSourceSelectionResult::operator=(
+    VideoContentCaptureSourceSelectionResult&& other) = default;
+
+int VideoContentCaptureSourceSelectionResult::Height() const {
+  DCHECK(HasValue());
+  return capture_params_.requested_format.frame_size.height();
+}
+
+int VideoContentCaptureSourceSelectionResult::Width() const {
+  DCHECK(HasValue());
+  return capture_params_.requested_format.frame_size.width();
+}
+
+float VideoContentCaptureSourceSelectionResult::FrameRate() const {
+  DCHECK(HasValue());
+  return capture_params_.requested_format.frame_rate;
+}
+
+media::ResolutionChangePolicy
+VideoContentCaptureSourceSelectionResult::ResolutionChangePolicy() const {
+  DCHECK(HasValue());
+  return capture_params_.resolution_change_policy;
+}
+
+VideoContentCaptureSourceSelectionResult
+SelectVideoContentCaptureSourceSettings(
+    const blink::WebMediaConstraints& constraints) {
+  VideoContentCaptureCandidates candidates;
+  candidates.resolution_set = ScreenCastResolutionCapabilities();
+  candidates.frame_rate_set =
+      DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate);
+  // candidates.device_id_set and candidates.noise_reduction_set are
+  // automatically initialized with the universal set.
+
+  candidates = candidates.Intersection(
+      VideoContentCaptureCandidates(constraints.basic()));
+  if (candidates.IsEmpty())
+    return UnsatisfiedConstraintsResult(candidates, constraints.basic());
+
+  for (const auto& advanced_set : constraints.advanced()) {
+    VideoContentCaptureCandidates advanced_candidates(advanced_set);
+    VideoContentCaptureCandidates intersection =
+        candidates.Intersection(advanced_candidates);
+    if (!intersection.IsEmpty())
+      candidates = std::move(intersection);
+  }
+
+  DCHECK(!candidates.IsEmpty());
+  return SelectResultFromCandidates(candidates, constraints.basic());
+}
+
+}  // namespace content
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.h b/content/renderer/media/media_stream_constraints_util_video_content.h
new file mode 100644
index 0000000..250b4ecd
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_video_content.h
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_VIDEO_CONTENT_H_
+#define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_VIDEO_CONTENT_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "content/common/content_export.h"
+#include "media/capture/video_capture_types.h"
+#include "third_party/webrtc/base/optional.h"
+
+namespace blink {
+class WebMediaConstraints;
+}
+
+namespace content {
+
+class CONTENT_EXPORT VideoContentCaptureSourceSelectionResult {
+ public:
+  // Creates a result without value and with the given |failed_constraint_name|.
+  // Does not take ownership of |failed_constraint_name|, so it must be null or
+  // point to a string that remains accessible.
+  explicit VideoContentCaptureSourceSelectionResult(
+      const char* failed_constraint_name);
+
+  // Creates a result with the given values. |device_id| is moved to an internal
+  // field.
+  VideoContentCaptureSourceSelectionResult(
+      std::string device_id,
+      const rtc::Optional<bool>& noise_reduction,
+      media::VideoCaptureParams capture_params);
+
+  VideoContentCaptureSourceSelectionResult(
+      const VideoContentCaptureSourceSelectionResult& other);
+  VideoContentCaptureSourceSelectionResult& operator=(
+      const VideoContentCaptureSourceSelectionResult& other);
+  VideoContentCaptureSourceSelectionResult(
+      VideoContentCaptureSourceSelectionResult&& other);
+  VideoContentCaptureSourceSelectionResult& operator=(
+      VideoContentCaptureSourceSelectionResult&& other);
+  ~VideoContentCaptureSourceSelectionResult();
+
+  bool HasValue() const { return failed_constraint_name_ == nullptr; }
+
+  // Accessors.
+  const char* failed_constraint_name() const { return failed_constraint_name_; }
+  const std::string& device_id() const {
+    DCHECK(HasValue());
+    return device_id_;
+  }
+  const rtc::Optional<bool>& noise_reduction() const {
+    DCHECK(HasValue());
+    return noise_reduction_;
+  }
+  media::VideoCaptureParams capture_params() const {
+    DCHECK(HasValue());
+    return capture_params_;
+  }
+
+  // Convenience accessors for fields embedded in the |capture_params_| field.
+  int Height() const;
+  int Width() const;
+  float FrameRate() const;
+  media::ResolutionChangePolicy ResolutionChangePolicy() const;
+
+ private:
+  const char* failed_constraint_name_;
+  std::string device_id_;
+  rtc::Optional<bool> noise_reduction_;
+  media::VideoCaptureParams capture_params_;
+};
+
+// This function performs source and source-settings selection for content
+// video capture based on the given |constraints|.
+VideoContentCaptureSourceSelectionResult CONTENT_EXPORT
+SelectVideoContentCaptureSourceSettings(
+    const blink::WebMediaConstraints& constraints);
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_VIDEO_CONTENT_H_
diff --git a/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc b/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
new file mode 100644
index 0000000..212391e
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
@@ -0,0 +1,1661 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/media_stream_constraints_util_video_content.h"
+
+#include <cmath>
+#include <string>
+
+#include "content/renderer/media/media_stream_video_source.h"
+#include "content/renderer/media/mock_constraint_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+
+namespace content {
+
+namespace {
+
+void CheckNonResolutionDefaults(
+    const VideoContentCaptureSourceSelectionResult& result) {
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+  EXPECT_EQ(std::string(), result.device_id());
+}
+
+void CheckNonFrameRateDefaults(
+    const VideoContentCaptureSourceSelectionResult& result) {
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+  EXPECT_EQ(std::string(), result.device_id());
+}
+
+}  // namespace
+
+class MediaStreamConstraintsUtilVideoContentTest : public testing::Test {
+ protected:
+  VideoContentCaptureSourceSelectionResult SelectSettings() {
+    blink::WebMediaConstraints constraints =
+        constraint_factory_.CreateWebMediaConstraints();
+    return SelectVideoContentCaptureSourceSettings(constraints);
+  }
+
+  MockConstraintFactory constraint_factory_;
+};
+
+// The Unconstrained test checks the default selection criteria.
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, Unconstrained) {
+  constraint_factory_.Reset();
+  auto result = SelectSettings();
+
+  // All settings should have default values.
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+// The "Overconstrained" tests verify that failure of any single required
+// constraint results in failure to select a candidate.
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, OverconstrainedOnHeight) {
+  constraint_factory_.Reset();
+  constraint_factory_.basic().height.setExact(123467890);
+  auto result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().height.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().height.setMin(123467890);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().height.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().height.setMax(0);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().height.name(),
+            result.failed_constraint_name());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, OverconstrainedOnWidth) {
+  constraint_factory_.Reset();
+  constraint_factory_.basic().width.setExact(123467890);
+  auto result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().width.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().width.setMin(123467890);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().width.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().width.setMax(0);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().width.name(),
+            result.failed_constraint_name());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       OverconstrainedOnAspectRatio) {
+  constraint_factory_.Reset();
+  constraint_factory_.basic().aspectRatio.setExact(123467890);
+  auto result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().aspectRatio.setMin(123467890);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().aspectRatio.setMax(0.00001);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
+            result.failed_constraint_name());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, OverconstrainedOnFrameRate) {
+  constraint_factory_.Reset();
+  constraint_factory_.basic().frameRate.setExact(123467890.0);
+  auto result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().frameRate.setMin(123467890.0);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
+            result.failed_constraint_name());
+
+  constraint_factory_.Reset();
+  constraint_factory_.basic().frameRate.setMax(0.0);
+  result = SelectSettings();
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
+            result.failed_constraint_name());
+}
+
+// The "Mandatory" and "Ideal" tests check that various selection criteria work
+// for each individual constraint in the basic constraint set.
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryDeviceID) {
+  const std::string kDeviceID = "Some ID";
+  constraint_factory_.Reset();
+  constraint_factory_.basic().deviceId.setExact(
+      blink::WebString::fromASCII(kDeviceID));
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(kDeviceID, result.device_id());
+  // Other settings should have default values.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, IdealDeviceID) {
+  const std::string kDeviceID = "Some ID";
+  const std::string kIdealID = "Ideal ID";
+  blink::WebVector<blink::WebString> device_ids(static_cast<size_t>(2));
+  device_ids[0] = blink::WebString::fromASCII(kDeviceID);
+  device_ids[1] = blink::WebString::fromASCII(kIdealID);
+  constraint_factory_.Reset();
+  constraint_factory_.basic().deviceId.setExact(device_ids);
+
+  blink::WebVector<blink::WebString> ideal_id(static_cast<size_t>(1));
+  ideal_id[0] = blink::WebString::fromASCII(kIdealID);
+  constraint_factory_.basic().deviceId.setIdeal(ideal_id);
+
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(kIdealID, result.device_id());
+  // Other settings should have default values.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryNoiseReduction) {
+  constraint_factory_.Reset();
+  const bool kNoiseReductionValues[] = {true, false};
+  for (auto noise_reduction : kNoiseReductionValues) {
+    constraint_factory_.basic().googNoiseReduction.setExact(noise_reduction);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(noise_reduction, result.noise_reduction());
+    // Other settings should have default values.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+    EXPECT_EQ(std::string(), result.device_id());
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, IdealNoiseReduction) {
+  constraint_factory_.Reset();
+  const bool kNoiseReductionValues[] = {true, false};
+  for (auto noise_reduction : kNoiseReductionValues) {
+    constraint_factory_.basic().googNoiseReduction.setIdeal(noise_reduction);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(noise_reduction, result.noise_reduction());
+    // Other settings should have default values.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+    EXPECT_EQ(std::string(), result.device_id());
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryExactHeight) {
+  constraint_factory_.Reset();
+  const int kHeight = 1000;
+  constraint_factory_.basic().height.setExact(kHeight);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(kHeight, result.Height());
+  // The algorithm tries to preserve the default aspect ratio.
+  EXPECT_EQ(std::round(kHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+            result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMinHeight) {
+  constraint_factory_.Reset();
+  const int kHeight = 1000;
+  constraint_factory_.basic().height.setMin(kHeight);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kHeight is greater that the default, so expect kHeight.
+  EXPECT_EQ(kHeight, result.Height());
+  EXPECT_EQ(std::round(kHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+            result.Width());
+  CheckNonResolutionDefaults(result);
+
+  const int kSmallHeight = 100;
+  constraint_factory_.basic().height.setMin(kSmallHeight);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kSmallHeight is less that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxHeight) {
+  constraint_factory_.Reset();
+  const int kHeight = 1000;
+  constraint_factory_.basic().height.setMax(kHeight);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kHeight is greater that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+
+  const int kSmallHeight = 100;
+  constraint_factory_.basic().height.setMax(kSmallHeight);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kSmallHeight is less that the default, so expect kSmallHeight.
+  EXPECT_EQ(kSmallHeight, result.Height());
+  EXPECT_EQ(
+      std::round(kSmallHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+      result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryHeightRange) {
+  constraint_factory_.Reset();
+  {
+    const int kMinHeight = 300;
+    const int kMaxHeight = 1000;
+    constraint_factory_.basic().height.setMin(kMinHeight);
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The range includes the default, so expect the default.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  {
+    const int kMinHeight = 900;
+    const int kMaxHeight = 1000;
+    constraint_factory_.basic().height.setMin(kMinHeight);
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is greater than the default, so expect the range minimum.
+    EXPECT_EQ(kMinHeight, result.Height());
+    EXPECT_EQ(
+        std::round(kMinHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  {
+    const int kMinHeight = 300;
+    const int kMaxHeight = 400;
+    constraint_factory_.basic().height.setMin(kMinHeight);
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is less than the default, so expect the range maximum.
+    EXPECT_EQ(kMaxHeight, result.Height());
+    EXPECT_EQ(
+        std::round(kMaxHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, IdealHeight) {
+  // Unconstrained.
+  {
+    constraint_factory_.Reset();
+    const int kIdealHeight = 1000;
+    constraint_factory_.basic().height.setIdeal(kIdealHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealHeight, result.Height());
+    // When ideal height is given, the algorithm returns a width that is closest
+    // to height * kDefaultAspectRatio.
+    EXPECT_EQ(
+        std::round(kIdealHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal greater than maximum.
+  {
+    constraint_factory_.Reset();
+    const int kIdealHeight = 1000;
+    const int kMaxHeight = 800;
+    constraint_factory_.basic().height.setIdeal(kIdealHeight);
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal height is greater than the maximum, expect maximum.
+    EXPECT_EQ(kMaxHeight, result.Height());
+    // Expect closest to kMaxHeight * kDefaultAspectRatio.
+    EXPECT_EQ(
+        std::round(kMaxHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal less than minimum.
+  {
+    constraint_factory_.Reset();
+    const int kIdealHeight = 1000;
+    const int kMinHeight = 1200;
+    constraint_factory_.basic().height.setIdeal(kIdealHeight);
+    constraint_factory_.basic().height.setMin(kMinHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal height is less than the minimum, expect minimum.
+    EXPECT_EQ(kMinHeight, result.Height());
+    // Expect closest to kMinHeight * kDefaultAspectRatio.
+    EXPECT_EQ(
+        std::round(kMinHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal intersects a box.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMin(500);
+    constraint_factory_.basic().height.setMax(1000);
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    const int kIdealHeight = 750;
+    constraint_factory_.basic().height.setIdeal(kIdealHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal height is included in the bounding box.
+    EXPECT_EQ(kIdealHeight, result.Height());
+    // Expect width closest to kIdealHeight * kDefaultAspectRatio, which is
+    // outside the box. Closest is max width.
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().width.setMin(1200);
+    constraint_factory_.basic().width.setMax(2000);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealHeight, result.Height());
+    // kIdealHeight * kDefaultAspectRatio is outside the box. Closest is
+    // min width.
+    EXPECT_EQ(constraint_factory_.basic().width.min(), result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealHeight, result.Height());
+    // kIdealHeight * kDefaultAspectRatio is outside the box. Closest is
+    // max width.
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal outside the box, closest to the side coinciding with max height.
+  {
+    const int kMaxHeight = 1000;
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMin(500);
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    constraint_factory_.basic().height.setIdeal(1200);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxHeight, result.Height());
+    // Expect width closest to kMaxHeight * kDefaultAspectRatio, which is
+    // outside the box. Closest it max width.
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().width.setMin(1500);
+    constraint_factory_.basic().width.setMax(2000);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxHeight, result.Height());
+    // kMaxHeight * kDefaultAspectRatio is outside the box. Closest is min
+    // width.
+    EXPECT_EQ(constraint_factory_.basic().width.min(), result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().width.setMin(100);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxHeight, result.Height());
+    // kMaxHeight * kDefaultAspectRatio is within the width limits.
+    EXPECT_EQ(
+        std::round(kMaxHeight * MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal outside the constrained set, closest to a single point.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMin(500);
+    constraint_factory_.basic().height.setMax(1000);
+    constraint_factory_.basic().width.setMin(500);
+    constraint_factory_.basic().width.setMax(1000);
+    constraint_factory_.basic().aspectRatio.setMin(1.0);
+    constraint_factory_.basic().height.setIdeal(1200);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // (max-height, max-width) is the single point closest to the ideal line.
+    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryExactWidth) {
+  constraint_factory_.Reset();
+  const int kWidth = 1000;
+  constraint_factory_.basic().width.setExact(kWidth);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(kWidth, result.Width());
+  EXPECT_EQ(std::round(kWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+            result.Height());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMinWidth) {
+  constraint_factory_.Reset();
+  const int kWidth = 1000;
+  constraint_factory_.basic().width.setMin(kWidth);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kWidth is greater that the default, so expect kWidth.
+  EXPECT_EQ(kWidth, result.Width());
+  EXPECT_EQ(std::round(kWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+            result.Height());
+  CheckNonResolutionDefaults(result);
+
+  const int kSmallWidth = 100;
+  constraint_factory_.basic().width.setMin(kSmallWidth);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kSmallWidth is less that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxWidth) {
+  constraint_factory_.Reset();
+  const int kWidth = 1000;
+  constraint_factory_.basic().width.setMax(kWidth);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kWidth is greater that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  CheckNonResolutionDefaults(result);
+
+  const int kSmallWidth = 100;
+  constraint_factory_.basic().width.setMax(kSmallWidth);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kSmallWidth is less that the default, so expect kSmallWidth.
+  EXPECT_EQ(kSmallWidth, result.Width());
+  EXPECT_EQ(
+      std::round(kSmallWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+      result.Height());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryWidthRange) {
+  constraint_factory_.Reset();
+  {
+    const int kMinWidth = 300;
+    const int kMaxWidth = 1000;
+    constraint_factory_.basic().width.setMin(kMinWidth);
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The range includes the default, so expect the default.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  {
+    const int kMinWidth = 900;
+    const int kMaxWidth = 1000;
+    constraint_factory_.basic().width.setMin(kMinWidth);
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is greater than the default, so expect the range minimum.
+    EXPECT_EQ(kMinWidth, result.Width());
+    EXPECT_EQ(
+        std::round(kMinWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  {
+    const int kMinWidth = 300;
+    const int kMaxWidth = 400;
+    constraint_factory_.basic().width.setMin(kMinWidth);
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is less than the default, so expect the range maximum.
+    EXPECT_EQ(kMaxWidth, result.Width());
+    EXPECT_EQ(
+        std::round(kMaxWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, IdealWidth) {
+  // Unconstrained
+  {
+    constraint_factory_.Reset();
+    const int kIdealWidth = 1000;
+    constraint_factory_.basic().width.setIdeal(kIdealWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealWidth, result.Width());
+    // When ideal width is given, the algorithm returns a height that is closest
+    // to width / kDefaultAspectRatio.
+    EXPECT_EQ(
+        std::round(kIdealWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal greater than maximum.
+  {
+    constraint_factory_.Reset();
+    const int kIdealWidth = 1000;
+    const int kMaxWidth = 800;
+    constraint_factory_.basic().width.setIdeal(kIdealWidth);
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxWidth, result.Width());
+    // Expect closest to kMaxWidth / kDefaultAspectRatio.
+    EXPECT_EQ(
+        std::round(kMaxWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal less than minimum.
+  {
+    constraint_factory_.Reset();
+    const int kIdealWidth = 1000;
+    const int kMinWidth = 1200;
+    constraint_factory_.basic().width.setIdeal(kIdealWidth);
+    constraint_factory_.basic().width.setMin(kMinWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMinWidth, result.Width());
+    // Expect closest to kMinWidth / kDefaultAspectRatio.
+    EXPECT_EQ(
+        std::round(kMinWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal intersects a box.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setMin(500);
+    constraint_factory_.basic().width.setMax(1000);
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    const int kIdealWidth = 750;
+    constraint_factory_.basic().width.setIdeal(kIdealWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal width is included in the bounding box.
+    EXPECT_EQ(kIdealWidth, result.Width());
+    // Expect height closest to kIdealWidth / kDefaultAspectRatio, which is
+    // outside the box. Closest is max height.
+    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().height.setMin(1200);
+    constraint_factory_.basic().height.setMax(2000);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealWidth, result.Width());
+    // kIdealWidth / kDefaultAspectRatio outside the box. Closest is
+    // min height.
+    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealWidth, result.Width());
+    // kIdealWidth / kDefaultAspectRatio is outside the box. Closest is max
+    // height.
+    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal outside the box, closest to the side coinciding with max width.
+  {
+    const int kMaxWidth = 1000;
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setMin(500);
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    constraint_factory_.basic().width.setIdeal(1200);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxWidth, result.Width());
+    // kMaxWidth / kDefaultAspectRatio is outside the box. Closest is max
+    // height.
+    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().height.setMin(1500);
+    constraint_factory_.basic().height.setMax(2000);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxWidth, result.Width());
+    // kMaxWidth / kDefaultAspectRatio is outside the box. Closest is
+    // min height.
+    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().height.setMin(100);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxWidth, result.Width());
+    // kMaxWidth / kDefaultAspectRatio is within the height limits.
+    EXPECT_EQ(
+        std::round(kMaxWidth / MediaStreamVideoSource::kDefaultAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal outside the constrained set, closest to a single point.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    constraint_factory_.basic().aspectRatio.setMax(1.0);
+    constraint_factory_.basic().width.setIdeal(1200);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // (max-width, max-height) is the single point closest to the ideal line.
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryExactAspectRatio) {
+  constraint_factory_.Reset();
+  const double kAspectRatio = 2.0;
+  constraint_factory_.basic().aspectRatio.setExact(kAspectRatio);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // Given that the default aspect ratio cannot be preserved, the algorithm
+  // tries to preserve, among the default height or width, the one that leads
+  // to highest area. In this case, height is preserved.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(std::round(MediaStreamVideoSource::kDefaultHeight * kAspectRatio),
+            result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMinAspectRatio) {
+  constraint_factory_.Reset();
+  const double kAspectRatio = 2.0;
+  constraint_factory_.basic().aspectRatio.setMin(kAspectRatio);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kAspectRatio is greater that the default, so expect kAspectRatio.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(std::round(MediaStreamVideoSource::kDefaultHeight * kAspectRatio),
+            result.Width());
+  CheckNonResolutionDefaults(result);
+
+  const double kSmallAspectRatio = 0.5;
+  constraint_factory_.basic().aspectRatio.setMin(kSmallAspectRatio);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kSmallAspectRatio is less that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxAspectRatio) {
+  constraint_factory_.Reset();
+  const double kAspectRatio = 2.0;
+  constraint_factory_.basic().aspectRatio.setMax(kAspectRatio);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kAspectRatio is greater that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+
+  const double kSmallAspectRatio = 0.5;
+  constraint_factory_.basic().aspectRatio.setMax(kSmallAspectRatio);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kSmallAspectRatio is less that the default, so expect kSmallAspectRatio.
+  // Prefer to preserve default width since that leads to larger area than
+  // preserving default height.
+  EXPECT_EQ(
+      std::round(MediaStreamVideoSource::kDefaultWidth / kSmallAspectRatio),
+      result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryRangeAspectRatio) {
+  constraint_factory_.Reset();
+  {
+    const double kMinAspectRatio = 0.5;
+    const double kMaxAspectRatio = 2.0;
+    constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Range includes default, so expect the default.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  {
+    const double kMinAspectRatio = 2.0;
+    const double kMaxAspectRatio = 3.0;
+    constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is greater than the default. Expect the minimum.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(
+        std::round(MediaStreamVideoSource::kDefaultHeight * kMinAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  {
+    const double kMinAspectRatio = 0.5;
+    const double kMaxAspectRatio = 1.0;
+    constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is less than the default. Expect the maximum.
+    EXPECT_EQ(
+        std::round(MediaStreamVideoSource::kDefaultWidth / kMaxAspectRatio),
+        result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, IdealAspectRatio) {
+  // Unconstrained.
+  {
+    constraint_factory_.Reset();
+    const double kIdealAspectRatio = 2.0;
+    constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(
+        std::round(MediaStreamVideoSource::kDefaultHeight * kIdealAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal greater than maximum.
+  {
+    constraint_factory_.Reset();
+    const double kIdealAspectRatio = 2.0;
+    const double kMaxAspectRatio = 1.5;
+    constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal height is greater than the maximum, expect maximum.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(
+        std::round(MediaStreamVideoSource::kDefaultHeight * kMaxAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal less than minimum.
+  {
+    constraint_factory_.Reset();
+    const double kIdealAspectRatio = 1.0;
+    const double kMinAspectRatio = 1.5;
+    constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal height is greater than the maximum, expect maximum.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(
+        std::round(MediaStreamVideoSource::kDefaultHeight * kMinAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal intersects a box.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    const int kIdealAspectRatio = 2.0;
+    constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal aspect-ratio is included in the bounding box, with the value
+    // closest to a standard width or height being the cut with the maximum
+    // width.
+    EXPECT_EQ(
+        std::round(constraint_factory_.basic().width.max() / kIdealAspectRatio),
+        result.Height());
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().height.setMin(1000);
+    constraint_factory_.basic().height.setMax(5000);
+    constraint_factory_.basic().width.setMin(1000);
+    constraint_factory_.basic().width.setMax(5000);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal aspect-ratio is included in the bounding box, with the value
+    // closest to a standard width or height and largest area being the cut with
+    // the minimum height.
+    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
+    EXPECT_EQ(std::round(constraint_factory_.basic().height.min() *
+                         kIdealAspectRatio),
+              result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().height.setMin(250);
+    constraint_factory_.basic().height.setMax(5000);
+    constraint_factory_.basic().width.setMin(250);
+    constraint_factory_.basic().width.setMax(5000);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal aspect-ratio and default width and height are included in the
+    // bounding box. Preserving default height leads to larger area than
+    // preserving default width.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight * kIdealAspectRatio,
+              result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal outside the constrained area, closest to min or max aspect ratio.
+  {
+    const double kMinAspectRatio = 0.5;
+    const double kMaxAspectRatio = 2.0;
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+    constraint_factory_.basic().aspectRatio.setIdeal(3.0);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal is closest to kMaxAspectRatio.
+    EXPECT_EQ(
+        std::round(constraint_factory_.basic().width.max() / kMaxAspectRatio),
+        result.Height());
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().aspectRatio.setIdeal(0.3);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal is closest to kMinAspectRatio.
+    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    EXPECT_EQ(
+        std::round(constraint_factory_.basic().height.max() * kMinAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+
+    // Use a box that is bigger and further from the origin to force closeness
+    // to a different default dimension.
+    constraint_factory_.basic().height.setMin(1000);
+    constraint_factory_.basic().height.setMax(5000);
+    constraint_factory_.basic().width.setMin(1000);
+    constraint_factory_.basic().width.setMax(5000);
+    constraint_factory_.basic().aspectRatio.setIdeal(3.0);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal is closest to kMaxAspectRatio.
+    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
+    EXPECT_EQ(
+        std::round(constraint_factory_.basic().height.min() * kMaxAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().aspectRatio.setIdeal(0.3);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal is closest to kMinAspectRatio.
+    EXPECT_EQ(
+        std::round(constraint_factory_.basic().width.min() / kMinAspectRatio),
+        result.Height());
+    EXPECT_EQ(constraint_factory_.basic().width.min(), result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+
+  // Ideal outside the constrained area, closest to a single point.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMin(100);
+    constraint_factory_.basic().height.setMax(500);
+    constraint_factory_.basic().width.setMin(100);
+    constraint_factory_.basic().width.setMax(500);
+    constraint_factory_.basic().aspectRatio.setMin(1.0);
+    constraint_factory_.basic().aspectRatio.setIdeal(10.0);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Ideal is closest to the min height and max width.
+    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryExactFrameRate) {
+  constraint_factory_.Reset();
+  const int kFrameRate = 45.0;
+  constraint_factory_.basic().frameRate.setExact(kFrameRate);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(kFrameRate, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMinFrameRate) {
+  constraint_factory_.Reset();
+  const double kFrameRate = 45.0;
+  constraint_factory_.basic().frameRate.setMin(kFrameRate);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kFrameRate is greater that the default, so expect kFrameRate.
+  EXPECT_EQ(kFrameRate, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+
+  const double kSmallFrameRate = 5.0;
+  constraint_factory_.basic().frameRate.setMin(kSmallFrameRate);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kFrameRate is greater that the default, so expect kFrameRate.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxFrameRate) {
+  constraint_factory_.Reset();
+  const double kFrameRate = 45.0;
+  constraint_factory_.basic().frameRate.setMax(kFrameRate);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kFrameRate is greater that the default, so expect the default.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+
+  const double kSmallFrameRate = 5.0;
+  constraint_factory_.basic().frameRate.setMax(kSmallFrameRate);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kFrameRate is less that the default, so expect kFrameRate.
+  EXPECT_EQ(kSmallFrameRate, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryRangeFrameRate) {
+  constraint_factory_.Reset();
+  {
+    const double kMinFrameRate = 15.0;
+    const double kMaxFrameRate = 45.0;
+    constraint_factory_.basic().frameRate.setMax(kMinFrameRate);
+    constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The range includes the default, so expect the default.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+
+  {
+    const double kMinFrameRate = 45.0;
+    const double kMaxFrameRate = 55.0;
+    constraint_factory_.basic().frameRate.setMax(kMinFrameRate);
+    constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is greater that the default, so expect the minimum.
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+
+  {
+    const double kMinFrameRate = 10.0;
+    const double kMaxFrameRate = 15.0;
+    constraint_factory_.basic().frameRate.setMax(kMinFrameRate);
+    constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The whole range is less that the default, so expect the maximum.
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, IdealFrameRate) {
+  // Unconstrained.
+  {
+    constraint_factory_.Reset();
+    const int kIdealFrameRate = 45.0;
+    constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+
+  // Ideal greater than maximum.
+  {
+    constraint_factory_.Reset();
+    const int kIdealFrameRate = 45.0;
+    const int kMaxFrameRate = 30.0;
+    constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+    constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+
+  // Ideal less than minimum.
+  {
+    constraint_factory_.Reset();
+    const int kIdealFrameRate = 45.0;
+    const int kMinFrameRate = 50.0;
+    constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+    constraint_factory_.basic().frameRate.setMin(kMinFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMinFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+
+  // Ideal within range.
+  {
+    constraint_factory_.Reset();
+    const int kIdealFrameRate = 45.0;
+    const int kMinFrameRate = 35.0;
+    const int kMaxFrameRate = 50.0;
+    constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+    constraint_factory_.basic().frameRate.setMin(kMinFrameRate);
+    constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kIdealFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+  }
+}
+
+// The "Advanced" tests check selection criteria involving advanced constraint
+// sets.
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedMinMaxResolutionFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setMin(2000000000);
+  advanced1.height.setMin(2000000000);
+  // The first advanced set cannot be satisfied and is therefore ignored in all
+  // calls to SelectSettings().
+  // In this case, default settings must be selected.
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  CheckNonResolutionDefaults(result);
+
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.height.setMax(400);
+  advanced2.width.setMax(500);
+  advanced2.aspectRatio.setExact(5.0 / 4.0);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(400, result.Height());
+  EXPECT_EQ(500, result.Width());
+  CheckNonResolutionDefaults(result);
+
+  blink::WebMediaTrackConstraintSet& advanced3 =
+      constraint_factory_.AddAdvanced();
+  advanced3.frameRate.setMax(10.0);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // The third advanced set is supported in addition to the previous set.
+  EXPECT_EQ(400, result.Height());
+  EXPECT_EQ(500, result.Width());
+  EXPECT_EQ(10.0, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+  EXPECT_EQ(std::string(), result.device_id());
+
+  blink::WebMediaTrackConstraintSet& advanced4 =
+      constraint_factory_.AddAdvanced();
+  advanced4.width.setExact(1000);
+  advanced4.height.setExact(1000);
+  result = SelectSettings();
+  // The fourth advanced set cannot be supported in combination with the
+  // previous two sets, so it must be ignored.
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(400, result.Height());
+  EXPECT_EQ(500, result.Width());
+  EXPECT_EQ(10.0, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+  EXPECT_EQ(std::string(), result.device_id());
+
+  constraint_factory_.basic().width.setIdeal(100);
+  constraint_factory_.basic().height.setIdeal(100);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // The closest point to (100, 100) that satisfies all previous constraint
+  // sets is its projection on the aspect-ratio line 5.0/4.0.
+  // This is a point m*(4, 5) such that Dot((4,5), (100 - m(4,5))) == 0.
+  // This works out to be m = 900/41.
+  EXPECT_EQ(std::round(4.0 * 900.0 / 41.0), result.Height());
+  EXPECT_EQ(std::round(5.0 * 900.0 / 41.0), result.Width());
+  EXPECT_EQ(10.0, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+  EXPECT_EQ(std::string(), result.device_id());
+
+  constraint_factory_.basic().width.setIdeal(2000);
+  constraint_factory_.basic().height.setIdeal(1500);
+  result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // The projection of (2000,1500) on the aspect-ratio line 5.0/4.0 is beyond
+  // the maximum of (400, 500), so use the maximum allowed resolution.
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(400, result.Height());
+  EXPECT_EQ(500, result.Width());
+  EXPECT_EQ(10.0, result.FrameRate());
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
+  EXPECT_EQ(std::string(), result.device_id());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, AdvancedExactResolution) {
+  {
+    constraint_factory_.Reset();
+    blink::WebMediaTrackConstraintSet& advanced1 =
+        constraint_factory_.AddAdvanced();
+    advanced1.width.setExact(40000000);
+    advanced1.height.setExact(40000000);
+    blink::WebMediaTrackConstraintSet& advanced2 =
+        constraint_factory_.AddAdvanced();
+    advanced2.width.setExact(300000000);
+    advanced2.height.setExact(300000000);
+    auto result = SelectSettings();
+    // None of the constraint sets can be satisfied. Default resolution should
+    // be selected.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    CheckNonResolutionDefaults(result);
+
+    blink::WebMediaTrackConstraintSet& advanced3 =
+        constraint_factory_.AddAdvanced();
+    advanced3.width.setExact(1920);
+    advanced3.height.setExact(1080);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(1920, result.Width());
+    EXPECT_EQ(1080, result.Height());
+    CheckNonResolutionDefaults(result);
+
+    blink::WebMediaTrackConstraintSet& advanced4 =
+        constraint_factory_.AddAdvanced();
+    advanced4.width.setExact(640);
+    advanced4.height.setExact(480);
+    result = SelectSettings();
+    // The fourth constraint set contradicts the third set. The fourth set
+    // should be ignored.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(1920, result.Width());
+    EXPECT_EQ(1080, result.Height());
+    CheckNonResolutionDefaults(result);
+
+    constraint_factory_.basic().width.setIdeal(800);
+    constraint_factory_.basic().height.setIdeal(600);
+    result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // The exact constraints has priority over ideal.
+    EXPECT_EQ(1920, result.Width());
+    EXPECT_EQ(1080, result.Height());
+    CheckNonResolutionDefaults(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedResolutionAndFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setExact(1920);
+  advanced1.height.setExact(1080);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.frameRate.setExact(60.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(1920, result.Width());
+  EXPECT_EQ(1080, result.Height());
+  EXPECT_EQ(60.0, result.FrameRate());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, AdvancedNoiseReduction) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setMin(640);
+  advanced1.height.setMin(480);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.width.setMin(1920);
+  advanced2.height.setMin(1080);
+  advanced2.googNoiseReduction.setExact(false);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(1920, result.Width());
+  // Preserves default aspect ratio.
+  EXPECT_EQ(static_cast<int>(std::round(
+                result.Width() / MediaStreamVideoSource::kDefaultAspectRatio)),
+            result.Height());
+  EXPECT_TRUE(result.noise_reduction() && !*result.noise_reduction());
+}
+
+// The "AdvancedContradictory" tests check that advanced constraint sets that
+// contradict previous constraint sets are ignored.
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryNoiseReduction) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setExact(640);
+  advanced1.height.setExact(480);
+  advanced1.googNoiseReduction.setExact(true);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.width.setExact(1920);
+  advanced2.height.setExact(1080);
+  advanced2.googNoiseReduction.setExact(false);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(640, result.Width());
+  EXPECT_EQ(480, result.Height());
+  EXPECT_TRUE(result.noise_reduction() && *result.noise_reduction());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryExactResolution) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setExact(640);
+  advanced1.height.setExact(480);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.width.setExact(1920);
+  advanced2.height.setExact(1080);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(640, result.Width());
+  EXPECT_EQ(480, result.Height());
+  // Resolution cannot be adjusted due to exact in the first advanced set.
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryMaxMinResolutionFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setMax(640);
+  advanced1.height.setMax(480);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.width.setMin(1920);
+  advanced2.height.setMin(1080);
+  advanced2.frameRate.setExact(60.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(640, result.Width());
+  EXPECT_EQ(480, result.Height());
+  // Resolution cannot exceed the requested resolution.
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryMinMaxResolutionFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setMin(800);
+  advanced1.height.setMin(600);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.width.setMax(640);
+  advanced2.height.setMax(480);
+  advanced2.frameRate.setExact(60.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(800, result.Width());
+  EXPECT_EQ(600, result.Height());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultFrameRate, result.FrameRate());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryExactAspectRatio) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.aspectRatio.setExact(10.0);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.aspectRatio.setExact(3.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(std::round(MediaStreamVideoSource::kDefaultHeight * 10.0),
+            result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryAspectRatioRange) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.aspectRatio.setMin(10.0);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.aspectRatio.setMax(3.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(std::round(MediaStreamVideoSource::kDefaultHeight * 10.0),
+            result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  CheckNonResolutionDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryExactFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.frameRate.setExact(40.0);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.frameRate.setExact(45.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(40.0, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryFrameRateRange) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.frameRate.setMin(40.0);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.frameRate.setMax(35.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_LE(40.0, result.FrameRate());
+  CheckNonFrameRateDefaults(result);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryWidthFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.width.setMax(1920);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.width.setMin(2000);
+  advanced2.frameRate.setExact(10.0);
+  blink::WebMediaTrackConstraintSet& advanced3 =
+      constraint_factory_.AddAdvanced();
+  advanced3.frameRate.setExact(90.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(90.0, result.FrameRate());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryHeightFrameRate) {
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  advanced1.height.setMax(1080);
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.height.setMin(1500);
+  advanced2.frameRate.setExact(10.0);
+  blink::WebMediaTrackConstraintSet& advanced3 =
+      constraint_factory_.AddAdvanced();
+  advanced3.frameRate.setExact(60.0);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+  EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+  EXPECT_EQ(60.0, result.FrameRate());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, AdvancedDeviceID) {
+  const std::string kDeviceID1 = "fake_device_1";
+  const std::string kDeviceID2 = "fake_device_2";
+  const std::string kDeviceID3 = "fake_device_3";
+  const std::string kDeviceID4 = "fake_device_4";
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  blink::WebString id_vector1[] = {blink::WebString::fromASCII(kDeviceID1),
+                                   blink::WebString::fromASCII(kDeviceID2)};
+  advanced1.deviceId.setExact(
+      blink::WebVector<blink::WebString>(id_vector1, arraysize(id_vector1)));
+  blink::WebString id_vector2[] = {blink::WebString::fromASCII(kDeviceID2),
+                                   blink::WebString::fromASCII(kDeviceID3)};
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.deviceId.setExact(
+      blink::WebVector<blink::WebString>(id_vector2, arraysize(id_vector2)));
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kDeviceID2 must be selected because it is the only one that satisfies both
+  // advanced sets.
+  EXPECT_EQ(kDeviceID2, result.device_id());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest,
+       AdvancedContradictoryDeviceID) {
+  const std::string kDeviceID1 = "fake_device_1";
+  const std::string kDeviceID2 = "fake_device_2";
+  const std::string kDeviceID3 = "fake_device_3";
+  const std::string kDeviceID4 = "fake_device_4";
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced1 =
+      constraint_factory_.AddAdvanced();
+  blink::WebString id_vector1[] = {blink::WebString::fromASCII(kDeviceID1),
+                                   blink::WebString::fromASCII(kDeviceID2)};
+  advanced1.deviceId.setExact(
+      blink::WebVector<blink::WebString>(id_vector1, arraysize(id_vector1)));
+  blink::WebString id_vector2[] = {blink::WebString::fromASCII(kDeviceID3),
+                                   blink::WebString::fromASCII(kDeviceID4)};
+  blink::WebMediaTrackConstraintSet& advanced2 =
+      constraint_factory_.AddAdvanced();
+  advanced2.deviceId.setExact(
+      blink::WebVector<blink::WebString>(id_vector2, arraysize(id_vector2)));
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // The second advanced set must be ignored because it contradicts the first
+  // set.
+  EXPECT_EQ(std::string(kDeviceID1), result.device_id());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, AdvancedIdealDeviceID) {
+  const std::string kDeviceID1 = "fake_device_1";
+  const std::string kDeviceID2 = "fake_device_2";
+  const std::string kDeviceID3 = "fake_device_3";
+  constraint_factory_.Reset();
+  blink::WebMediaTrackConstraintSet& advanced =
+      constraint_factory_.AddAdvanced();
+  blink::WebString id_vector1[] = {blink::WebString::fromASCII(kDeviceID1),
+                                   blink::WebString::fromASCII(kDeviceID2)};
+  advanced.deviceId.setExact(
+      blink::WebVector<blink::WebString>(id_vector1, arraysize(id_vector1)));
+
+  blink::WebString id_vector2[] = {blink::WebString::fromASCII(kDeviceID2),
+                                   blink::WebString::fromASCII(kDeviceID3)};
+  constraint_factory_.basic().deviceId.setIdeal(
+      blink::WebVector<blink::WebString>(id_vector2, arraysize(id_vector2)));
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // Should select kDeviceID2, which appears in ideal and satisfies the advanced
+  // set.
+  EXPECT_EQ(std::string(kDeviceID2), result.device_id());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, ResolutionChangePolicy) {
+  {
+    constraint_factory_.Reset();
+    auto result = SelectSettings();
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
+    EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
+    // Resolution can be adjusted.
+    EXPECT_EQ(media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT,
+              result.ResolutionChangePolicy());
+  }
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setIdeal(630);
+    constraint_factory_.basic().height.setIdeal(470);
+    auto result = SelectSettings();
+    EXPECT_EQ(630, result.Width());
+    EXPECT_EQ(470, result.Height());
+    // Resolution can be adjusted because ideal was used to select the
+    // resolution.
+    EXPECT_EQ(media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT,
+              result.ResolutionChangePolicy());
+  }
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setExact(640);
+    constraint_factory_.basic().height.setExact(480);
+    auto result = SelectSettings();
+    EXPECT_EQ(640, result.Width());
+    EXPECT_EQ(480, result.Height());
+    EXPECT_EQ(media::RESOLUTION_POLICY_FIXED_RESOLUTION,
+              result.ResolutionChangePolicy());
+  }
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setExact(1000);
+    constraint_factory_.basic().height.setExact(500);
+    auto result = SelectSettings();
+    EXPECT_EQ(1000, result.Width());
+    EXPECT_EQ(500, result.Height());
+    EXPECT_EQ(media::RESOLUTION_POLICY_FIXED_RESOLUTION,
+              result.ResolutionChangePolicy());
+  }
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setExact(630);
+    constraint_factory_.basic().height.setExact(470);
+    auto result = SelectSettings();
+    EXPECT_EQ(630, result.Width());
+    EXPECT_EQ(470, result.Height());
+    EXPECT_EQ(media::RESOLUTION_POLICY_FIXED_RESOLUTION,
+              result.ResolutionChangePolicy());
+  }
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setIdeal(630);
+    constraint_factory_.basic().width.setMin(629);
+    constraint_factory_.basic().width.setMax(631);
+    constraint_factory_.basic().height.setIdeal(470);
+    constraint_factory_.basic().height.setMin(469);
+    auto result = SelectSettings();
+    EXPECT_EQ(630, result.Width());
+    EXPECT_EQ(470, result.Height());
+    // Min/Max ranges prevent the resolution from being adjusted.
+    EXPECT_EQ(media::RESOLUTION_POLICY_FIXED_RESOLUTION,
+              result.ResolutionChangePolicy());
+  }
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().aspectRatio.setExact(1.32);
+    constraint_factory_.basic().height.setIdeal(480);
+    auto result = SelectSettings();
+    EXPECT_EQ(std::round(480 * 1.32), result.Width());
+    EXPECT_EQ(480, result.Height());
+    // Exact aspect ratio prevents the resolution from being adjusted.
+    EXPECT_EQ(media::RESOLUTION_POLICY_FIXED_RESOLUTION,
+              result.ResolutionChangePolicy());
+  }
+}
+
+}  // namespace content
diff --git a/content/renderer/media/media_stream_constraints_util_video_device.cc b/content/renderer/media/media_stream_constraints_util_video_device.cc
index 344ccd80..bfcaefe 100644
--- a/content/renderer/media/media_stream_constraints_util_video_device.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_device.cc
@@ -174,15 +174,13 @@
 
 VideoDeviceCaptureSourceSelectionResult ResultFromSettings(
     const VideoDeviceCaptureSourceSettings& settings) {
-  VideoDeviceCaptureSourceSelectionResult result;
-  result.capture_params.power_line_frequency = settings.power_line_frequency();
-  result.capture_params.requested_format = settings.format();
-  result.device_id = settings.device_id();
-  result.facing_mode = settings.facing_mode();
-  result.noise_reduction = settings.noise_reduction();
-  result.failed_constraint_name = nullptr;
+  media::VideoCaptureParams capture_params;
+  capture_params.requested_format = settings.format();
+  capture_params.power_line_frequency = settings.power_line_frequency();
 
-  return result;
+  return VideoDeviceCaptureSourceSelectionResult(
+      settings.device_id(), settings.facing_mode(), capture_params,
+      settings.noise_reduction());
 }
 
 // Generic distance function between two numeric values. Based on the fitness
@@ -748,12 +746,26 @@
 VideoDeviceCaptureCapabilities& VideoDeviceCaptureCapabilities::operator=(
     VideoDeviceCaptureCapabilities&& other) = default;
 
-const char kDefaultFailedConstraintName[] = "";
-
 VideoDeviceCaptureSourceSelectionResult::
     VideoDeviceCaptureSourceSelectionResult()
-    : failed_constraint_name(kDefaultFailedConstraintName),
-      facing_mode(::mojom::FacingMode::NONE) {}
+    : VideoDeviceCaptureSourceSelectionResult("") {}
+
+VideoDeviceCaptureSourceSelectionResult::
+    VideoDeviceCaptureSourceSelectionResult(const char* failed_constraint_name)
+    : failed_constraint_name_(failed_constraint_name) {}
+
+VideoDeviceCaptureSourceSelectionResult::
+    VideoDeviceCaptureSourceSelectionResult(
+        const std::string& device_id,
+        ::mojom::FacingMode facing_mode,
+        media::VideoCaptureParams capture_params,
+        rtc::Optional<bool> noise_reduction)
+    : failed_constraint_name_(nullptr),
+      device_id_(device_id),
+      facing_mode_(facing_mode),
+      capture_params_(capture_params),
+      noise_reduction_(noise_reduction) {}
+
 VideoDeviceCaptureSourceSelectionResult::
     VideoDeviceCaptureSourceSelectionResult(
         const VideoDeviceCaptureSourceSelectionResult& other) = default;
@@ -793,7 +805,7 @@
                                kNumDefaultDistanceEntries);
   std::fill(best_distance.begin(), best_distance.end(), HUGE_VAL);
   VideoDeviceCaptureSourceSelectionResult result;
-  const char* failed_constraint_name = result.failed_constraint_name;
+  const char* failed_constraint_name = result.failed_constraint_name();
 
   for (auto& device : capabilities.device_capabilities) {
     double basic_device_distance =
@@ -884,7 +896,7 @@
   }
 
   if (!result.HasValue())
-    result.failed_constraint_name = failed_constraint_name;
+    return VideoDeviceCaptureSourceSelectionResult(failed_constraint_name);
 
   return result;
 }
diff --git a/content/renderer/media/media_stream_constraints_util_video_device.h b/content/renderer/media/media_stream_constraints_util_video_device.h
index 71a1925..02e47d7 100644
--- a/content/renderer/media/media_stream_constraints_util_video_device.h
+++ b/content/renderer/media/media_stream_constraints_util_video_device.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/logging.h"
 #include "content/common/content_export.h"
 #include "content/common/media/media_devices.mojom.h"
 #include "media/capture/video_capture_types.h"
@@ -38,40 +39,86 @@
   std::vector<rtc::Optional<bool>> noise_reduction_capabilities;
 };
 
-struct CONTENT_EXPORT VideoDeviceCaptureSourceSelectionResult {
+class CONTENT_EXPORT VideoDeviceCaptureSourceSelectionResult {
+ public:
+  // Creates a result without value and with an empty failed constraint name.
   VideoDeviceCaptureSourceSelectionResult();
+
+  // Creates a result without value and with the given |failed_constraint_name|.
+  // Does not take ownership of |failed_constraint_name|, so it must be null or
+  // point to a string that remains accessible.
+  explicit VideoDeviceCaptureSourceSelectionResult(
+      const char* failed_constraint_name);
+
+  // Creates a result with the given values.
+  VideoDeviceCaptureSourceSelectionResult(
+      const std::string& device_id,
+      ::mojom::FacingMode facing_mode_,
+      media::VideoCaptureParams capture_params_,
+      rtc::Optional<bool> noise_reduction_);
+
   VideoDeviceCaptureSourceSelectionResult(
       const VideoDeviceCaptureSourceSelectionResult& other);
+  VideoDeviceCaptureSourceSelectionResult& operator=(
+      const VideoDeviceCaptureSourceSelectionResult& other);
   VideoDeviceCaptureSourceSelectionResult(
       VideoDeviceCaptureSourceSelectionResult&& other);
+  VideoDeviceCaptureSourceSelectionResult& operator=(
+      VideoDeviceCaptureSourceSelectionResult&& other);
   ~VideoDeviceCaptureSourceSelectionResult();
-  VideoDeviceCaptureSourceSelectionResult& operator=(
-      const VideoDeviceCaptureSourceSelectionResult& other);
-  VideoDeviceCaptureSourceSelectionResult& operator=(
-      VideoDeviceCaptureSourceSelectionResult&& other);
 
-  bool HasValue() const { return failed_constraint_name == nullptr; }
+  bool HasValue() const { return failed_constraint_name_ == nullptr; }
 
   // Convenience accessors for fields embedded in |capture_params|.
   const media::VideoCaptureFormat& Format() const {
-    return capture_params.requested_format;
+    return capture_params_.requested_format;
   }
   int Width() const {
-    return capture_params.requested_format.frame_size.width();
+    DCHECK(HasValue());
+    return capture_params_.requested_format.frame_size.width();
   }
   int Height() const {
-    return capture_params.requested_format.frame_size.height();
+    DCHECK(HasValue());
+    return capture_params_.requested_format.frame_size.height();
   }
-  float FrameRate() const { return capture_params.requested_format.frame_rate; }
+  float FrameRate() const {
+    DCHECK(HasValue());
+    return capture_params_.requested_format.frame_rate;
+  }
   media::PowerLineFrequency PowerLineFrequency() const {
-    return capture_params.power_line_frequency;
+    DCHECK(HasValue());
+    return capture_params_.power_line_frequency;
   }
 
-  const char* failed_constraint_name;
-  std::string device_id;
-  ::mojom::FacingMode facing_mode;
-  media::VideoCaptureParams capture_params;
-  rtc::Optional<bool> noise_reduction;
+  // Other accessors.
+  const char* failed_constraint_name() const { return failed_constraint_name_; }
+
+  const std::string& device_id() const {
+    DCHECK(HasValue());
+    return device_id_;
+  }
+
+  ::mojom::FacingMode facing_mode() const {
+    DCHECK(HasValue());
+    return facing_mode_;
+  }
+
+  const media::VideoCaptureParams& capture_params() const {
+    DCHECK(HasValue());
+    return capture_params_;
+  }
+
+  const rtc::Optional<bool>& noise_reduction() const {
+    DCHECK(HasValue());
+    return noise_reduction_;
+  }
+
+ private:
+  const char* failed_constraint_name_;
+  std::string device_id_;
+  ::mojom::FacingMode facing_mode_;
+  media::VideoCaptureParams capture_params_;
+  rtc::Optional<bool> noise_reduction_;
 };
 
 // This function performs source and source-settings selection based on
diff --git a/content/renderer/media/media_stream_constraints_util_video_device_unittest.cc b/content/renderer/media/media_stream_constraints_util_video_device_unittest.cc
index 7e3a420..4222ea3 100644
--- a/content/renderer/media/media_stream_constraints_util_video_device_unittest.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_device_unittest.cc
@@ -140,13 +140,13 @@
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
   // Should select the default device with closest-to-default settings.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
-  EXPECT_EQ(default_device_->facing_mode, result.facing_mode);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
+  EXPECT_EQ(default_device_->facing_mode, result.facing_mode());
   EXPECT_EQ(*default_closest_format_, result.Format());
   // Should select default settings for other constraints.
   EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
             result.PowerLineFrequency());
-  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction);
+  EXPECT_EQ(rtc::Optional<bool>(), result.noise_reduction());
 }
 
 // The "Overconstrained" tests verify that failure of any single required
@@ -158,7 +158,7 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().deviceId.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnFacingMode) {
@@ -169,7 +169,7 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().facingMode.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnVideoKind) {
@@ -180,7 +180,7 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().videoKind.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnHeight) {
@@ -189,21 +189,21 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().height.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().height.setMin(123467890);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().height.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().height.setMax(0);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().height.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnWidth) {
@@ -212,21 +212,21 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().width.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().width.setMin(123467890);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().width.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().width.setMax(0);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().width.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -236,14 +236,14 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().aspectRatio.setMin(123467890.0);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   // This value is lower than the minimum supported by sources.
@@ -252,7 +252,7 @@
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnFrameRate) {
@@ -261,21 +261,21 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().frameRate.setMin(123467890.0);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().frameRate.setMax(0.0);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -285,21 +285,21 @@
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().googPowerLineFrequency.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().googPowerLineFrequency.setMin(123467890);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().googPowerLineFrequency.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 
   constraint_factory_.Reset();
   constraint_factory_.basic().googPowerLineFrequency.setMax(-1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().googPowerLineFrequency.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -327,7 +327,7 @@
       SelectVideoDeviceCaptureSourceSettings(capabilities, constraints);
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().googNoiseReduction.name(),
-            result.failed_constraint_name);
+            result.failed_constraint_name());
 }
 
 // The "Mandatory" and "Ideal" tests check that various selection criteria work
@@ -338,7 +338,7 @@
       blink::WebString::fromASCII(default_device_->device_id));
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(*default_closest_format_, result.Format());
   EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
             result.PowerLineFrequency());
@@ -346,7 +346,7 @@
   constraint_factory_.basic().deviceId.setExact(
       blink::WebString::fromASCII(low_res_device_->device_id));
   result = SelectSettings();
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(*low_res_closest_format_, result.Format());
   EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
             result.PowerLineFrequency());
@@ -354,7 +354,7 @@
   constraint_factory_.basic().deviceId.setExact(
       blink::WebString::fromASCII(high_res_device_->device_id));
   result = SelectSettings();
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_closest_format_, result.Format());
   EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
             result.PowerLineFrequency());
@@ -366,10 +366,10 @@
       blink::WebString::fromASCII("environment"));
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(::mojom::FacingMode::ENVIRONMENT, result.facing_mode);
+  EXPECT_EQ(::mojom::FacingMode::ENVIRONMENT, result.facing_mode());
   // Only the low-res device supports environment facing mode. Should select
   // default settings for everything else.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(*low_res_closest_format_, result.Format());
   EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
             result.PowerLineFrequency());
@@ -378,10 +378,10 @@
       blink::WebString::fromASCII("user"));
   result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(::mojom::FacingMode::USER, result.facing_mode);
+  EXPECT_EQ(::mojom::FacingMode::USER, result.facing_mode());
   // Only the high-res device supports user facing mode. Should select default
   // settings for everything else.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_closest_format_, result.Format());
   EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
             result.PowerLineFrequency());
@@ -393,14 +393,14 @@
       blink::WebString::fromASCII("depth"));
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(kDeviceID4, result.device_id);
+  EXPECT_EQ(kDeviceID4, result.device_id());
   EXPECT_EQ(media::PIXEL_FORMAT_Y16, result.Format().pixel_format);
 
   constraint_factory_.basic().videoKind.setExact(
       blink::WebString::fromASCII("color"));
   result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryPowerLineFrequency) {
@@ -416,8 +416,8 @@
     EXPECT_EQ(power_line_frequency, result.PowerLineFrequency());
     // The default device and settings closest to the default should be
     // selected.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
-    EXPECT_EQ(default_device_->facing_mode, result.facing_mode);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
+    EXPECT_EQ(default_device_->facing_mode, result.facing_mode());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 }
@@ -429,11 +429,11 @@
     constraint_factory_.basic().googNoiseReduction.setExact(noise_reduction);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(noise_reduction, result.noise_reduction);
+    EXPECT_EQ(noise_reduction, result.noise_reduction());
     // The default device and settings closest to the default should be
     // selected.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
-    EXPECT_EQ(default_device_->facing_mode, result.facing_mode);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
+    EXPECT_EQ(default_device_->facing_mode, result.facing_mode());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 }
@@ -447,7 +447,7 @@
   // All devices in |capabilities_| support the requested height. The algorithm
   // should prefer the first device that supports the requested height natively,
   // which is the low-res device.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(kHeight, result.Height());
 
   const int kLargeHeight = 1500;
@@ -456,7 +456,7 @@
   EXPECT_TRUE(result.HasValue());
   // Only the high-res device at the highest resolution supports the requested
   // height, even if not natively.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_highest_format_, result.Format());
 }
 
@@ -468,7 +468,7 @@
   EXPECT_TRUE(result.HasValue());
   // All devices in |capabilities_| support the requested height range. The
   // algorithm should prefer the default device.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_LE(kHeight, result.Height());
 
   const int kLargeHeight = 1500;
@@ -477,7 +477,7 @@
   EXPECT_TRUE(result.HasValue());
   // Only the high-res device at the highest resolution supports the requested
   // height range.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_highest_format_, result.Format());
 }
 
@@ -490,7 +490,7 @@
   // All devices in |capabilities_| support the requested height range. The
   // algorithm should prefer the settings that natively exceed the requested
   // maximum by the lowest amount. In this case it is the low-res device.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(low_res_device_->formats[0], result.Format());
 }
 
@@ -509,7 +509,7 @@
     // algorithm should prefer the default device since it has at least one
     // native format (the closest-to-default format) included in the requested
     // range.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -525,7 +525,7 @@
     // In this case, the algorithm should prefer the low-res device since it is
     // the first device with a native format (800x600) included in the requested
     // range.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(800, result.Width());
     EXPECT_EQ(600, result.Height());
   }
@@ -542,7 +542,7 @@
     // In this case, the algorithm should prefer the high-res device since it is
     // the only device with a native format (1280x720) included in the requested
     // range.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1280, result.Width());
     EXPECT_EQ(720, result.Height());
   }
@@ -557,7 +557,7 @@
     EXPECT_TRUE(result.HasValue());
     // The algorithm should select the first device that supports the ideal
     // height natively.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(kIdealHeight, result.Height());
   }
 
@@ -570,7 +570,7 @@
     // ideal at a lower cost than the other devices (500 vs 600 or 720).
     // Note that a native resolution of 480 is further from the ideal than
     // 500 cropped to 480.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -582,7 +582,7 @@
     // In this case, the high-res device has two configurations that satisfy
     // the ideal value (1920x1080 and 2304x1536). Select the one with shortest
     // native distance to the ideal value (1920x1080).
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1920, result.Width());
     EXPECT_EQ(1080, result.Height());
   }
@@ -594,7 +594,7 @@
     EXPECT_TRUE(result.HasValue());
     // The algorithm must the select the only device that can satisfy the ideal,
     // which is the high-res device at the highest resolution.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(*high_res_highest_format_, result.Format());
   }
 }
@@ -608,7 +608,7 @@
   // All devices in |capabilities_| support the requested width. The algorithm
   // should prefer the first device that supports the requested width natively,
   // which is the low-res device.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(kWidth, result.Width());
 
   const int kLargeWidth = 2000;
@@ -618,7 +618,7 @@
   EXPECT_LE(kLargeWidth, result.Width());
   // Only the high-res device at the highest resolution supports the requested
   // width, even if not natively.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_highest_format_, result.Format());
 }
 
@@ -631,7 +631,7 @@
   // All devices in |capabilities_| support the requested width range. The
   // algorithm should prefer the default device at 1000x1000, which is the
   // first configuration that satisfies the minimum width.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_LE(kWidth, result.Width());
   EXPECT_EQ(1000, result.Width());
   EXPECT_EQ(1000, result.Height());
@@ -642,7 +642,7 @@
   EXPECT_TRUE(result.HasValue());
   // Only the high-res device at the highest resolution supports the requested
   // minimum width.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_LE(kLargeWidth, result.Width());
   EXPECT_EQ(*high_res_highest_format_, result.Format());
 }
@@ -657,7 +657,7 @@
   // algorithm should prefer the settings that natively exceed the requested
   // maximum by the lowest amount. In this case it is the low-res device at its
   // lowest resolution.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(low_res_device_->formats[0], result.Format());
 }
 
@@ -675,7 +675,7 @@
     // All devices in |capabilities_| support the constraint range. The
     // algorithm should prefer the default device since it has at least one
     // native format (1000x1000) included in the requested range.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(1000, result.Width());
     EXPECT_EQ(1000, result.Height());
   }
@@ -692,7 +692,7 @@
     // In this case, the algorithm should prefer the low-res device since it is
     // the first device with a native format (800x600) included in the requested
     // range.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(800, result.Width());
     EXPECT_EQ(600, result.Height());
   }
@@ -709,7 +709,7 @@
     // In this case, the algorithm should prefer the high-res device since it is
     // the only device with a native format (1920x1080) included in the
     // requested range.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1920, result.Width());
     EXPECT_EQ(1080, result.Height());
   }
@@ -724,7 +724,7 @@
     EXPECT_TRUE(result.HasValue());
     // The algorithm should select the first device that supports the ideal
     // width natively, which is the low-res device at 320x240.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(kIdealWidth, result.Width());
   }
 
@@ -737,7 +737,7 @@
     // ideal at a lower cost than the other devices (500 vs 640).
     // Note that a native resolution of 320 is further from the ideal value of
     // 321 than 500 cropped to 321.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -747,7 +747,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // The algorithm must the select the only device that can satisfy the ideal.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(*high_res_highest_format_, result.Format());
   }
 
@@ -758,7 +758,7 @@
     EXPECT_TRUE(result.HasValue());
     // The algorithm must the select the device and setting with less distance
     // to the ideal.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(*high_res_highest_format_, result.Format());
   }
 }
@@ -772,7 +772,7 @@
   // All devices in |capabilities_| support the requested frame rate. The
   // algorithm should prefer the first device that supports the requested frame
   // rate natively, which is the low-res device at 640x480x30Hz.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(kFrameRate, result.FrameRate());
   EXPECT_EQ(640, result.Width());
   EXPECT_EQ(480, result.Height());
@@ -784,7 +784,7 @@
   // Only the high-res device supports the requested frame rate, even if not
   // natively. The least expensive configuration that supports the requested
   // frame rate is 1280x720x60Hz.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(60.0, result.FrameRate());
   EXPECT_EQ(1280, result.Width());
   EXPECT_EQ(720, result.Height());
@@ -798,7 +798,7 @@
   EXPECT_TRUE(result.HasValue());
   // All devices in |capabilities_| support the requested frame-rate range. The
   // algorithm should prefer the default device.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   // The format closest to the default satisfies the constraint.
   EXPECT_EQ(*default_closest_format_, result.Format());
 
@@ -808,7 +808,7 @@
   EXPECT_TRUE(result.HasValue());
   // Only the high-res device supports the requested frame-rate range.
   // The least expensive configuration is 1280x720x60Hz.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_LE(kLargeFrameRate, result.FrameRate());
   EXPECT_EQ(1280, result.Width());
   EXPECT_EQ(720, result.Height());
@@ -824,7 +824,7 @@
   // algorithm should prefer the settings that natively exceed the requested
   // maximum by the lowest amount. In this case it is the high-res device with
   // default resolution .
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(kLowFrameRate, result.FrameRate());
   EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
   EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
@@ -844,7 +844,7 @@
     // All devices in |capabilities_| support the constraint range. The
     // algorithm should prefer the default device since its closest-to-default
     // format has a frame rate included in the requested range.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -860,7 +860,7 @@
     // In this case, the algorithm should prefer the low-res device since it is
     // the first device with a native frame rate included in the requested
     // range. The default resolution should be preferred as secondary criterion.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(*low_res_closest_format_, result.Format());
   }
 
@@ -877,7 +877,7 @@
     // the only device with a native format included in the requested range.
     // The 1280x720 resolution should be selected due to closeness to default
     // settings, which is the second tie-breaker criterion that applies.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1280, result.Width());
     EXPECT_EQ(720, result.Height());
   }
@@ -893,7 +893,7 @@
     // The algorithm should select the first configuration that supports the
     // ideal frame rate natively, which is the low-res device. Default
     // resolution should be selected as secondary criterion.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(*low_res_closest_format_, result.Format());
   }
 
@@ -906,7 +906,7 @@
     // ideal at a lower cost than the other devices (40 vs 60).
     // Note that a native frame rate of 30 is further from the ideal than
     // 31 adjusted to 30.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -918,7 +918,7 @@
     // The high-res device format 1280x720x60.0 must be selected because its
     // frame rate can satisfy the ideal frame rate and has resolution closest
     // to the default.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1280, result.Width());
     EXPECT_EQ(720, result.Height());
     EXPECT_EQ(60, result.FrameRate());
@@ -933,7 +933,7 @@
     // The high-res device format 1280x720x60.0 must be selected because its
     // frame rate it closest to the ideal value and it has resolution closest to
     // the default.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1280, result.Width());
     EXPECT_EQ(720, result.Height());
     EXPECT_EQ(60, result.FrameRate());
@@ -958,7 +958,7 @@
   // All devices in |capabilities_| support the requested aspect ratio.
   // The algorithm should prefer the first device that supports the requested
   // aspect ratio.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(*default_closest_format_, result.Format());
 
   const int kMinWidth = 500;
@@ -981,7 +981,7 @@
   EXPECT_LE(kAspectRatio, max_aspect_ratio);
   // The default device can support the requested aspect ratio with the default
   // settings (500x500) using cropping.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(*default_closest_format_, result.Format());
 
   const int kMinHeight = 480;
@@ -1007,7 +1007,7 @@
   // resolution of 640x480. Higher resolutions for the default device are more
   // penalized by the constraints than the default native resolution of the
   // low-res device.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(*low_res_closest_format_, result.Format());
 }
 
@@ -1026,7 +1026,7 @@
   // All devices in |capabilities_| support the requested aspect-ratio range.
   // The algorithm should prefer the first device that supports the requested
   // aspect-ratio range, which in this case is the default device.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(*default_closest_format_, result.Format());
 
   const int kMinWidth = 500;
@@ -1052,7 +1052,7 @@
   // resolution of 640x480.
   // Higher resolutions for the default device are more penalized by the
   // constraints than the default native resolution of the low-res device.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(*low_res_closest_format_, result.Format());
 }
 
@@ -1071,7 +1071,7 @@
   // All devices in |capabilities_| support the requested aspect-ratio range.
   // The algorithm should prefer the first device that supports the requested
   // aspect-ratio range, which in this case is the default device.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(*default_closest_format_, result.Format());
 
   const int kExactWidth = 360;
@@ -1095,7 +1095,7 @@
   // The high-res device with a native resolution of 1280x720 can support
   // 360x720 with cropping with less penalty than the default device at
   // 1000x1000.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(1280, result.Width());
   EXPECT_EQ(720, result.Height());
 }
@@ -1123,7 +1123,7 @@
     // All devices in |capabilities_| support the requested aspect-ratio range.
     // The algorithm should prefer the first device that supports the requested
     // aspect-ratio range, which in this case is the default device.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -1150,7 +1150,7 @@
     EXPECT_GE(kMaxAspectRatio, min_aspect_ratio);
     // The only device that supports the resolution and aspect ratio constraint
     // is the high-res device. The 1920x1080 is the least expensive format.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1920, result.Width());
     EXPECT_EQ(1080, result.Height());
   }
@@ -1174,7 +1174,7 @@
     // settings.
     EXPECT_LE(kIdealAspectRatio, max_aspect_ratio);
     EXPECT_GE(kIdealAspectRatio, min_aspect_ratio);
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_EQ(*default_closest_format_, result.Format());
   }
 
@@ -1186,7 +1186,7 @@
     // The only device that supports the ideal aspect ratio is the high-res
     // device. The least expensive way to support it with the 1920x1080 format
     // cropped to 1500x1.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1920, result.Width());
     EXPECT_EQ(1080, result.Height());
   }
@@ -1198,7 +1198,7 @@
     EXPECT_TRUE(result.HasValue());
     // The only device that supports the ideal aspect ratio is the high-res
     // device with its highest resolution, cropped to 2000x1.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(*high_res_highest_format_, result.Format());
   }
 
@@ -1209,7 +1209,7 @@
     EXPECT_TRUE(result.HasValue());
     // The configuration closest to the ideal aspect ratio is is the high-res
     // device with its highest resolution, cropped to 2304x1.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(*high_res_highest_format_, result.Format());
   }
 
@@ -1222,7 +1222,7 @@
     // The first device to support the ideal aspect ratio and the resolution
     // constraint is the low-res device. The 800x600 format cropped to 800x400
     // is the lest expensive way to achieve it.
-    EXPECT_EQ(low_res_device_->device_id, result.device_id);
+    EXPECT_EQ(low_res_device_->device_id, result.device_id());
     EXPECT_EQ(800, result.Width());
     EXPECT_EQ(600, result.Height());
   }
@@ -1236,7 +1236,7 @@
     // The only device that supports the ideal aspect ratio and the resolution
     // constraint is the high-res device. The 1280x720 cropped to 1200x400 is
     // the lest expensive way to achieve it.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_EQ(1280, result.Width());
     EXPECT_EQ(720, result.Height());
   }
@@ -1255,7 +1255,7 @@
   // set is therefore ignored in all calls to SelectSettings().
   // Tie-breaker rule that applies is closeness to default settings.
   auto result = SelectSettings();
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(*default_closest_format_, result.Format());
 
   blink::WebMediaTrackConstraintSet& advanced2 =
@@ -1267,7 +1267,7 @@
   result = SelectSettings();
   // The device that best supports this advanced set is the low-res device,
   // which natively supports the maximum resolution.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(640, result.Width());
   EXPECT_EQ(480, result.Height());
 
@@ -1278,7 +1278,7 @@
   EXPECT_TRUE(result.HasValue());
   // The high-res device natively supports the third advanced set in addition
   // to the previous set and should be selected.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(640, result.Width());
   EXPECT_EQ(480, result.Height());
 
@@ -1291,7 +1291,7 @@
   // advanced natively, having better support for the previous sets has
   // precedence, so the high-res device is selected.
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(640, result.Width());
   EXPECT_EQ(480, result.Height());
 
@@ -1305,7 +1305,7 @@
   // precedence over the native fitness distance.
   // Both support standard fitness distance equally, since 600x400 can be
   // cropped to 320x240.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(600, result.Width());
   EXPECT_EQ(400, result.Height());
 
@@ -1315,7 +1315,7 @@
   EXPECT_TRUE(result.HasValue());
   // The high-res device at 640x480@10Hz is closer to the large ideal
   // resolution.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(640, result.Width());
   EXPECT_EQ(480, result.Height());
 }
@@ -1340,7 +1340,7 @@
   // set. 2304x1536x10.0 satisfies sets 1 and 3, while 1920x1080x60.0
   // satisfies sets 1, and 2. The latter must be selected, regardless of
   // any other criteria.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(1920, result.Width());
   EXPECT_EQ(1080, result.Height());
   EXPECT_EQ(60.0, result.FrameRate());
@@ -1359,10 +1359,10 @@
   advanced2.googNoiseReduction.setExact(false);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_LE(1920, result.Width());
   EXPECT_LE(1080, result.Height());
-  EXPECT_TRUE(result.noise_reduction && !*result.noise_reduction);
+  EXPECT_TRUE(result.noise_reduction() && !*result.noise_reduction());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -1384,10 +1384,10 @@
     // The second advanced set cannot be satisfied because it contradicts the
     // first set. The default device supports the first set and should be
     // selected.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_LE(640, result.Width());
     EXPECT_LE(480, result.Height());
-    EXPECT_TRUE(result.noise_reduction && *result.noise_reduction);
+    EXPECT_TRUE(result.noise_reduction() && *result.noise_reduction());
   }
 
   // Same test without noise reduction
@@ -1404,11 +1404,11 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     // Only the high-res device can satisfy the second advanced set.
-    EXPECT_EQ(high_res_device_->device_id, result.device_id);
+    EXPECT_EQ(high_res_device_->device_id, result.device_id());
     EXPECT_LE(1920, result.Width());
     EXPECT_LE(1080, result.Height());
     // Should select default noise reduction setting.
-    EXPECT_TRUE(!result.noise_reduction);
+    EXPECT_TRUE(!result.noise_reduction());
   }
 }
 
@@ -1428,7 +1428,7 @@
   // The second advanced set must be ignored because it contradicts the first
   // set. The low-res device is the one that best supports the requested
   // resolution.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(640, result.Width());
   EXPECT_EQ(480, result.Height());
 }
@@ -1451,7 +1451,7 @@
   // set. The default device with the 200x200@40Hz format should be selected.
   // That format satisfies the first advanced set as well as any other, so the
   // tie breaker rule that applies is default device ID.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(200, result.Width());
   EXPECT_EQ(200, result.Height());
   EXPECT_EQ(40, result.FrameRate());
@@ -1475,7 +1475,7 @@
   // set. The default device with the 1000x1000@20Hz format should be selected.
   // That format satisfies the first advanced set as well as any other, so the
   // tie breaker rule that applies is default device ID.
-  EXPECT_EQ(default_device_->device_id, result.device_id);
+  EXPECT_EQ(default_device_->device_id, result.device_id());
   EXPECT_EQ(1000, result.Width());
   EXPECT_EQ(1000, result.Height());
   EXPECT_EQ(20, result.FrameRate());
@@ -1495,7 +1495,7 @@
   // The second advanced set must be ignored because it contradicts the first
   // set. Only the high-res device in the highest-resolution format supports the
   // requested aspect ratio.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_highest_format_, result.Format());
 }
 
@@ -1513,7 +1513,7 @@
   // The second advanced set must be ignored because it contradicts the first
   // set. Only the high-res device in the highest-resolution format supports the
   // requested aspect ratio.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(*high_res_highest_format_, result.Format());
 }
 
@@ -1567,7 +1567,7 @@
   // The low-res device at 320x240@30Hz satisfies advanced sets 1 and 3.
   // The high-res device at 2304x1536@10.0f can satisfy sets 1 and 2, but not
   // both at the same time. Thus, low-res device must be preferred.
-  EXPECT_EQ(low_res_device_->device_id, result.device_id);
+  EXPECT_EQ(low_res_device_->device_id, result.device_id());
   EXPECT_EQ(30.0, result.FrameRate());
   EXPECT_GE(1920, result.Width());
 }
@@ -1591,7 +1591,7 @@
   // sets 1 and 3. The same device at 2304x1536@10.0f can satisfy sets 1 and 2,
   // but not both at the same time. Thus, the format closest to default that
   // satisfies sets 1 and 3 must be chosen.
-  EXPECT_EQ(high_res_device_->device_id, result.device_id);
+  EXPECT_EQ(high_res_device_->device_id, result.device_id());
   EXPECT_EQ(60.0, result.FrameRate());
   EXPECT_GE(1080, result.Height());
 }
@@ -1614,7 +1614,7 @@
   EXPECT_TRUE(result.HasValue());
   // kDeviceID2 must be selected because it is the only one that satisfies both
   // advanced sets.
-  EXPECT_EQ(std::string(kDeviceID2), result.device_id);
+  EXPECT_EQ(std::string(kDeviceID2), result.device_id());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -1636,7 +1636,7 @@
   EXPECT_TRUE(result.HasValue());
   // The second advanced set must be ignored because it contradicts the first
   // set.
-  EXPECT_EQ(std::string(kDeviceID1), result.device_id);
+  EXPECT_EQ(std::string(kDeviceID1), result.device_id());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
@@ -1658,7 +1658,7 @@
     // The second advanced set cannot be satisfied because it contradicts the
     // first set. The default device supports the first set and should be
     // selected.
-    EXPECT_EQ(default_device_->device_id, result.device_id);
+    EXPECT_EQ(default_device_->device_id, result.device_id());
     EXPECT_LE(640, result.Width());
     EXPECT_LE(480, result.Height());
     EXPECT_EQ(50, static_cast<int>(result.PowerLineFrequency()));
@@ -1673,7 +1673,7 @@
   auto result = SelectVideoDeviceCaptureSourceSettings(
       capabilities, constraint_factory_.CreateWebMediaConstraints());
   EXPECT_FALSE(result.HasValue());
-  EXPECT_TRUE(std::string(result.failed_constraint_name).empty());
+  EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, NoDevicesWithConstraints) {
@@ -1683,7 +1683,7 @@
   auto result = SelectVideoDeviceCaptureSourceSettings(
       capabilities, constraint_factory_.CreateWebMediaConstraints());
   EXPECT_FALSE(result.HasValue());
-  EXPECT_TRUE(std::string(result.failed_constraint_name).empty());
+  EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
 }
 
 }  // namespace content
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index 0b8fb8c..15fc8ce 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -394,7 +394,7 @@
     const VideoDeviceCaptureSourceSelectionResult& selection_result) {
   DCHECK(CalledOnValidThread());
   if (selection_result.HasValue()) {
-    controls->video.device_id = selection_result.device_id;
+    controls->video.device_id = selection_result.device_id();
   } else {
     // TODO(guidou): Abort the request in all cases where |selection_result|
     // has no value, as the spec mandates.
@@ -403,7 +403,7 @@
     // devices. Fix once the standard behavior ceases to be disruptive.
     // See http://crbug.com/690491.
     blink::WebString failed_constraint_name =
-        blink::WebString::fromASCII(selection_result.failed_constraint_name);
+        blink::WebString::fromASCII(selection_result.failed_constraint_name());
     blink::WebString device_id_constraint_name = blink::WebString::fromASCII(
         user_media_request.videoConstraints().basic().deviceId.name());
     if (failed_constraint_name.equals(device_id_constraint_name)) {
diff --git a/content/renderer/media/user_media_client_impl.h b/content/renderer/media/user_media_client_impl.h
index fd0cde4..5e7b341 100644
--- a/content/renderer/media/user_media_client_impl.h
+++ b/content/renderer/media/user_media_client_impl.h
@@ -39,7 +39,7 @@
 class MediaStreamAudioSource;
 class MediaStreamDispatcher;
 class MediaStreamVideoSource;
-struct VideoDeviceCaptureSourceSelectionResult;
+class VideoDeviceCaptureSourceSelectionResult;
 
 // UserMediaClientImpl is a delegate for the Media Stream GetUserMedia API.
 // It ties together WebKit and MediaStreamManager
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1b58cf93..6fc1bc8 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1587,6 +1587,7 @@
       "../renderer/media/media_stream_audio_unittest.cc",
       "../renderer/media/media_stream_constraints_util_sets_unittest.cc",
       "../renderer/media/media_stream_constraints_util_unittest.cc",
+      "../renderer/media/media_stream_constraints_util_video_content_unittest.cc",
       "../renderer/media/media_stream_constraints_util_video_device_unittest.cc",
       "../renderer/media/media_stream_dispatcher_unittest.cc",
       "../renderer/media/media_stream_video_capturer_source_unittest.cc",
diff --git a/content/test/data/gpu/functional_files/context.js b/content/test/data/gpu/functional_files/context.js
index 90ba336..d89e722 100644
--- a/content/test/data/gpu/functional_files/context.js
+++ b/content/test/data/gpu/functional_files/context.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Global variable.
+// Global variables.
 var gl_context;
+var gl_renderer;
 
 initializeWebGL = function(canvas) {
   gl_context = null;
@@ -13,7 +14,7 @@
   // If we don't have a GL context, give up now.
   if (!gl_context) {
     err = "Unable to initialize WebGL. Your browser may not support it.";
-    if (domAutomationController) {
+    if (window.domAutomationController) {
       console.log(err);
     } else {
       alert(err);
@@ -34,8 +35,14 @@
     gl_context.clearDepth(1);
     gl_context.clear(gl_context.COLOR_BUFFER_BIT |
                      gl_context.DEPTH_BUFFER_BIT);
+
+    // Also fetch the unmasked GL_RENDERER string.
+    var ext = gl_context.getExtension("WEBGL_debug_renderer_info");
+    gl_renderer = gl_context.getParameter(ext.UNMASKED_RENDERER_WEBGL);
   }
 
-  domAutomationController.setAutomationId(0);
-  domAutomationController.send("FINISHED");
+  if (window.domAutomationController) {
+    domAutomationController.setAutomationId(0);
+    domAutomationController.send("FINISHED");
+  }
 }
diff --git a/content/test/data/loader/reload_test.html b/content/test/data/loader/reload_test.html
new file mode 100644
index 0000000..feaf92c
--- /dev/null
+++ b/content/test/data/loader/reload_test.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<img src="empty16x16.png">
+<iframe src="simple_frame.html"></iframe>
+</body>
+</html>
diff --git a/content/test/data/loader/reload.html b/content/test/data/loader/simple_frame.html
similarity index 100%
rename from content/test/data/loader/reload.html
rename to content/test/data/loader/simple_frame.html
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index 4222577..e4459eda 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -5,6 +5,7 @@
 # Fuzzers for content/ components.
 
 import("//testing/libfuzzer/fuzzer_test.gni")
+import("//third_party/protobuf/proto_library.gni")
 
 # Empty group for package discovery.
 group("fuzzer") {
@@ -67,4 +68,22 @@
     ]
     seed_corpus = "//content/test/data/fuzzer_corpus/clear_site_data/"
   }
+
+  fuzzer_test("renderer_proto_tree_fuzzer") {
+    sources = [
+      "renderer_proto_tree_fuzzer.cc",
+    ]
+    deps = [
+      ":fuzzer_support",
+      ":html_tree_proto",
+      "//third_party/libprotobuf-mutator",
+    ]
+  }
+
+  proto_library("html_tree_proto") {
+    sources = [
+      "html_tree.proto",
+    ]
+    testonly = true
+  }
 }
diff --git a/content/test/fuzzer/DEPS b/content/test/fuzzer/DEPS
new file mode 100644
index 0000000..bfe6240
--- /dev/null
+++ b/content/test/fuzzer/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+third_party/libprotobuf-mutator/src/src",
+]
+
diff --git a/content/test/fuzzer/html_tree.proto b/content/test/fuzzer/html_tree.proto
new file mode 100644
index 0000000..7e8e40a
--- /dev/null
+++ b/content/test/fuzzer/html_tree.proto
@@ -0,0 +1,252 @@
+syntax = "proto3";
+
+message Document {
+  Tag root = 1;
+}
+
+message Tag {
+  Name name = 1;
+  repeated Attribute attrs = 2;
+  repeated Tag subtags = 3;
+
+  enum Name {
+    A = 0;
+    ABBR = 1;
+    ADDRESS = 2;
+    AREA = 3;
+    ARTICLE = 4;
+    ASIDE = 5;
+    AUDIO = 6;
+    B = 7;
+    BASE = 8;
+    BDI = 9;
+    BDO = 10;
+    BLOCKQUOTE = 11;
+    BODY = 12;
+    BR = 13;
+    BUTTON = 14;
+    CANVAS = 15;
+    CAPTION = 16;
+    CITE = 17;
+    CODE = 18;
+    COL = 19;
+    COLGROUP = 20;
+    DATA = 21;
+    DATALIST = 22;
+    DD = 23;
+    DEL = 24;
+    DFN = 25;
+    DIV = 26;
+    DL = 27;
+    DT = 28;
+    EM = 29;
+    EMBED = 30;
+    FIELDSET = 31;
+    FIGCAPTION = 32;
+    FIGURE = 33;
+    FOOTER = 34;
+    FORM = 35;
+    H1 = 36;
+    H2 = 37;
+    H3 = 38;
+    H4 = 39;
+    H5 = 40;
+    H6 = 41;
+    HEAD = 42;
+    HEADER = 43;
+    HR = 44;
+    HTML = 45;
+    I = 46;
+    IFRAME = 47;
+    IMG = 48;
+    INPUT = 49;
+    INS = 50;
+    KBD = 51;
+    KEYGEN = 52;
+    LABEL = 53;
+    LEGEND = 54;
+    LI = 55;
+    LINK = 56;
+    MAIN = 57;
+    MAP = 58;
+    MARK = 59;
+    META = 60;
+    METER = 61;
+    NAV = 62;
+    NOSCRIPT = 63;
+    OBJECT = 64;
+    OL = 65;
+    OPTGROUP = 66;
+    OPTION = 67;
+    OUTPUT = 68;
+    P = 69;
+    PARAM = 70;
+    PRE = 71;
+    PROGRESS = 72;
+    Q = 73;
+    RB = 74;
+    RP = 75;
+    RT = 76;
+    RTC = 77;
+    RUBY = 78;
+    S = 79;
+    SAMP = 80;
+    SCRIPT = 81;
+    SECTION = 82;
+    SELECT = 83;
+    SMALL = 84;
+    SOURCE = 85;
+    SPAN = 86;
+    STRONG = 87;
+    STYLE = 88;
+    SUB = 89;
+    SUP = 90;
+    TABLE = 91;
+    TBODY = 92;
+    TD = 93;
+    TEMPLATE = 94;
+    TEXTAREA = 95;
+    TFOOT = 96;
+    TH = 97;
+    THEAD = 98;
+    TIME = 99;
+    TITLE = 100;
+    TR = 101;
+    TRACK = 102;
+    U = 103;
+    UL = 104;
+    VAR = 105;
+    VIDEO = 106;
+    WBR = 107;
+  }
+}
+
+message Attribute {
+  Name name = 1;
+  Value value = 2;
+
+  message Value {
+    oneof value {
+      bool bool_value = 1;
+      uint64 uint_value = 2;
+      int64 int_value = 3;
+      double double_value = 4;
+
+      int64 px_value = 5;
+      uint32 pct_value = 6;
+    }
+  }
+
+  enum Name {
+    ACCEPT = 0;
+    ACCEPT_CHARSET = 1;
+    ACCESSKEY = 2;
+    ACTION = 3;
+    ALIGN = 4;
+    ALT = 5;
+    ASYNC = 6;
+    AUTOCOMPLETE = 7;
+    AUTOFOCUS = 8;
+    AUTOPLAY = 9;
+    AUTOSAVE = 10;
+    BGCOLOR = 11;
+    BORDER = 12;
+    BUFFERED = 13;
+    CHALLENGE = 14;
+    CHARSET = 15;
+    CHECKED = 16;
+    CITE = 17;
+    CLASS = 18;
+    CODE = 19;
+    CODEBASE = 20;
+    COLOR = 21;
+    COLS = 22;
+    COLSPAN = 23;
+    CONTENT = 24;
+    CONTENTEDITABLE = 25;
+    CONTEXTMENU = 26;
+    CONTROLS = 27;
+    DATA = 28;
+    DATETIME = 29;
+    DEFAULT = 30;
+    DEFER = 31;
+    DIR = 32;
+    DIRNAME = 33;
+    DISABLED = 34;
+    DOWNLOAD = 35;
+    DRAGGABLE = 36;
+    DROPZONE = 37;
+    ENCTYPE = 38;
+    FOR = 39;
+    FORM = 40;
+    FORMACTION = 41;
+    HEADERS = 42;
+    HEIGHT = 43;
+    HIDDEN = 44;
+    HIGH = 45;
+    HREF = 46;
+    HREFLANG = 47;
+    ICON = 48;
+    ID = 49;
+    ISMAP = 50;
+    ITEMPROP = 51;
+    KEYTYPE = 52;
+    KIND = 53;
+    LABEL = 54;
+    LANG = 55;
+    LANGUAGE = 56;
+    LIST = 57;
+    LOOP = 58;
+    LOW = 59;
+    MANIFEST = 60;
+    MAX = 61;
+    MAXLENGTH = 62;
+    MEDIA = 63;
+    METHOD = 64;
+    MIN = 65;
+    MULTIPLE = 66;
+    MUTED = 67;
+    NAME = 68;
+    NOVALIDATE = 69;
+    OPEN = 70;
+    OPTIMUM = 71;
+    PATTERN = 72;
+    PING = 73;
+    PLACEHOLDER = 74;
+    POSTER = 75;
+    PRELOAD = 76;
+    RADIOGROUP = 77;
+    READONLY = 78;
+    REL = 79;
+    REQUIRED = 80;
+    REVERSED = 81;
+    ROWS = 82;
+    ROWSPAN = 83;
+    SANDBOX = 84;
+    SCOPE = 85;
+    SCOPED = 86;
+    SEAMLESS = 87;
+    SELECTED = 88;
+    SHAPE = 89;
+    SIZE = 90;
+    SIZES = 91;
+    SPAN = 92;
+    SPELLCHECK = 93;
+    SRC = 94;
+    SRCDOC = 95;
+    SRCLANG = 96;
+    SRCSET = 97;
+    START = 98;
+    STEP = 99;
+    STYLE = 100;
+    SUMMARY = 101;
+    TABINDEX = 102;
+    TARGET = 103;
+    TITLE = 104;
+    TYPE = 105;
+    USEMAP = 106;
+    VALUE = 107;
+    WIDTH = 108;
+    WRAP = 109;
+  }
+}
diff --git a/content/test/fuzzer/renderer_proto_tree_fuzzer.cc b/content/test/fuzzer/renderer_proto_tree_fuzzer.cc
new file mode 100644
index 0000000..1a71c04c
--- /dev/null
+++ b/content/test/fuzzer/renderer_proto_tree_fuzzer.cc
@@ -0,0 +1,144 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Fuzzer for content/renderer
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <sstream>
+
+#include "content/test/fuzzer/fuzzer_support.h"
+#include "content/test/fuzzer/html_tree.pb.h"
+#include "third_party/libprotobuf-mutator/src/src/binary_format.h"
+#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_mutator.h"
+
+protobuf_mutator::protobuf::LogSilencer log_silincer;
+
+namespace content {
+
+class HtmlTreeWriter {
+ public:
+  HtmlTreeWriter() {}
+
+  template <typename T>
+  HtmlTreeWriter& operator<<(const T& t) {
+    out_ << t;
+    return *this;
+  }
+
+  std::string str() const { return out_.str(); }
+
+ private:
+  std::ostringstream out_;
+};
+
+static HtmlTreeWriter& operator<<(HtmlTreeWriter& w,
+                                  const Attribute::Value& value) {
+  switch (value.value_case()) {
+    case Attribute::Value::kBoolValue:
+      return w << (value.bool_value() ? "true" : "false");
+    case Attribute::Value::kUintValue:
+      return w << value.uint_value();
+    case Attribute::Value::kIntValue:
+      return w << value.int_value();
+    case Attribute::Value::kDoubleValue:
+      return w << value.double_value();
+    case Attribute::Value::kPxValue:
+      return w << value.px_value() << "px";
+    case Attribute::Value::kPctValue:
+      return w << value.pct_value() << "%";
+    case Attribute::Value::VALUE_NOT_SET:
+      return w;
+  }
+}
+
+static HtmlTreeWriter& operator<<(HtmlTreeWriter& w,
+                                  const Attribute::Name& name) {
+  return w << Attribute_Name_Name(name);
+}
+
+static HtmlTreeWriter& operator<<(HtmlTreeWriter& w, const Attribute& attr) {
+  return w << attr.name() << "=\"" << attr.value() << "\"";
+}
+
+static HtmlTreeWriter& operator<<(HtmlTreeWriter& w, const Tag::Name& tagName) {
+  return w << Tag_Name_Name(tagName);
+}
+
+static void operator<<(HtmlTreeWriter& w, const Tag& tag) {
+  w << "<" << tag.name();
+  for (const auto& attr : tag.attrs()) {
+    w << " " << attr;
+  }
+
+  w << ">";
+  for (const auto& subtag : tag.subtags()) {
+    w << subtag;
+  }
+  w << "</" << tag.name() << ">";
+}
+
+static void operator<<(HtmlTreeWriter& w, const Document& document) {
+  w << document.root();
+}
+
+static std::string str(const uint8_t* data, size_t size) {
+  Document document;
+  protobuf_mutator::ParseBinaryMessage(data, size, &document);
+
+  HtmlTreeWriter writer;
+  writer << document;
+  return writer.str();
+  //  return document.ShortDebugString();
+}
+
+extern "C" void LLVMPrintInput(const uint8_t* data, size_t size) {
+  //  fprintf(stderr, "NEW %s\n", str(data, size).c_str());
+}
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data,
+                                          size_t size,
+                                          size_t max_size,
+                                          unsigned int seed) {
+  fprintf(stderr, "BEFORE    %s\n", str(data, size).c_str());
+  size_t new_size = protobuf_mutator::libfuzzer::MutateBinaryMessage<Document>(
+      data, size, max_size, seed);
+  fprintf(stderr, "AFTER     %s\n", str(data, new_size).c_str());
+  return new_size;
+}
+
+extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* data1,
+                                            size_t size1,
+                                            const uint8_t* data2,
+                                            size_t size2,
+                                            uint8_t* out,
+                                            size_t max_out_size,
+                                            unsigned int seed) {
+  fprintf(stderr, "BEFOR1    %s\n", str(data1, size1).c_str());
+  fprintf(stderr, "BEFOR2    %s\n", str(data2, size2).c_str());
+  size_t new_size =
+      protobuf_mutator::libfuzzer::CrossOverBinaryMessages<Document>(
+          data1, size1, data2, size2, out, max_out_size, seed);
+  fprintf(stderr, "AFTER     %s\n", str(data1, new_size).c_str());
+  return new_size;
+}
+
+static Env* env = nullptr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Environment has to be initialized in the same thread.
+  if (env == nullptr)
+    env = new Env();
+
+  //  str(data, size);
+
+  env->adapter->LoadHTML(str(data, size), "http://www.example.org");
+
+  //  fprintf(stderr, "%s\n", writer.str().c_str());
+
+  return 0;
+}
+
+}  // namespace content
diff --git a/content/test/gpu/gpu_tests/gpu_process_expectations.py b/content/test/gpu/gpu_tests/gpu_process_expectations.py
index 6a74f2b..b2f13df7 100644
--- a/content/test/gpu/gpu_tests/gpu_process_expectations.py
+++ b/content/test/gpu/gpu_tests/gpu_process_expectations.py
@@ -20,7 +20,13 @@
     # Chrome on Windows creates a GPU process that uses SwiftShader when using
     # either --disable-gpu or a blacklisted GPU.
     self.Skip('GpuProcess_no_gpu_process', ['win', 'debug'], bug=630728)
-    self.Skip('GpuProcess_skip_gpu_process', ['win', 'debug'], bug=630728)
+    self.Skip('GpuProcess_skip_gpu_process', ['win'], bug=630728)
+
+    # Currently SwiftShader's integrated only on Windows. Remove
+    # platforms from this suppression as it is integrated on more
+    # platforms.
+    self.Skip('GpuProcess_swiftshader_for_webgl',
+              ['mac', 'linux', 'android', 'chromeos'], bug=630728)
 
     # There is no Android multi-gpu configuration and the helper
     # gpu_info_collector.cc::IdentifyActiveGPU is not even called.
diff --git a/content/test/gpu/gpu_tests/gpu_process_integration_test.py b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
index 39ab8718..247c952b 100644
--- a/content/test/gpu/gpu_tests/gpu_process_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
@@ -110,12 +110,13 @@
              ('GpuProcess_driver_bug_workarounds_upon_gl_renderer',
               'chrome:gpu'),
              ('GpuProcess_only_one_workaround', 'chrome:gpu'),
-             ('GpuProcess_skip_gpu_process', 'chrome:gpu'),
+             ('GpuProcess_skip_gpu_process', 'gpu/functional_webgl.html'),
              ('GpuProcess_identify_active_gpu1', 'chrome:gpu'),
              ('GpuProcess_identify_active_gpu2', 'chrome:gpu'),
              ('GpuProcess_identify_active_gpu3', 'chrome:gpu'),
              ('GpuProcess_identify_active_gpu4', 'chrome:gpu'),
-             ('GpuProcess_disabling_workarounds_works', 'chrome:gpu'))
+             ('GpuProcess_disabling_workarounds_works', 'chrome:gpu'),
+             ('GpuProcess_swiftshader_for_webgl', 'gpu/functional_webgl.html'))
 
     # The earlier has_transparent_visuals_gpu_process and
     # no_transparent_visuals_gpu_process tests became no-ops in
@@ -450,10 +451,15 @@
         (recorded_disabled_gl_extensions, new_disabled_gl_extensions))
 
   def _GpuProcess_skip_gpu_process(self, test_path):
+    # This test loads functional_webgl.html so that there is a
+    # deliberate attempt to use an API which would start the GPU
+    # process. On platforms where SwiftShader is used, this test
+    # should be skipped. Once SwiftShader is enabled on all platforms,
+    # this test should be removed.
     self.RestartBrowserIfNecessaryWithArgs([
       '--disable-gpu',
       '--skip-gpu-data-loading'])
-    self._Navigate(test_path)
+    self._NavigateAndWait(test_path)
     if self.tab.EvaluateJavaScript('chrome.gpuBenchmarking.hasGpuProcess()'):
       self.fail('GPU process detected')
 
@@ -523,6 +529,27 @@
     if 'use_gpu_driver_workaround_for_testing' in workarounds:
       self.fail('use_gpu_driver_workaround_for_testing erroneously present')
 
+  def _GpuProcess_swiftshader_for_webgl(self, test_path):
+    # This test loads functional_webgl.html so that there is a
+    # deliberate attempt to use an API which would start the GPU
+    # process. On Windows, and eventually on other platforms where
+    # SwiftShader is used, this test should pass.
+    #
+    # TODO(kbr): figure out a better way than --disable-gpu to
+    # reliably trigger SwiftShader.
+    self.RestartBrowserIfNecessaryWithArgs(['--disable-gpu'])
+    self._NavigateAndWait(test_path)
+    # It looks like when SwiftShader is in use (via --disable-gpu),
+    # that GPU information collection doesn't yet contain what's
+    # expected (the system_info.gpu.aux_attributes['gl_renderer']
+    # looks like it'll be null). Verified locally that we can fetch
+    # the desired information via WebGL.
+    renderer = self.tab.EvaluateJavaScript('gl_renderer')
+    if not renderer:
+      self.fail('getParameter(UNMASKED_RENDERER_WEBGL) was null')
+    if 'SwiftShader' not in renderer:
+      self.fail('Expected SwiftShader renderer; instead got ' + renderer)
+
 def load_tests(loader, tests, pattern):
   del loader, tests, pattern  # Unused.
   return gpu_integration_test.LoadAllTestsInModule(sys.modules[__name__])
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index db99f21..6f3f0f3 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -1002,11 +1002,20 @@
 
     # Linux AMD R7 240
     self.Fail('conformance2/textures/image_bitmap_from_video/' +
+        'tex-2d-rgba16f-rgba-float.html',
+        ['linux', ('amd', 0x6613)], bug=701138)
+    self.Fail('conformance2/textures/image_bitmap_from_video/' +
         'tex-2d-rgba16f-rgba-half_float.html',
         ['linux', ('amd', 0x6613)], bug=701138)
     self.Fail('conformance2/textures/image_bitmap_from_video/' +
         'tex-2d-rgba32f-rgba-float.html',
         ['linux', ('amd', 0x6613)], bug=701138)
+    self.Fail('conformance2/textures/image_bitmap_from_video/' +
+        'tex-2d-rgba4-rgba-unsigned_byte.html',
+        ['linux', ('amd', 0x6613)], bug=701138)
+    self.Fail('conformance2/textures/image_bitmap_from_video/' +
+        'tex-2d-rgba4-rgba-unsigned_short_4_4_4_4.html',
+        ['linux', ('amd', 0x6613)], bug=701138)
 
     # Conflicting expectations to test that the
     # "Expectations have no collisions" unittest works.
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 1f6128a08f..6c61b28 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -656,6 +656,11 @@
         ['android', 'android-chromium',
          ('nvidia', 'NVIDIA Tegra')], bug=624621)
 
+    # NVIDIA Shield
+    self.Flaky('conformance/context/' +
+        'context-eviction-with-garbage-collection.html',
+        ['android', ('nvidia', 'NVIDIA Tegra')], bug=701929)
+
     ############
     # ChromeOS #
     ############
diff --git a/content/test/gpu/run_gpu_integration_test.py b/content/test/gpu/run_gpu_integration_test.py
index 16deb3ae..4845805 100755
--- a/content/test/gpu/run_gpu_integration_test.py
+++ b/content/test/gpu/run_gpu_integration_test.py
@@ -5,6 +5,7 @@
 
 import argparse
 import json
+import os
 import sys
 
 from gpu_tests import path_util
@@ -15,31 +16,47 @@
 from telemetry.testing import browser_test_runner
 
 def PostprocessJSON(file_name, run_test_args):
-  def TrimPrefix(s):
-    return s[1 + s.rfind('.'):]
-  with open(file_name) as f:
-    test_result = json.load(f)
-  test_result['successes'] = map(TrimPrefix, test_result['successes'])
-  test_result['failures'] = map(TrimPrefix, test_result['failures'])
-  test_result['run_test_args'] = run_test_args
-  with open(file_name, 'w') as f:
-    json.dump(test_result, f)
+  # The file is not necessarily written depending on the arguments - only
+  # postprocess it in case it is.
+  if os.path.isfile(file_name):
+    with open(file_name) as f:
+      test_result = json.load(f)
+    test_result['run_test_args'] = run_test_args
+    with open(file_name, 'w') as f:
+      json.dump(test_result, f, indent=2)
 
 def main():
   rest_args = sys.argv[1:]
-  retval = browser_test_runner.Run(
-      gpu_project_config.CONFIG, rest_args)
-  # Postprocess the outputted JSON to trim all of the prefixes from
-  # the test names, to keep them as similar to the old form as
-  # possible -- and keep them from getting crazily long.
-  parser = argparse.ArgumentParser(description='Temporary argument parser')
+  parser = argparse.ArgumentParser(description='Extra argument parser',
+                                   add_help=False)
+
   parser.add_argument(
-    '--write-abbreviated-json-results-to', metavar='FILENAME',
+    '--write-run-test-arguments',
+    action='store_true',
+    help=('Write the test script arguments to the results file.'))
+  option, rest_args_filtered = parser.parse_known_args(rest_args)
+
+  retval = browser_test_runner.Run(
+      gpu_project_config.CONFIG, rest_args_filtered)
+
+  # We're not relying on argparse to print the help in the normal way, because
+  # we need the help output from both the argument parser here and the argument
+  # parser in browser_test_runner.
+  if '--help' in rest_args:
+    parser.print_help()
+    return retval
+
+  # This duplicates an argument of browser_test_runner.
+  parser.add_argument(
+    '--write-full-results-to', metavar='FILENAME',
     action='store',
-    help=('Full path for json results'))
+    help=('If specified, writes the full results to that path.'))
+
   option, _ = parser.parse_known_args(rest_args)
-  if option.write_abbreviated_json_results_to:
-    PostprocessJSON(option.write_abbreviated_json_results_to, rest_args)
+
+  # Postprocess the outputted JSON to add test arguments.
+  if option.write_run_test_arguments and option.write_full_results_to:
+    PostprocessJSON(option.write_full_results_to, rest_args)
   return retval
 
 if __name__ == '__main__':
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h
index 4a97d320..291adef 100644
--- a/device/bluetooth/bluetooth_adapter.h
+++ b/device/bluetooth/bluetooth_adapter.h
@@ -101,7 +101,6 @@
     //  * IsConnecting()
     //  * IsGattConnected()
     //  * IsPaired()
-    //  * IsTrustable()
     //
     // On Android and MacOS this method is called for each advertisement packet
     // received. On Chrome OS and Linux, we can't guarantee that this method
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index 587fcb7c..665cbb6 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -274,24 +274,12 @@
   // Microsoft "Microsoft Bluetooth Notebook Mouse 5000", model X807028-001
   if (type == BluetoothDeviceType::MOUSE && vendor == "7C:ED:8D")
     return false;
-  // Sony PlayStation Dualshock3
-  if (IsTrustable())
-    return false;
 
   // TODO: Move this database into a config file.
 
   return true;
 }
 
-bool BluetoothDevice::IsTrustable() const {
-  // Sony PlayStation Dualshock3
-  if ((GetVendorID() == 0x054c && GetProductID() == 0x0268 &&
-       GetName() == std::string("PLAYSTATION(R)3 Controller")))
-    return true;
-
-  return false;
-}
-
 BluetoothDevice::UUIDSet BluetoothDevice::GetUUIDs() const {
   return device_uuids_.GetUUIDs();
 }
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 8a5d063..8e9dedc 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -252,10 +252,6 @@
   bool IsPairable() const;
 
   // Indicates whether the device is paired with the adapter.
-  // On Chrome OS this function also returns true if the user has connected
-  // to the device in the past.
-  // TODO(crbug.com/649651): Change Chrome OS to only return true if the
-  // device is actually paired.
   virtual bool IsPaired() const = 0;
 
   // Indicates whether the device is currently connected to the adapter.
@@ -278,10 +274,6 @@
   // were called after the corresponding call to Connect().
   virtual bool IsConnecting() const = 0;
 
-  // Indicates whether the device can be trusted, based on device properties,
-  // such as vendor and product id.
-  bool IsTrustable() const;
-
   // Returns the set of UUIDs that this device supports.
   //  * For classic Bluetooth devices this data is collected from both the EIR
   //    data and SDP tables.
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
index 77ec31bf..1c40bbc 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -669,14 +669,6 @@
 
   // UMA connection counting
   if (property_name == properties->connected.name()) {
-    // PlayStation joystick tries to reconnect after disconnection from USB.
-    // If it is still not trusted, set it, so it becomes available on the
-    // list of known devices.
-    if (properties->connected.value()) {
-      if (device_bluez->IsTrustable() && !properties->trusted.value())
-        device_bluez->SetTrusted();
-    }
-
     int count = 0;
 
     for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
diff --git a/device/bluetooth/bluez/bluetooth_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
index 5db8d4fb..8d1b1e2 100644
--- a/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
@@ -2820,15 +2820,13 @@
                               kConnectedTrustedNotPairedDeviceAddress);
   ASSERT_TRUE(device != nullptr);
 
-  // On the DBus level the device is trusted but not paired. But the current
-  // implementation of |BluetoothDevice::IsPaired()| returns true in this case.
   bluez::FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
           dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::
                                kConnectedTrustedNotPairedDevicePath));
   EXPECT_FALSE(properties->paired.value());
   EXPECT_TRUE(properties->trusted.value());
-  ASSERT_TRUE(device->IsPaired());
+  ASSERT_FALSE(device->IsPaired());
 
   // The |kConnectedTrustedNotPairedDevicePath| requests a passkey confirmation.
   // Obs.: This is the flow when CrOS triggers pairing with a iOS device.
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.cc b/device/bluetooth/bluez/bluetooth_device_bluez.cc
index 8e09968..2df2d43 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.cc
@@ -333,10 +333,11 @@
           object_path_);
   DCHECK(properties);
 
-  // Trusted devices are devices that don't support pairing but that the
-  // user has explicitly connected; it makes no sense for UI purposes to
-  // treat them differently from each other.
-  return properties->paired.value() || properties->trusted.value();
+  // The Paired property reflects the successful pairing for BR/EDR/LE. The
+  // value of the Paired property is always false for the devices that don't
+  // support pairing. Once a device is paired successfully, both Paired and
+  // Trusted properties will be set to true.
+  return properties->paired.value();
 }
 
 bool BluetoothDeviceBlueZ::IsConnected() const {
@@ -449,7 +450,7 @@
   VLOG(1) << object_path_.value() << ": Connecting, " << num_connecting_calls_
           << " in progress";
 
-  if (IsPaired() || !pairing_delegate || !IsPairable()) {
+  if (IsPaired() || !pairing_delegate) {
     // No need to pair, or unable to, skip straight to connection.
     ConnectInternal(false, callback, error_callback);
   } else {
diff --git a/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java b/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
index 96508aa2..1ec65dee 100644
--- a/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
+++ b/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
@@ -63,11 +63,13 @@
             mNdef = ndef;
         }
 
+        @Override
         public void write(NdefMessage message)
                 throws IOException, TagLostException, FormatException {
             mNdef.writeNdefMessage(message);
         }
 
+        @Override
         public NdefMessage read() throws IOException, TagLostException, FormatException {
             return mNdef.getNdefMessage();
         }
@@ -84,11 +86,13 @@
             mNdefFormattable = ndefFormattable;
         }
 
+        @Override
         public void write(NdefMessage message)
                 throws IOException, TagLostException, FormatException {
             mNdefFormattable.format(message);
         }
 
+        @Override
         public NdefMessage read() throws IOException, TagLostException, FormatException {
             return NfcTypeConverter.emptyNdefMessage();
         }
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 3eaf1adc7..4c2c50ea 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -913,6 +913,7 @@
              (gl_version_info_->IsAtLeastGLES(3, 1) ||
               (gl_version_info_->IsAtLeastGL(3, 0) &&
                extensions.Contains("GL_ARB_shading_language_420pack") &&
+               extensions.Contains("GL_ARB_texture_storage") &&
                extensions.Contains("GL_ARB_texture_gather") &&
                extensions.Contains("GL_ARB_explicit_uniform_location") &&
                extensions.Contains("GL_ARB_explicit_attrib_location") &&
diff --git a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
index 4b9d7e43..e4cdd30 100644
--- a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
+++ b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
@@ -25,7 +25,6 @@
       is_in_gamma_correct_mode_(false),
       supports_usampler_(true),
       supports_r8_image_(true),
-      supports_r8_read_format_(true),
       is_gles31_compatible_(false),
       frame_id_(0),
       width_(0),
@@ -59,67 +58,55 @@
   is_gles31_compatible_ =
       decoder->GetGLContext()->GetVersionInfo()->IsAtLeastGLES(3, 1);
 
-  // Check if RGBA8UI is supported as an FBO colour target with depth.
-  // If not supported, GLSL needs to convert the data to/from float so there is
-  // a small extra cost.
-  {
-    GLuint rgba8ui_texture = 0, depth_texture = 0;
-    glGenTextures(1, &rgba8ui_texture);
-    glBindTexture(GL_TEXTURE_2D, rgba8ui_texture);
-    glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8UI, 4, 4);
+  if (is_gles31_compatible_) {
+    supports_r8_image_ =
+        decoder->GetGLContext()->HasExtension("GL_NV_image_formats");
 
-    glGenTextures(1, &depth_texture);
-    glBindTexture(GL_TEXTURE_2D, depth_texture);
-    glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 4, 4);
+    // ES 3.0 requires GL_RGBA8UI is color renderable.
+    supports_usampler_ = true;
+  } else {
+    // CMAA requires GL_ARB_shader_image_load_store for GL, and it requires r8
+    // image texture.
+    DCHECK(decoder->GetGLContext()->HasExtension(
+        "GL_ARB_shader_image_load_store"));
+    supports_r8_image_ = true;
 
-    // Create the FBO
-    GLuint rgba8ui_framebuffer = 0;
-    glGenFramebuffersEXT(1, &rgba8ui_framebuffer);
-    glBindFramebufferEXT(GL_FRAMEBUFFER, rgba8ui_framebuffer);
+    // Check if RGBA8UI is supported as an FBO colour target with depth.
+    // If not supported, GLSL needs to convert the data to/from float so there
+    // is a small extra cost.
+    {
+      glActiveTexture(GL_TEXTURE0);
 
-    // Bind to the FBO to test support
-    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                              GL_TEXTURE_2D, rgba8ui_texture, 0);
-    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-                              GL_TEXTURE_2D, depth_texture, 0);
-    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+      GLuint rgba8ui_texture = 0, depth_texture = 0;
+      glGenTextures(1, &rgba8ui_texture);
+      glBindTexture(GL_TEXTURE_2D, rgba8ui_texture);
+      glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8UI, 4, 4);
 
-    supports_usampler_ = (status == GL_FRAMEBUFFER_COMPLETE);
+      glGenTextures(1, &depth_texture);
+      glBindTexture(GL_TEXTURE_2D, depth_texture);
+      glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 4, 4);
 
-    glDeleteFramebuffersEXT(1, &rgba8ui_framebuffer);
-    glDeleteTextures(1, &rgba8ui_texture);
-    glDeleteTextures(1, &depth_texture);
-  }
+      // Create the FBO
+      GLuint rgba8ui_framebuffer = 0;
+      glGenFramebuffersEXT(1, &rgba8ui_framebuffer);
+      glBindFramebufferEXT(GL_FRAMEBUFFER, rgba8ui_framebuffer);
 
-  // Check to see if R8 images are supported
-  // If not supported, images are bound as R32F for write targets, not R8.
-  {
-    GLuint r8_texture = 0;
-    glGenTextures(1, &r8_texture);
-    glBindTexture(GL_TEXTURE_2D, r8_texture);
-    glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_R8, 4, 4);
+      // Bind to the FBO to test support
+      glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                GL_TEXTURE_2D, rgba8ui_texture, 0);
+      glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                                GL_TEXTURE_2D, depth_texture, 0);
+      GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
 
-    glGetError();  // reset all previous errors
-    glBindImageTextureEXT(0, r8_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8);
-    if (glGetError() != GL_NO_ERROR)
-      supports_r8_image_ = false;
+      supports_usampler_ = (status == GL_FRAMEBUFFER_COMPLETE);
 
-    glDeleteTextures(1, &r8_texture);
-  }
+      glDeleteFramebuffersEXT(1, &rgba8ui_framebuffer);
+      glDeleteTextures(1, &rgba8ui_texture);
+      glDeleteTextures(1, &depth_texture);
 
-  // Check if R8 GLSL read formats are supported.
-  // If not supported, r32f is used instead.
-  {
-    const char shader_source[] =
-        SHADER(layout(r8) restrict writeonly uniform highp image2D g_r8Image;
-               void main() {
-                 imageStore(g_r8Image, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 0.0));
-               });
-
-    GLuint shader = CreateShader(GL_FRAGMENT_SHADER, "", shader_source);
-    supports_r8_read_format_ = (shader != 0);
-    if (shader != 0) {
-      glDeleteShader(shader);
+      decoder->RestoreTextureUnitBindings(0);
+      decoder->RestoreActiveTexture();
+      decoder->RestoreFramebufferBindings();
     }
   }
 
@@ -128,9 +115,6 @@
   VLOG(1) << "ApplyFramebufferAttachmentCMAAINTEL: "
           << "Supports R8 Images is "
           << (supports_r8_image_ ? "true" : "false");
-  VLOG(1) << "ApplyFramebufferAttachmentCMAAINTEL: "
-          << "Supports R8 Read Format is "
-          << (supports_r8_read_format_ ? "true" : "false");
 
   // Create the shaders
   std::ostringstream defines, edge1, edge2, combineEdges, blur, displayEdges,
@@ -148,7 +132,7 @@
     defines << "#define IN_GAMMA_CORRECT_MODE\n";
   }
 
-  if (supports_r8_read_format_) {
+  if (supports_r8_image_) {
     defines << "#define EDGE_READ_FORMAT r8\n";
   } else {
     defines << "#define EDGE_READ_FORMAT r32f\n";
@@ -629,7 +613,7 @@
 
   const char header_es31[] =
       "#version 310 es                                                      \n";
-  const char header_gl30[] =
+  const char header_gl130[] =
       "#version 130                                                         \n"
       "#extension GL_ARB_shading_language_420pack  : require                \n"
       "#extension GL_ARB_texture_gather            : require                \n"
@@ -637,14 +621,17 @@
       "#extension GL_ARB_explicit_attrib_location  : require                \n"
       "#extension GL_ARB_shader_image_load_store   : require                \n";
 
-  const char* header = NULL;
+  std::ostringstream header;
   if (is_gles31_compatible_) {
-    header = header_es31;
+    header << header_es31;
+    if (supports_r8_image_)
+      header << "#extension GL_NV_image_formats : require\n";
   } else {
-    header = header_gl30;
+    header << header_gl130;
   }
 
-  const char* source_array[4] = {header, defines, "\n", source};
+  std::string header_str = header.str();
+  const char* source_array[4] = {header_str.c_str(), defines, "\n", source};
   glShaderSource(shader, 4, source_array, NULL);
 
   glCompileShader(shader);
@@ -821,42 +808,6 @@
       return ret;
     }
 
-    uint PackZ(const uvec2 screenPos, const bool invertedZShape) {
-      uint retVal = screenPos.x | (screenPos.y << 15u);
-      if (invertedZShape)
-        retVal |= (1u << 30u);
-      return retVal;
-    }
-
-    void UnpackZ(uint packedZ, out uvec2 screenPos,
-                               out bool invertedZShape)
-    {
-      screenPos.x = packedZ & 0x7FFFu;
-      screenPos.y = (packedZ >> 15u) & 0x7FFFu;
-      invertedZShape = (packedZ >> 30u) == 1u;
-    }
-
-    uint PackZ(const uvec2 screenPos,
-               const bool invertedZShape,
-               const bool horizontal) {
-      uint retVal = screenPos.x | (screenPos.y << 15u);
-      if (invertedZShape)
-        retVal |= (1u << 30u);
-      if (horizontal)
-        retVal |= (1u << 31u);
-      return retVal;
-    }
-
-    void UnpackZ(uint packedZ,
-                 out uvec2 screenPos,
-                 out bool invertedZShape,
-                 out bool horizontal) {
-      screenPos.x = packedZ & 0x7FFFu;
-      screenPos.y = (packedZ >> 15u) & 0x7FFFu;
-      invertedZShape = (packedZ & (1u << 30u)) != 0u;
-      horizontal = (packedZ & (1u << 31u)) != 0u;
-    }
-
     vec4 PackBlurAAInfo(ivec2 pixelPos, uint shapeType) {
       uint packedEdges = uint(
           texelFetch(g_src0TextureFlt, pixelPos, 0).r * 255.5);
diff --git a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h
index f9bad67..9f2bd9ae 100644
--- a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h
+++ b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h
@@ -57,7 +57,6 @@
   bool is_in_gamma_correct_mode_;
   bool supports_usampler_;
   bool supports_r8_image_;
-  bool supports_r8_read_format_;
   bool is_gles31_compatible_;
 
   int frame_id_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 125ef53..6fb3fae 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -17603,7 +17603,6 @@
       apply_framebuffer_attachment_cmaa_intel_.reset(
           new ApplyFramebufferAttachmentCMAAINTELResourceManager());
       apply_framebuffer_attachment_cmaa_intel_->Initialize(this);
-      RestoreCurrentFramebufferBindings();
       if (LOCAL_PEEK_GL_ERROR("glApplyFramebufferAttachmentCMAAINTEL") !=
           GL_NO_ERROR)
         return;
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 8ed8042..af5fb220 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -22,6 +22,10 @@
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/init/gl_factory.h"
 
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 namespace gpu {
 
 namespace {
@@ -175,6 +179,14 @@
 
   base::TimeTicks before_initialize_one_off = base::TimeTicks::Now();
 
+#if defined(USE_OZONE)
+  // Initialize Ozone GPU after the watchdog in case it hangs. The sandbox
+  // may also have started at this point.
+  ui::OzonePlatform::InitParams params;
+  params.single_process = false;
+  ui::OzonePlatform::InitializeForGPU(params);
+#endif
+
   // Load and initialize the GL implementation and locate the GL entry points if
   // needed. This initialization may have already happened if running in the
   // browser process, for example.
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 6a4a637..59a76ae8 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -3678,15 +3678,12 @@
     Tab* newTab = [_preloadController releasePrerenderContents];
     DCHECK(oldTab);
     DCHECK(newTab);
-    if (oldTab && newTab) {
+    bool canPruneItems =
+        [newTab navigationManager]->CanPruneAllButLastCommittedItem();
+    if (oldTab && newTab && canPruneItems) {
       [oldTab recordStateInHistory];
-      DCHECK([newTab navigationManager]);
-      CRWSessionController* newHistory =
-          [newTab navigationManagerImpl]->GetSessionController();
-      DCHECK([oldTab navigationManager]);
-      CRWSessionController* oldHistory =
-          [oldTab navigationManagerImpl]->GetSessionController();
-      [newHistory insertStateFromSessionController:oldHistory];
+      [newTab navigationManager]->CopyStateFromAndPrune(
+          [oldTab navigationManager]);
       [[newTab nativeAppNavigationController]
           copyStateFrom:[oldTab nativeAppNavigationController]];
       [_model replaceTab:oldTab withTab:newTab];
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h
index 6fdd181..8e017a1 100644
--- a/ios/web/navigation/crw_session_controller.h
+++ b/ios/web/navigation/crw_session_controller.h
@@ -41,6 +41,13 @@
 @property(nonatomic, readonly, strong)
     CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
 
+// Whether the CRWSessionController can prune all but the last committed item.
+// This is true when all the following conditions are met:
+// - There is a last committed NavigationItem
+// - There is not currently a pending history navigation
+// - There is no transient NavigationItem.
+@property(nonatomic, readonly) BOOL canPruneAllButLastCommittedItem;
+
 // The ScopedNavigationItemImplList used to store the NavigationItemImpls for
 // this session.
 @property(nonatomic, readonly) const web::ScopedNavigationItemImplList& items;
@@ -119,9 +126,18 @@
 // Removes the pending and transient NavigationItems.
 - (void)discardNonCommittedItems;
 
-// Inserts history state from |otherController| to the front of |items|.  This
-// function will create copies of |otherController|'s NavigationItems.
-- (void)insertStateFromSessionController:(CRWSessionController*)otherController;
+// Removes all items from this except the last committed item, and inserts
+// copies of all items from |source| at the beginning of the session history.
+//
+// For example:
+// source: A B *C* D
+// this:   E F *G*
+// result: A B C *G*
+//
+// If there is a pending item after *G* in |this|, it is also preserved.
+// This ignores any pending or transient entries in |source|.  No-op if
+// |canPruneAllButLastCommittedItem| is false.
+- (void)copyStateFromSessionControllerAndPrune:(CRWSessionController*)source;
 
 // Sets |currentNavigationIndex_| to the |index| if it's in the entries bounds.
 - (void)goToItemAtIndex:(NSInteger)index;
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm
index 1bb37874..e367d21b 100644
--- a/ios/web/navigation/crw_session_controller.mm
+++ b/ios/web/navigation/crw_session_controller.mm
@@ -152,6 +152,11 @@
   DCHECK(_pendingItemIndex == -1 || self.pendingItem);
 }
 
+- (BOOL)canPruneAllButLastCommittedItem {
+  return self.currentNavigationIndex != -1 && self.pendingItemIndex == -1 &&
+         !self.transientItem;
+}
+
 - (const web::ScopedNavigationItemImplList&)items {
   return _items;
 }
@@ -486,35 +491,44 @@
   _transientItem.reset();
 }
 
-- (void)insertStateFromSessionController:(CRWSessionController*)sourceSession {
-  DCHECK(sourceSession);
+- (void)copyStateFromSessionControllerAndPrune:(CRWSessionController*)source {
+  DCHECK(source);
+  if (!self.canPruneAllButLastCommittedItem)
+    return;
 
   // The other session may not have any items, in which case there is nothing
-  // to insert.  The other session's currentItem will be bogus in such cases, so
-  // ignore it and return early.
-  web::ScopedNavigationItemImplList& sourceItems = sourceSession->_items;
+  // to insert.
+  const web::ScopedNavigationItemImplList& sourceItems = source->_items;
   if (sourceItems.empty())
     return;
 
-  // Cycle through the items from the other session and insert them before any
-  // items from this session.  Do not copy anything that comes after the other
-  // session's current item.
-  NSInteger lastIndexToCopy = sourceSession.currentNavigationIndex;
-  for (NSInteger i = 0; i <= lastIndexToCopy; ++i) {
-    std::unique_ptr<web::NavigationItemImpl> sourceItemCopy =
-        base::MakeUnique<web::NavigationItemImpl>(*sourceItems[i]);
-    _items.insert(_items.begin() + i, std::move(sourceItemCopy));
+  // Early return if there's no committed source item.
+  if (!source.lastCommittedItem)
+    return;
+
+  // Copy |sourceItems| into a new NavigationItemList.  |mergedItems| is needs
+  // to be large enough for all items in |source| preceding
+  // |sourceCurrentIndex|, the |source|'s current item, and |self|'s current
+  // item, which comes out to |sourceCurrentIndex| + 2.
+  DCHECK_GT(source.currentNavigationIndex, -1);
+  size_t sourceCurrentIndex =
+      static_cast<size_t>(source.currentNavigationIndex);
+  web::ScopedNavigationItemImplList mergedItems(sourceCurrentIndex + 2);
+  for (size_t index = 0; index <= sourceCurrentIndex; ++index) {
+    mergedItems[index] =
+        base::MakeUnique<web::NavigationItemImpl>(*sourceItems[index]);
   }
+  mergedItems.back() = std::move(_items[self.currentNavigationIndex]);
+
+  // Use |mergedItems| as the session history.
+  std::swap(mergedItems, _items);
 
   // Update state to reflect inserted NavigationItems.
   _previousNavigationIndex = -1;
-  _currentNavigationIndex += lastIndexToCopy + 1;
-  if (self.pendingItemIndex != -1)
-    self.pendingItemIndex += lastIndexToCopy + 1;
+  _currentNavigationIndex = self.items.size() - 1;
 
   DCHECK_LT(static_cast<NSUInteger>(_currentNavigationIndex),
             self.items.size());
-  DCHECK(self.pendingItemIndex == -1 || self.pendingItem);
 }
 
 - (void)goToItemAtIndex:(NSInteger)index {
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm
index 7c7d0177..087aa4b 100644
--- a/ios/web/navigation/crw_session_controller_unittest.mm
+++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -473,7 +473,7 @@
 }
 
 // Tests inserting session controller state.
-TEST_F(CRWSessionControllerTest, InsertState) {
+TEST_F(CRWSessionControllerTest, CopyState) {
   // Add 1 committed and 1 pending item to target controller.
   [session_controller_
       addPendingItem:GURL("http://www.url.com/2")
@@ -504,8 +504,9 @@
       initiationType:web::NavigationInitiationType::USER_INITIATED];
 
   // Insert and verify the state of target session controller.
+  EXPECT_TRUE([session_controller_ canPruneAllButLastCommittedItem]);
   [session_controller_
-      insertStateFromSessionController:other_session_controller.get()];
+      copyStateFromSessionControllerAndPrune:other_session_controller.get()];
 
   EXPECT_EQ(2U, [session_controller_ items].size());
   EXPECT_EQ(1, [session_controller_ currentNavigationIndex]);
@@ -521,7 +522,7 @@
 }
 
 // Tests inserting session controller state from empty session controller.
-TEST_F(CRWSessionControllerTest, InsertStateFromEmptySessionController) {
+TEST_F(CRWSessionControllerTest, CopyStateFromEmptySessionController) {
   // Add 2 committed items to target controller.
   [session_controller_
       addPendingItem:GURL("http://www.url.com/0")
@@ -542,8 +543,9 @@
                                              openedByDOM:NO]);
 
   // Insert and verify the state of target session controller.
+  EXPECT_TRUE([session_controller_ canPruneAllButLastCommittedItem]);
   [session_controller_
-      insertStateFromSessionController:other_session_controller.get()];
+      copyStateFromSessionControllerAndPrune:other_session_controller.get()];
   EXPECT_EQ(2U, [session_controller_ items].size());
   EXPECT_EQ(1, [session_controller_ currentNavigationIndex]);
   EXPECT_EQ(0, [session_controller_ previousNavigationIndex]);
@@ -555,10 +557,12 @@
             [session_controller_ URLForItemAtIndex:1]);
 }
 
-// Tests inserting session controller state to empty session controller.
-TEST_F(CRWSessionControllerTest, InsertStateToEmptySessionController) {
-  // Create source session controller with 2 committed items and one
-  // pending item.
+// Tests that |-copyStateFromSessionControllerAndPrune:| is a no-op when the
+// receiver has no last committed item.
+TEST_F(CRWSessionControllerTest, CopyStateToEmptySessionController) {
+  EXPECT_FALSE([session_controller_ canPruneAllButLastCommittedItem]);
+
+  // Create source session controller with 1 committed item.
   base::scoped_nsobject<CRWSessionController> other_session_controller(
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_
                                              openedByDOM:NO]);
@@ -573,46 +577,35 @@
             referrer:web::Referrer()
           transition:ui::PAGE_TRANSITION_TYPED
       initiationType:web::NavigationInitiationType::USER_INITIATED];
-  [other_session_controller commitPendingItem];
-  [other_session_controller
-      addPendingItem:GURL("http://www.url.com/2")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
 
-  // Insert and verify the state of target session controller.
+  // Attempt to copy |other_session_controller|'s state and verify that
+  // |session_controller_| is unchanged.
   [session_controller_
-      insertStateFromSessionController:other_session_controller.get()];
-
-  EXPECT_EQ(2U, [session_controller_ items].size());
-  EXPECT_EQ(1, [session_controller_ currentNavigationIndex]);
+      copyStateFromSessionControllerAndPrune:other_session_controller];
+  EXPECT_TRUE([session_controller_ items].empty());
+  EXPECT_EQ(-1, [session_controller_ currentNavigationIndex]);
   EXPECT_EQ(-1, [session_controller_ previousNavigationIndex]);
-  EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
+  EXPECT_FALSE([session_controller_ currentItem]);
   EXPECT_FALSE([session_controller_ pendingItem]);
-  EXPECT_EQ(GURL("http://www.url.com/0"),
-            [session_controller_ URLForItemAtIndex:0]);
-  EXPECT_EQ(GURL("http://www.url.com/1"),
-            [session_controller_ URLForItemAtIndex:1]);
+  EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
 }
 
-// Tests inserting session controller state. Verifies that pending item index
-// remains valid.
-TEST_F(CRWSessionControllerTest,
-       InsertStateWithPendingItemIndexInTargetController) {
-  // Add 2 committed items and make the first item pending.
+// Tests that |-copyStateFromSessionControllerAndPrune:| is a no-op during a
+// pending history navigation.
+TEST_F(CRWSessionControllerTest, CopyStateDuringPendingHistoryNavigation) {
+  // Add 1 committed and 1 pending item to target controller.
+  [session_controller_
+      addPendingItem:GURL("http://www.url.com/1")
+            referrer:web::Referrer()
+          transition:ui::PAGE_TRANSITION_TYPED
+      initiationType:web::NavigationInitiationType::USER_INITIATED];
+  [session_controller_ commitPendingItem];
   [session_controller_
       addPendingItem:GURL("http://www.url.com/2")
             referrer:web::Referrer()
           transition:ui::PAGE_TRANSITION_TYPED
       initiationType:web::NavigationInitiationType::USER_INITIATED];
   [session_controller_ commitPendingItem];
-  [session_controller_
-      addPendingItem:GURL("http://www.url.com/3")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
-  [session_controller_ commitPendingItem];
-  [session_controller_ setPendingItemIndex:0];
 
   // Create source session controller with 1 committed item.
   base::scoped_nsobject<CRWSessionController> other_session_controller(
@@ -624,21 +617,76 @@
           transition:ui::PAGE_TRANSITION_TYPED
       initiationType:web::NavigationInitiationType::USER_INITIATED];
   [other_session_controller commitPendingItem];
+  [other_session_controller
+      addPendingItem:GURL("http://www.url.com/1")
+            referrer:web::Referrer()
+          transition:ui::PAGE_TRANSITION_TYPED
+      initiationType:web::NavigationInitiationType::USER_INITIATED];
 
-  // Insert and verify the state of target session controller.
+  // Set the pending item index to the first item.
+  [session_controller_ setPendingItemIndex:0];
+  EXPECT_FALSE([session_controller_ canPruneAllButLastCommittedItem]);
+
+  // Attempt to copy |other_session_controller|'s state and verify that
+  // |session_controller_| is unchanged.
   [session_controller_
-      insertStateFromSessionController:other_session_controller.get()];
+      copyStateFromSessionControllerAndPrune:other_session_controller];
+  EXPECT_EQ(2U, [session_controller_ items].size());
+  EXPECT_EQ(1, [session_controller_ currentNavigationIndex]);
+  EXPECT_EQ(0, [session_controller_ previousNavigationIndex]);
+  EXPECT_EQ(0, [session_controller_ pendingItemIndex]);
+  EXPECT_TRUE([session_controller_ pendingItem]);
+  EXPECT_EQ([session_controller_ previousItem],
+            [session_controller_ pendingItem]);
+}
 
-  EXPECT_EQ(3U, [session_controller_ items].size());
-  EXPECT_EQ(2, [session_controller_ currentNavigationIndex]);
+// Tests that |-copyStateFromSessionControllerAndPrune:| is a when a transient
+// NavigationItem exists.
+TEST_F(CRWSessionControllerTest, CopyStateWithTransientItem) {
+  // Add 1 committed and 1 pending item to target controller.
+  [session_controller_
+      addPendingItem:GURL("http://www.url.com/1")
+            referrer:web::Referrer()
+          transition:ui::PAGE_TRANSITION_TYPED
+      initiationType:web::NavigationInitiationType::USER_INITIATED];
+  [session_controller_ commitPendingItem];
+  GURL second_url = GURL("http://www.url.com/2");
+  [session_controller_
+      addPendingItem:second_url
+            referrer:web::Referrer()
+          transition:ui::PAGE_TRANSITION_TYPED
+      initiationType:web::NavigationInitiationType::USER_INITIATED];
+  [session_controller_ addTransientItemWithURL:second_url];
+
+  // Create source session controller with 1 committed item.
+  base::scoped_nsobject<CRWSessionController> other_session_controller(
+      [[CRWSessionController alloc] initWithBrowserState:&browser_state_
+                                             openedByDOM:NO]);
+  [other_session_controller
+      addPendingItem:GURL("http://www.url.com/0")
+            referrer:web::Referrer()
+          transition:ui::PAGE_TRANSITION_TYPED
+      initiationType:web::NavigationInitiationType::USER_INITIATED];
+  [other_session_controller commitPendingItem];
+  [other_session_controller
+      addPendingItem:GURL("http://www.url.com/1")
+            referrer:web::Referrer()
+          transition:ui::PAGE_TRANSITION_TYPED
+      initiationType:web::NavigationInitiationType::USER_INITIATED];
+
+  // Attempt to copy |other_session_controller|'s state and verify that
+  // |session_controller_| is unchanged.
+  EXPECT_FALSE([session_controller_ canPruneAllButLastCommittedItem]);
+  [session_controller_
+      copyStateFromSessionControllerAndPrune:other_session_controller];
+  EXPECT_EQ(1U, [session_controller_ items].size());
+  EXPECT_EQ(0, [session_controller_ currentNavigationIndex]);
   EXPECT_EQ(-1, [session_controller_ previousNavigationIndex]);
-  EXPECT_EQ(1, [session_controller_ pendingItemIndex]);
-  EXPECT_EQ(GURL("http://www.url.com/0"),
-            [session_controller_ URLForItemAtIndex:0]);
-  EXPECT_EQ(GURL("http://www.url.com/2"),
-            [session_controller_ URLForItemAtIndex:1]);
-  EXPECT_EQ(GURL("http://www.url.com/2"),
-            [session_controller_ pendingItem]->GetURL());
+  EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
+  EXPECT_TRUE([session_controller_ pendingItem]);
+  EXPECT_TRUE([session_controller_ transientItem]);
+  EXPECT_EQ([session_controller_ transientItem],
+            [session_controller_ currentItem]);
 }
 
 // Tests state of an empty session controller.
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
index 764ef58..56bd247 100644
--- a/ios/web/navigation/navigation_manager_impl.h
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -144,6 +144,8 @@
   void Reload(ReloadType reload_type, bool check_for_reposts) override;
   NavigationItemList GetBackwardItems() const override;
   NavigationItemList GetForwardItems() const override;
+  void CopyStateFromAndPrune(const NavigationManager* source) override;
+  bool CanPruneAllButLastCommittedItem() const override;
   void OverrideDesktopUserAgentForNextPendingItem() override;
 
   // Returns the current list of transient url rewriters, passing ownership to
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index 9acc1412..41454efe 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -360,6 +360,18 @@
   delegate_->GetWebState()->OpenURL(params);
 }
 
+void NavigationManagerImpl::CopyStateFromAndPrune(
+    const NavigationManager* manager) {
+  DCHECK(manager);
+  CRWSessionController* other_session =
+      static_cast<const NavigationManagerImpl*>(manager)->session_controller_;
+  [session_controller_ copyStateFromSessionControllerAndPrune:other_session];
+}
+
+bool NavigationManagerImpl::CanPruneAllButLastCommittedItem() const {
+  return [session_controller_ canPruneAllButLastCommittedItem];
+}
+
 std::unique_ptr<std::vector<BrowserURLRewriter::URLRewriter>>
 NavigationManagerImpl::GetTransientURLRewriters() {
   return std::move(transient_url_rewriters_);
diff --git a/ios/web/public/navigation_manager.h b/ios/web/public/navigation_manager.h
index 50bf82b..a8b1cf57 100644
--- a/ios/web/public/navigation_manager.h
+++ b/ios/web/public/navigation_manager.h
@@ -166,6 +166,26 @@
   virtual NavigationItemList GetBackwardItems() const = 0;
   virtual NavigationItemList GetForwardItems() const = 0;
 
+  // Removes all items from this except the last committed item, and inserts
+  // copies of all items from |source| at the beginning of the session history.
+  //
+  // For example:
+  // source: A B *C* D
+  // this:   E F *G*
+  // result: A B C *G*
+  //
+  // If there is a pending item after *G* in |this|, it is also preserved.
+  // This ignores any pending or transient entries in |source|.  This will be a
+  // no-op if called while CanPruneAllButLastCommittedItem() is false.
+  virtual void CopyStateFromAndPrune(const NavigationManager* source) = 0;
+
+  // Whether the NavigationManager can prune all but the last committed item.
+  // This is true when all the following conditions are met:
+  // - There is a last committed NavigationItem.
+  // - There is no pending history navigation.
+  // - There is no transient NavigationItem.
+  virtual bool CanPruneAllButLastCommittedItem() const = 0;
+
   // Forces the pending item to be loaded using desktop user agent. Note that
   // the pending item may or may not already exist.
   // TODO(crbug.com/692303): Remove this when overriding the user agent doesn't
diff --git a/ios/web/public/test/fakes/test_navigation_manager.h b/ios/web/public/test/fakes/test_navigation_manager.h
index e0d6cc6a..53a7ef0a 100644
--- a/ios/web/public/test/fakes/test_navigation_manager.h
+++ b/ios/web/public/test/fakes/test_navigation_manager.h
@@ -44,6 +44,8 @@
   void Reload(ReloadType reload_type, bool check_for_reposts) override;
   NavigationItemList GetBackwardItems() const override;
   NavigationItemList GetForwardItems() const override;
+  void CopyStateFromAndPrune(const NavigationManager* source) override;
+  bool CanPruneAllButLastCommittedItem() const override;
   void OverrideDesktopUserAgentForNextPendingItem() override;
 
   // Setters for test data.
diff --git a/ios/web/public/test/fakes/test_navigation_manager.mm b/ios/web/public/test/fakes/test_navigation_manager.mm
index 579a1e7..49cbc3b 100644
--- a/ios/web/public/test/fakes/test_navigation_manager.mm
+++ b/ios/web/public/test/fakes/test_navigation_manager.mm
@@ -150,6 +150,16 @@
   return NavigationItemList();
 }
 
+void TestNavigationManager::CopyStateFromAndPrune(
+    const NavigationManager* source) {
+  NOTREACHED();
+}
+
+bool TestNavigationManager::CanPruneAllButLastCommittedItem() const {
+  NOTREACHED();
+  return false;
+}
+
 void TestNavigationManager::OverrideDesktopUserAgentForNextPendingItem() {
   NOTREACHED();
 }
diff --git a/ios/web/webui/mojo_facade.h b/ios/web/webui/mojo_facade.h
index 0e773088..f22d5b37 100644
--- a/ios/web/webui/mojo_facade.h
+++ b/ios/web/webui/mojo_facade.h
@@ -10,7 +10,7 @@
 #include <string>
 
 #import "base/ios/weak_nsobject.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 @protocol CRWJSInjectionEvaluator;
 
@@ -133,7 +133,7 @@
   // Id of the last created watch.
   int last_watch_id_;
   // Currently active watches created through this facade.
-  std::map<int, std::unique_ptr<mojo::Watcher>> watchers_;
+  std::map<int, std::unique_ptr<mojo::SimpleWatcher>> watchers_;
 };
 
 }  // web
diff --git a/ios/web/webui/mojo_facade.mm b/ios/web/webui/mojo_facade.mm
index 651de5a..0761888 100644
--- a/ios/web/webui/mojo_facade.mm
+++ b/ios/web/webui/mojo_facade.mm
@@ -252,16 +252,17 @@
   int callback_id;
   CHECK(args->GetInteger("callbackId", &callback_id));
 
-  mojo::Watcher::ReadyCallback callback = base::BindBlockArc(^(
+  mojo::SimpleWatcher::ReadyCallback callback = base::BindBlockArc(^(
       MojoResult result) {
     NSString* script =
         [NSString stringWithFormat:@"__crWeb.mojo.signalWatch(%d, %d)",
                                    callback_id, result];
     [script_evaluator_ executeJavaScript:script completionHandler:nil];
   });
-  mojo::Watcher* watcher = new mojo::Watcher(FROM_HERE);
+  mojo::SimpleWatcher* watcher = new mojo::SimpleWatcher(
+      FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC);
   watchers_.insert(std::make_pair(++last_watch_id_, base::WrapUnique(watcher)));
-  watcher->Start(static_cast<mojo::Handle>(handle), signals, callback);
+  watcher->Watch(static_cast<mojo::Handle>(handle), signals, callback);
   return ValueFromInteger(last_watch_id_);
 }
 
diff --git a/ios/web/webui/mojo_facade_unittest.mm b/ios/web/webui/mojo_facade_unittest.mm
index 542124c..cf7e512 100644
--- a/ios/web/webui/mojo_facade_unittest.mm
+++ b/ios/web/webui/mojo_facade_unittest.mm
@@ -199,6 +199,18 @@
                                  callback_id, MOJO_RESULT_OK];
   [[[evaluator() expect] andDo:^(NSInvocation*) {
     callback_received = true;
+
+    // Cancel the watch immediately to ensure there are no additional
+    // notifications.
+    NSDictionary* cancel_watch = @{
+      @"name" : @"support.cancelWatch",
+      @"args" : @{
+        @"watchId" : @(watch_id),
+      },
+    };
+    std::string result_as_string =
+        facade()->HandleMojoMessage(GetJson(cancel_watch));
+    EXPECT_TRUE(result_as_string.empty());
   }] executeJavaScript:expected_script completionHandler:nil];
 
   // Write to the other end of the pipe.
diff --git a/ios/web/webui/web_ui_mojo_inttest.mm b/ios/web/webui/web_ui_mojo_inttest.mm
index 64a3d1ea..cd69059 100644
--- a/ios/web/webui/web_ui_mojo_inttest.mm
+++ b/ios/web/webui/web_ui_mojo_inttest.mm
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #import "base/test/ios/wait_util.h"
+#include "base/threading/thread_task_runner_handle.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/web_ui_ios_data_source.h"
 #include "ios/web/public/webui/web_ui_ios_controller.h"
@@ -161,7 +162,16 @@
 
   // Wait until |TestUIHandler| receives "ack" message from WebUI page.
   base::test::ios::WaitUntilCondition(^{
-    base::RunLoop().RunUntilIdle();
+    // Flush any pending tasks. Don't RunUntilIdle() because
+    // RunUntilIdle() is incompatible with mojo::SimpleWatcher's
+    // automatic arming behavior, which Mojo JS still depends upon.
+    //
+    // TODO(crbug.com/701875): Introduce the full watcher API to JS and get rid
+    // of this hack.
+    base::RunLoop loop;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  loop.QuitClosure());
+    loop.Run();
     return test_ui_handler()->IsFinReceived();
   });
 }
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 840c89d9..6852399 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -41,9 +41,9 @@
   *error = result != MOJO_RESULT_OK;
 }
 
-// A ReadyCallback for use with mojo::Watcher. Ignores the result (DCHECKs, but
-// is only used in cases where failure should be impossible) and runs
-// |callback|.
+// A ReadyCallback for use with mojo::SimpleWatcher. Ignores the result
+// (DCHECKs, but is only used in cases where failure should be impossible) and
+// runs |callback|.
 void RunOnHandleReady(const base::Closure& callback, MojoResult result) {
   DCHECK_EQ(result, MOJO_RESULT_OK);
   callback.Run();
@@ -230,11 +230,11 @@
     }
   }
 
-  mojo::Watcher* top_send_done_watcher() {
+  mojo::SimpleWatcher* top_send_done_watcher() {
     return top_send_done_watcher_;
   }
 
-  void set_top_send_done_watcher(mojo::Watcher* watcher) {
+  void set_top_send_done_watcher(mojo::SimpleWatcher* watcher) {
     top_send_done_watcher_ = watcher;
   }
 
@@ -300,7 +300,7 @@
   // The current send done handle watcher for this thread. Used to maintain
   // a thread-local stack of send done watchers to ensure that nested sync
   // message loops complete correctly.
-  mojo::Watcher* top_send_done_watcher_;
+  mojo::SimpleWatcher* top_send_done_watcher_;
 
   // If not null, the address of a flag to set when the dispatch event signals,
   // in lieu of actually dispatching messages. This is used by
@@ -527,7 +527,8 @@
     WaitableEvent* shutdown_event)
     : ChannelProxy(new SyncContext(listener, ipc_task_runner, shutdown_event)),
       sync_handle_registry_(mojo::SyncHandleRegistry::current()),
-      dispatch_watcher_(FROM_HERE) {
+      dispatch_watcher_(FROM_HERE,
+                        mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {
   // The current (listener) thread must be distinct from the IPC thread, or else
   // sending synchronous messages will deadlock.
   DCHECK_NE(ipc_task_runner.get(), base::ThreadTaskRunnerHandle::Get().get());
@@ -622,7 +623,6 @@
     context->received_sync_msgs()->UnblockDispatch();
     DCHECK(!error);
 
-
     registry->UnregisterHandle(context->GetSendDoneEvent()->GetHandle());
     if (pump_messages_event)
       registry->UnregisterHandle(pump_messages_event->GetHandle());
@@ -643,14 +643,15 @@
 }
 
 void SyncChannel::WaitForReplyWithNestedMessageLoop(SyncContext* context) {
-  mojo::Watcher send_done_watcher(FROM_HERE);
+  mojo::SimpleWatcher send_done_watcher(
+      FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC);
 
   ReceivedSyncMsgQueue* sync_msg_queue = context->received_sync_msgs();
   DCHECK_NE(sync_msg_queue, nullptr);
 
-  mojo::Watcher* old_watcher = sync_msg_queue->top_send_done_watcher();
+  mojo::SimpleWatcher* old_watcher = sync_msg_queue->top_send_done_watcher();
   mojo::Handle old_handle(mojo::kInvalidHandleValue);
-  mojo::Watcher::ReadyCallback old_callback;
+  mojo::SimpleWatcher::ReadyCallback old_callback;
 
   // Maintain a thread-local stack of watchers to ensure nested calls complete
   // in the correct sequence, i.e. the outermost call completes first, etc.
@@ -664,7 +665,7 @@
 
   {
     base::RunLoop nested_loop;
-    send_done_watcher.Start(
+    send_done_watcher.Watch(
         context->GetSendDoneEvent()->GetHandle(), MOJO_HANDLE_SIGNAL_READABLE,
         base::Bind(&RunOnHandleReady, nested_loop.QuitClosure()));
 
@@ -676,7 +677,7 @@
 
   sync_msg_queue->set_top_send_done_watcher(old_watcher);
   if (old_watcher)
-    old_watcher->Start(old_handle, MOJO_HANDLE_SIGNAL_READABLE, old_callback);
+    old_watcher->Watch(old_handle, MOJO_HANDLE_SIGNAL_READABLE, old_callback);
 }
 
 void SyncChannel::OnDispatchHandleReady(MojoResult result) {
@@ -690,10 +691,10 @@
   // messages once the listener thread is unblocked and pumping its task queue.
   // The ReceivedSyncMsgQueue also watches this event and may dispatch
   // immediately if woken up by a message which it's allowed to dispatch.
-  dispatch_watcher_.Start(sync_context()->GetDispatchEvent()->GetHandle(),
-                          MOJO_HANDLE_SIGNAL_READABLE,
-                          base::Bind(&SyncChannel::OnDispatchHandleReady,
-                                     base::Unretained(this)));
+  dispatch_watcher_.Watch(
+      sync_context()->GetDispatchEvent()->GetHandle(),
+      MOJO_HANDLE_SIGNAL_READABLE,
+      base::Bind(&SyncChannel::OnDispatchHandleReady, base::Unretained(this)));
 }
 
 void SyncChannel::OnChannelInit() {
diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h
index e8c96d20..7738b7f 100644
--- a/ipc/ipc_sync_channel.h
+++ b/ipc/ipc_sync_channel.h
@@ -19,7 +19,7 @@
 #include "ipc/ipc_sync_message.h"
 #include "ipc/ipc_sync_message_filter.h"
 #include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace base {
 class WaitableEvent;
@@ -27,7 +27,6 @@
 
 namespace mojo {
 class SyncHandleRegistry;
-class Watcher;
 }
 
 namespace IPC {
@@ -241,7 +240,7 @@
   scoped_refptr<mojo::SyncHandleRegistry> sync_handle_registry_;
 
   // Used to signal events between the IPC and listener threads.
-  mojo::Watcher dispatch_watcher_;
+  mojo::SimpleWatcher dispatch_watcher_;
 
   // Tracks SyncMessageFilters created before complete channel initialization.
   std::vector<scoped_refptr<SyncMessageFilter>> pre_init_sync_message_filters_;
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index 380068a..e942169 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -701,6 +701,7 @@
         return true;
     }
 
+    @Override
     public PhotoCapabilities getPhotoCapabilities() {
         final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mContext, mId);
         PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder();
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
index 06233eaa..acccf2e 100644
--- a/media/filters/ffmpeg_audio_decoder.cc
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -95,12 +95,17 @@
     return buffer_size_in_bytes;
   int frames_required = buffer_size_in_bytes / bytes_per_channel / channels;
   DCHECK_GE(frames_required, frame->nb_samples);
+
+  ChannelLayout channel_layout =
+      ChannelLayoutToChromeChannelLayout(s->channel_layout, s->channels);
+
+  if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
+    DLOG(ERROR) << "Unsupported channel layout.";
+    return AVERROR(EINVAL);
+  }
+
   scoped_refptr<AudioBuffer> buffer = AudioBuffer::CreateBuffer(
-      sample_format,
-      ChannelLayoutToChromeChannelLayout(s->channel_layout, s->channels),
-      channels,
-      s->sample_rate,
-      frames_required);
+      sample_format, channel_layout, channels, s->sample_rate, frames_required);
 
   // Initialize the data[] and extended_data[] fields to point into the memory
   // allocated for AudioBuffer. |number_of_planes| will be 1 for interleaved
diff --git a/media/gpu/dxva_picture_buffer_win.cc b/media/gpu/dxva_picture_buffer_win.cc
index 44f27f9..bcaa177 100644
--- a/media/gpu/dxva_picture_buffer_win.cc
+++ b/media/gpu/dxva_picture_buffer_win.cc
@@ -370,6 +370,10 @@
   return true;
 }
 
+bool PbufferPictureBuffer::AllowOverlay() const {
+  return false;
+}
+
 PbufferPictureBuffer::PbufferPictureBuffer(const PictureBuffer& buffer)
     : DXVAPictureBuffer(buffer),
       decoding_surface_(NULL),
@@ -508,6 +512,10 @@
   return true;
 }
 
+bool EGLStreamPictureBuffer::AllowOverlay() const {
+  return true;
+}
+
 EGLStreamCopyPictureBuffer::EGLStreamCopyPictureBuffer(
     const PictureBuffer& buffer)
     : DXVAPictureBuffer(buffer), stream_(nullptr) {}
@@ -671,4 +679,8 @@
   return true;
 }
 
+bool EGLStreamCopyPictureBuffer::AllowOverlay() const {
+  return true;
+}
+
 }  // namespace media
diff --git a/media/gpu/dxva_picture_buffer_win.h b/media/gpu/dxva_picture_buffer_win.h
index ee4ce0c..29d502d1 100644
--- a/media/gpu/dxva_picture_buffer_win.h
+++ b/media/gpu/dxva_picture_buffer_win.h
@@ -61,6 +61,11 @@
     color_space_ = color_space;
   }
 
+  // Returns true if these could in theory be used as an overlay. May
+  // still be drawn using GL depending on the scene and precise hardware
+  // support.
+  virtual bool AllowOverlay() const = 0;
+
   bool waiting_to_reuse() const { return state_ == WAITING_TO_REUSE; }
   virtual gl::GLFence* reuse_fence();
 
@@ -102,6 +107,7 @@
   gl::GLFence* reuse_fence() override;
   bool CopySurfaceComplete(IDirect3DSurface9* src_surface,
                            IDirect3DSurface9* dest_surface) override;
+  bool AllowOverlay() const override;
 
  protected:
   EGLSurface decoding_surface_;
@@ -144,6 +150,7 @@
   bool Initialize();
   bool ReusePictureBuffer() override;
   bool BindSampleToTexture(base::win::ScopedComPtr<IMFSample> sample) override;
+  bool AllowOverlay() const override;
 
  private:
   EGLStreamKHR stream_;
@@ -167,6 +174,7 @@
                                            int input_buffer_id) override;
   bool CopySurfaceComplete(IDirect3DSurface9* src_surface,
                            IDirect3DSurface9* dest_surface) override;
+  bool AllowOverlay() const override;
 
  private:
   EGLStreamKHR stream_;
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc
index 3a36c38..a5d0ad9 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -2088,14 +2088,15 @@
 void DXVAVideoDecodeAccelerator::NotifyPictureReady(
     int picture_buffer_id,
     int input_buffer_id,
-    const gfx::ColorSpace& color_space) {
+    const gfx::ColorSpace& color_space,
+    bool allow_overlay) {
   DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
   // This task could execute after the decoder has been torn down.
   if (GetState() != kUninitialized && client_) {
     // TODO(henryhsu): Use correct visible size instead of (0, 0). We can't use
     // coded size here so use (0, 0) intentionally to have the client choose.
     Picture picture(picture_buffer_id, input_buffer_id, gfx::Rect(0, 0),
-                    color_space, false);
+                    color_space, allow_overlay);
     client_->PictureReady(picture);
   }
 }
@@ -2506,7 +2507,8 @@
                                PLATFORM_FAILURE, );
 
   NotifyPictureReady(picture_buffer->id(), input_buffer_id,
-                     picture_buffer->color_space());
+                     picture_buffer->color_space(),
+                     picture_buffer->AllowOverlay());
 
   {
     base::AutoLock lock(decoder_lock_);
@@ -2559,7 +2561,8 @@
                                PLATFORM_FAILURE, );
 
   NotifyPictureReady(picture_buffer->id(), input_buffer_id,
-                     picture_buffer->color_space());
+                     picture_buffer->color_space(),
+                     picture_buffer->AllowOverlay());
 
   {
     base::AutoLock lock(decoder_lock_);
diff --git a/media/gpu/dxva_video_decode_accelerator_win.h b/media/gpu/dxva_video_decode_accelerator_win.h
index 6422f63f..9637845 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.h
+++ b/media/gpu/dxva_video_decode_accelerator_win.h
@@ -241,7 +241,8 @@
   // Notifies the client about the availability of a picture.
   void NotifyPictureReady(int picture_buffer_id,
                           int input_buffer_id,
-                          const gfx::ColorSpace& color_space);
+                          const gfx::ColorSpace& color_space,
+                          bool allow_overlay);
 
   // Sends pending input buffer processed acks to the client if we don't have
   // output samples waiting to be processed.
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index 9ed0624..cb61aced 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -450,7 +450,7 @@
         texture_manager->SetLevelInfo(texture_ref, texture_target_, 0, GL_RGBA,
                                       texture_dimensions_.width(),
                                       texture_dimensions_.height(), 1, 0,
-                                      GL_RGBA, 0, gfx::Rect());
+                                      GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
       } else {
         // For other targets, texture dimensions should already be defined.
         GLsizei width = 0, height = 0;
@@ -467,9 +467,10 @@
         GLenum format =
             video_decode_accelerator_.get()->GetSurfaceInternalFormat();
         if (format != GL_RGBA) {
+          DCHECK(format == GL_BGRA_EXT);
           texture_manager->SetLevelInfo(texture_ref, texture_target_, 0, format,
-                                        width, height, 1, 0, format, 0,
-                                        gfx::Rect());
+                                        width, height, 1, 0, format,
+                                        GL_UNSIGNED_BYTE, gfx::Rect());
         }
       }
       service_ids.push_back(texture_ref->service_id());
diff --git a/media/mojo/common/mojo_decoder_buffer_converter.cc b/media/mojo/common/mojo_decoder_buffer_converter.cc
index 32080d3..d47e5f5 100644
--- a/media/mojo/common/mojo_decoder_buffer_converter.cc
+++ b/media/mojo/common/mojo_decoder_buffer_converter.cc
@@ -64,18 +64,20 @@
 MojoDecoderBufferReader::MojoDecoderBufferReader(
     mojo::ScopedDataPipeConsumerHandle consumer_handle)
     : consumer_handle_(std::move(consumer_handle)),
-      pipe_watcher_(FROM_HERE),
+      pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       bytes_read_(0) {
   DVLOG(1) << __func__;
 
   MojoResult result =
-      pipe_watcher_.Start(consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+      pipe_watcher_.Watch(consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
                           base::Bind(&MojoDecoderBufferReader::OnPipeReadable,
                                      base::Unretained(this)));
   if (result != MOJO_RESULT_OK) {
     DVLOG(1) << __func__
              << ": Failed to start watching the pipe. result=" << result;
     consumer_handle_.reset();
+  } else {
+    pipe_watcher_.ArmOrNotify();
   }
 }
 
@@ -159,13 +161,16 @@
 
   if (IsPipeReadWriteError(result)) {
     OnPipeError(result);
-  } else if (result == MOJO_RESULT_OK) {
-    DCHECK_GT(num_bytes, 0u);
-    bytes_read_ += num_bytes;
-    if (bytes_read_ == buffer_size) {
-      DCHECK(read_cb_);
-      bytes_read_ = 0;
-      std::move(read_cb_).Run(std::move(media_buffer_));
+  } else {
+    pipe_watcher_.ArmOrNotify();
+    if (result == MOJO_RESULT_OK) {
+      DCHECK_GT(num_bytes, 0u);
+      bytes_read_ += num_bytes;
+      if (bytes_read_ == buffer_size) {
+        DCHECK(read_cb_);
+        bytes_read_ = 0;
+        std::move(read_cb_).Run(std::move(media_buffer_));
+      }
     }
   }
 }
@@ -186,18 +191,20 @@
 MojoDecoderBufferWriter::MojoDecoderBufferWriter(
     mojo::ScopedDataPipeProducerHandle producer_handle)
     : producer_handle_(std::move(producer_handle)),
-      pipe_watcher_(FROM_HERE),
+      pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       bytes_written_(0) {
   DVLOG(1) << __func__;
 
   MojoResult result =
-      pipe_watcher_.Start(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+      pipe_watcher_.Watch(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
                           base::Bind(&MojoDecoderBufferWriter::OnPipeWritable,
                                      base::Unretained(this)));
   if (result != MOJO_RESULT_OK) {
     DVLOG(1) << __func__
              << ": Failed to start watching the pipe. result=" << result;
     producer_handle_.reset();
+  } else {
+    pipe_watcher_.ArmOrNotify();
   }
 }
 
@@ -273,12 +280,15 @@
 
   if (IsPipeReadWriteError(result)) {
     OnPipeError(result);
-  } else if (result == MOJO_RESULT_OK) {
-    DCHECK_GT(num_bytes, 0u);
-    bytes_written_ += num_bytes;
-    if (bytes_written_ == buffer_size) {
-      media_buffer_ = nullptr;
-      bytes_written_ = 0;
+  } else {
+    pipe_watcher_.ArmOrNotify();
+    if (result == MOJO_RESULT_OK) {
+      DCHECK_GT(num_bytes, 0u);
+      bytes_written_ += num_bytes;
+      if (bytes_written_ == buffer_size) {
+        media_buffer_ = nullptr;
+        bytes_written_ = 0;
+      }
     }
   }
 
diff --git a/media/mojo/common/mojo_decoder_buffer_converter.h b/media/mojo/common/mojo_decoder_buffer_converter.h
index cc58469..10940f3 100644
--- a/media/mojo/common/mojo_decoder_buffer_converter.h
+++ b/media/mojo/common/mojo_decoder_buffer_converter.h
@@ -12,7 +12,7 @@
 #include "media/base/demuxer_stream.h"
 #include "media/mojo/interfaces/media_types.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace media {
 
@@ -47,7 +47,7 @@
 
   // For reading the data section of a DecoderBuffer.
   mojo::ScopedDataPipeConsumerHandle consumer_handle_;
-  mojo::Watcher pipe_watcher_;
+  mojo::SimpleWatcher pipe_watcher_;
 
   // Only valid during pending read.
   ReadCB read_cb_;
@@ -85,7 +85,7 @@
 
   // For writing the data section of DecoderBuffer into DataPipe.
   mojo::ScopedDataPipeProducerHandle producer_handle_;
-  mojo::Watcher pipe_watcher_;
+  mojo::SimpleWatcher pipe_watcher_;
 
   // Only valid when data is being written to the pipe.
   scoped_refptr<DecoderBuffer> media_buffer_;
diff --git a/media/remoting/demuxer_stream_adapter.cc b/media/remoting/demuxer_stream_adapter.cc
index c12ee00..de8bfd0 100644
--- a/media/remoting/demuxer_stream_adapter.cc
+++ b/media/remoting/demuxer_stream_adapter.cc
@@ -57,7 +57,7 @@
       pending_flush_(false),
       current_pending_frame_offset_(0),
       pending_frame_is_eos_(false),
-      write_watcher_(FROM_HERE),
+      write_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       media_status_(DemuxerStream::kOk),
       producer_handle_(std::move(producer_handle)),
       bytes_written_to_pipe_(0),
@@ -202,9 +202,10 @@
   // Starts Mojo watcher.
   if (!write_watcher_.IsWatching()) {
     DEMUXER_VLOG(2) << "Start Mojo data pipe watcher";
-    write_watcher_.Start(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+    write_watcher_.Watch(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
                          base::Bind(&DemuxerStreamAdapter::TryWriteData,
                                     weak_factory_.GetWeakPtr()));
+    write_watcher_.ArmOrNotify();
   }
 }
 
@@ -326,15 +327,17 @@
       WriteDataRaw(producer_handle_.get(),
                    pending_frame_.data() + current_pending_frame_offset_,
                    &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
-  if (mojo_result != MOJO_RESULT_OK) {
-    if (mojo_result != MOJO_RESULT_SHOULD_WAIT) {
-      DEMUXER_VLOG(1) << "Pipe was closed unexpectedly (or a bug). result:"
-                      << mojo_result;
-      OnFatalError(MOJO_PIPE_ERROR);
-    }
+  if (mojo_result != MOJO_RESULT_OK && mojo_result != MOJO_RESULT_SHOULD_WAIT) {
+    DEMUXER_VLOG(1) << "Pipe was closed unexpectedly (or a bug). result:"
+                    << mojo_result;
+    OnFatalError(MOJO_PIPE_ERROR);
     return;
   }
 
+  write_watcher_.ArmOrNotify();
+  if (mojo_result != MOJO_RESULT_OK)
+    return;
+
   stream_sender_->ConsumeDataChunk(current_pending_frame_offset_, num_bytes,
                                    pending_frame_.size());
   current_pending_frame_offset_ += num_bytes;
diff --git a/media/remoting/demuxer_stream_adapter.h b/media/remoting/demuxer_stream_adapter.h
index 141fe8e..350ad665 100644
--- a/media/remoting/demuxer_stream_adapter.h
+++ b/media/remoting/demuxer_stream_adapter.h
@@ -21,6 +21,7 @@
 #include "media/remoting/rpc_broker.h"
 #include "media/remoting/triggers.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -167,7 +168,7 @@
   bool pending_frame_is_eos_;
 
   // Monitor if data pipe is available to write data.
-  mojo::Watcher write_watcher_;
+  mojo::SimpleWatcher write_watcher_;
 
   // Keeps latest demuxer stream status and audio/video decoder config.
   DemuxerStream::Status media_status_;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java
index 6a99fe15..e14adb1 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java
@@ -68,6 +68,17 @@
 
     private static class WatcherResult implements Callback {
         private int mResult = Integer.MIN_VALUE;
+        private MessagePipeHandle mReadPipe;
+
+        /**
+         * @param readPipe A MessagePipeHandle to read from when onResult triggers success.
+         */
+        public WatcherResult(MessagePipeHandle readPipe) {
+            mReadPipe = readPipe;
+        }
+        public WatcherResult() {
+            this(null);
+        }
 
         /**
          * @see Callback#onResult(int)
@@ -75,6 +86,11 @@
         @Override
         public void onResult(int result) {
             this.mResult = result;
+
+            if (result == MojoResult.OK && mReadPipe != null) {
+                mReadPipe.readMessage(
+                        null, 0, MessagePipeHandle.ReadFlags.none().setMayDiscard(true));
+            }
         }
 
         /**
@@ -93,7 +109,7 @@
         // Checking a correct result.
         Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
         addHandlePairToClose(handles);
-        final WatcherResult watcherResult = new WatcherResult();
+        final WatcherResult watcherResult = new WatcherResult(handles.first);
         assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
 
         mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult);
diff --git a/mojo/android/system/watcher_impl.cc b/mojo/android/system/watcher_impl.cc
index 28d8c9f1..139dfb4 100644
--- a/mojo/android/system/watcher_impl.cc
+++ b/mojo/android/system/watcher_impl.cc
@@ -15,7 +15,7 @@
 #include "base/bind.h"
 #include "jni/WatcherImpl_jni.h"
 #include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace mojo {
 namespace android {
@@ -26,7 +26,7 @@
 
 class WatcherImpl {
  public:
-  WatcherImpl() : watcher_(FROM_HERE) {}
+  WatcherImpl() : watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC) {}
 
   ~WatcherImpl() = default;
 
@@ -40,9 +40,8 @@
         base::Bind(&WatcherImpl::OnHandleReady, base::Unretained(this));
 
     MojoResult result =
-        watcher_.Start(mojo::Handle(static_cast<MojoHandle>(mojo_handle)),
+        watcher_.Watch(mojo::Handle(static_cast<MojoHandle>(mojo_handle)),
                        static_cast<MojoHandleSignals>(signals), ready_callback);
-
     if (result != MOJO_RESULT_OK)
       java_watcher_.Reset();
 
@@ -68,7 +67,7 @@
         result);
   }
 
-  Watcher watcher_;
+  SimpleWatcher watcher_;
   base::android::ScopedJavaGlobalRef<jobject> java_watcher_;
 
   DISALLOW_COPY_AND_ASSIGN(WatcherImpl);
diff --git a/mojo/common/data_pipe_drainer.cc b/mojo/common/data_pipe_drainer.cc
index 27bd893d..e705c8d 100644
--- a/mojo/common/data_pipe_drainer.cc
+++ b/mojo/common/data_pipe_drainer.cc
@@ -17,10 +17,10 @@
                                  mojo::ScopedDataPipeConsumerHandle source)
     : client_(client),
       source_(std::move(source)),
-      handle_watcher_(FROM_HERE),
+      handle_watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC),
       weak_factory_(this) {
   DCHECK(client_);
-  handle_watcher_.Start(
+  handle_watcher_.Watch(
       source_.get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::Bind(&DataPipeDrainer::WaitComplete, weak_factory_.GetWeakPtr()));
 }
diff --git a/mojo/common/data_pipe_drainer.h b/mojo/common/data_pipe_drainer.h
index d0366fa..5cff820 100644
--- a/mojo/common/data_pipe_drainer.h
+++ b/mojo/common/data_pipe_drainer.h
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "mojo/common/mojo_common_export.h"
 #include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace mojo {
 namespace common {
@@ -36,7 +36,7 @@
 
   Client* client_;
   mojo::ScopedDataPipeConsumerHandle source_;
-  mojo::Watcher handle_watcher_;
+  mojo::SimpleWatcher handle_watcher_;
 
   base::WeakPtrFactory<DataPipeDrainer> weak_factory_;
 
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index f09c5e1..ecf1630 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -45,15 +45,29 @@
                           signals_states);
 }
 
-MojoResult MojoWatchImpl(MojoHandle handle,
-                         MojoHandleSignals signals,
-                         MojoWatchCallback callback,
-                         uintptr_t context) {
-  return g_core->Watch(handle, signals, callback, context);
+MojoResult MojoCreateWatcherImpl(MojoWatcherCallback callback,
+                                 MojoHandle* watcher_handle) {
+  return g_core->CreateWatcher(callback, watcher_handle);
 }
 
-MojoResult MojoCancelWatchImpl(MojoHandle handle, uintptr_t context) {
-  return g_core->CancelWatch(handle, context);
+MojoResult MojoArmWatcherImpl(MojoHandle watcher_handle,
+                              uint32_t* num_ready_contexts,
+                              uintptr_t* ready_contexts,
+                              MojoResult* ready_results,
+                              MojoHandleSignalsState* ready_signals_states) {
+  return g_core->ArmWatcher(watcher_handle, num_ready_contexts, ready_contexts,
+                            ready_results, ready_signals_states);
+}
+
+MojoResult MojoWatchImpl(MojoHandle watcher_handle,
+                         MojoHandle handle,
+                         MojoHandleSignals signals,
+                         uintptr_t context) {
+  return g_core->Watch(watcher_handle, handle, signals, context);
+}
+
+MojoResult MojoCancelWatchImpl(MojoHandle watcher_handle, uintptr_t context) {
+  return g_core->CancelWatch(watcher_handle, context);
 }
 
 MojoResult MojoAllocMessageImpl(uint32_t num_bytes,
@@ -287,8 +301,10 @@
                                     MojoAddHandleImpl,
                                     MojoRemoveHandleImpl,
                                     MojoGetReadyHandlesImpl,
+                                    MojoCreateWatcherImpl,
                                     MojoWatchImpl,
                                     MojoCancelWatchImpl,
+                                    MojoArmWatcherImpl,
                                     MojoFuseMessagePipesImpl,
                                     MojoWriteMessageNewImpl,
                                     MojoReadMessageNewImpl,
diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc
index cfd0bb5..334ced32 100644
--- a/mojo/edk/js/drain_data.cc
+++ b/mojo/edk/js/drain_data.cc
@@ -23,7 +23,7 @@
 DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle)
     : isolate_(isolate),
       handle_(DataPipeConsumerHandle(handle.value())),
-      handle_watcher_(FROM_HERE) {
+      handle_watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC) {
   v8::Handle<v8::Context> context(isolate_->GetCurrentContext());
   runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
 
@@ -43,7 +43,7 @@
 }
 
 void DrainData::WaitForData() {
-  handle_watcher_.Start(
+  handle_watcher_.Watch(
       handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::Bind(&DrainData::DataReady, base::Unretained(this)));
 }
diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h
index 6e8555c..42da90f 100644
--- a/mojo/edk/js/drain_data.h
+++ b/mojo/edk/js/drain_data.h
@@ -10,7 +10,7 @@
 
 #include "gin/runner.h"
 #include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "v8/include/v8.h"
 
 namespace mojo {
@@ -52,7 +52,7 @@
 
   v8::Isolate* isolate_;
   ScopedDataPipeConsumerHandle handle_;
-  Watcher handle_watcher_;
+  SimpleWatcher handle_watcher_;
   base::WeakPtr<gin::Runner> runner_;
   v8::UniquePersistent<v8::Promise::Resolver> resolver_;
   std::vector<std::unique_ptr<DataBuffer>> data_buffers_;
diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc
index fada039..6ad4bd0 100644
--- a/mojo/edk/js/waiting_callback.cc
+++ b/mojo/edk/js/waiting_callback.cc
@@ -32,7 +32,7 @@
     bool one_shot) {
   gin::Handle<WaitingCallback> waiting_callback = gin::CreateHandle(
       isolate, new WaitingCallback(isolate, callback, one_shot));
-  MojoResult result = waiting_callback->watcher_.Start(
+  MojoResult result = waiting_callback->watcher_.Watch(
       handle_wrapper->get(), signals,
       base::Bind(&WaitingCallback::OnHandleReady,
                  base::Unretained(waiting_callback.get())));
@@ -53,7 +53,7 @@
                                  v8::Handle<v8::Function> callback,
                                  bool one_shot)
     : one_shot_(one_shot),
-      watcher_(FROM_HERE),
+      watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC),
       weak_factory_(this) {
   v8::Handle<v8::Context> context = isolate->GetCurrentContext();
   runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h
index 1195a98..f97b389a 100644
--- a/mojo/edk/js/waiting_callback.h
+++ b/mojo/edk/js/waiting_callback.h
@@ -12,7 +12,7 @@
 #include "gin/wrappable.h"
 #include "mojo/edk/js/handle.h"
 #include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace mojo {
 namespace edk {
@@ -54,7 +54,7 @@
   const bool one_shot_;
 
   base::WeakPtr<gin::Runner> runner_;
-  Watcher watcher_;
+  SimpleWatcher watcher_;
   base::WeakPtrFactory<WaitingCallback> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(WaitingCallback);
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index b0acf237..3c94e153 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -66,8 +66,10 @@
     "wait_set_dispatcher.h",
     "waiter.cc",
     "waiter.h",
-    "watcher.cc",
-    "watcher.h",
+    "watch.cc",
+    "watch.h",
+    "watcher_dispatcher.cc",
+    "watcher_dispatcher.h",
     "watcher_set.cc",
     "watcher_set.h",
   ]
@@ -167,7 +169,7 @@
     "waiter_test_utils.cc",
     "waiter_test_utils.h",
     "waiter_unittest.cc",
-    "watch_unittest.cc",
+    "watcher_unittest.cc",
   ]
 
   if (!is_ios) {
diff --git a/mojo/edk/system/awakable_list.cc b/mojo/edk/system/awakable_list.cc
index 2045f32..429e691 100644
--- a/mojo/edk/system/awakable_list.cc
+++ b/mojo/edk/system/awakable_list.cc
@@ -39,7 +39,6 @@
     }
   }
   awakables_.erase(last, awakables_.end());
-  watchers_.NotifyForStateChange(state);
 }
 
 void AwakableList::CancelAll() {
@@ -48,7 +47,6 @@
     it->awakable->Awake(MOJO_RESULT_CANCELLED, it->context);
   }
   awakables_.clear();
-  watchers_.NotifyClosed();
 }
 
 void AwakableList::Add(Awakable* awakable,
@@ -72,16 +70,5 @@
   awakables_.erase(last, awakables_.end());
 }
 
-MojoResult AwakableList::AddWatcher(MojoHandleSignals signals,
-                                    const Watcher::WatchCallback& callback,
-                                    uintptr_t context,
-                                    const HandleSignalsState& current_state) {
-  return watchers_.Add(signals, callback, context, current_state);
-}
-
-MojoResult AwakableList::RemoveWatcher(uintptr_t context) {
-  return watchers_.Remove(context);
-}
-
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/awakable_list.h b/mojo/edk/system/awakable_list.h
index 355677f..34d6b06 100644
--- a/mojo/edk/system/awakable_list.h
+++ b/mojo/edk/system/awakable_list.h
@@ -12,8 +12,6 @@
 
 #include "base/macros.h"
 #include "mojo/edk/system/system_impl_export.h"
-#include "mojo/edk/system/watcher.h"
-#include "mojo/edk/system/watcher_set.h"
 #include "mojo/public/c/system/types.h"
 
 namespace mojo {
@@ -39,13 +37,6 @@
   void Add(Awakable* awakable, MojoHandleSignals signals, uintptr_t context);
   void Remove(Awakable* awakable);
 
-  // Add and remove Watchers to this AwakableList.
-  MojoResult AddWatcher(MojoHandleSignals signals,
-                        const Watcher::WatchCallback& callback,
-                        uintptr_t context,
-                        const HandleSignalsState& current_state);
-  MojoResult RemoveWatcher(uintptr_t context);
-
  private:
   struct AwakeInfo {
     AwakeInfo(Awakable* awakable, MojoHandleSignals signals, uintptr_t context)
@@ -59,10 +50,6 @@
 
   AwakeInfoList awakables_;
 
-  // TODO: Remove AwakableList and instead use WatcherSet directly in
-  // dispatchers.
-  WatcherSet watchers_;
-
   DISALLOW_COPY_AND_ASSIGN(AwakableList);
 };
 
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 1e0bf4e..cfe01fa 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -35,6 +35,7 @@
 #include "mojo/edk/system/shared_buffer_dispatcher.h"
 #include "mojo/edk/system/wait_set_dispatcher.h"
 #include "mojo/edk/system/waiter.h"
+#include "mojo/edk/system/watcher_dispatcher.h"
 
 namespace mojo {
 namespace edk {
@@ -48,15 +49,6 @@
 // pipes too; for now we just use a constant. This only affects bootstrap pipes.
 const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL;
 
-void CallWatchCallback(MojoWatchCallback callback,
-                       uintptr_t context,
-                       MojoResult result,
-                       const HandleSignalsState& signals_state,
-                       MojoWatchNotificationFlags flags) {
-  callback(context, result, static_cast<MojoHandleSignalsState>(signals_state),
-      flags);
-}
-
 MojoResult MojoPlatformHandleToScopedPlatformHandle(
     const MojoPlatformHandle* platform_handle,
     ScopedPlatformHandle* out_handle) {
@@ -428,24 +420,50 @@
   return rv;
 }
 
-MojoResult Core::Watch(MojoHandle handle,
-                       MojoHandleSignals signals,
-                       MojoWatchCallback callback,
-                       uintptr_t context) {
+MojoResult Core::CreateWatcher(MojoWatcherCallback callback,
+                               MojoHandle* watcher_handle) {
   RequestContext request_context;
-  scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
-  if (!dispatcher)
+  if (!watcher_handle)
     return MOJO_RESULT_INVALID_ARGUMENT;
-  return dispatcher->Watch(
-      signals, base::Bind(&CallWatchCallback, callback, context), context);
+  *watcher_handle = AddDispatcher(new WatcherDispatcher(callback));
+  if (*watcher_handle == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  return MOJO_RESULT_OK;
 }
 
-MojoResult Core::CancelWatch(MojoHandle handle, uintptr_t context) {
+MojoResult Core::Watch(MojoHandle watcher_handle,
+                       MojoHandle handle,
+                       MojoHandleSignals signals,
+                       uintptr_t context) {
   RequestContext request_context;
+  scoped_refptr<Dispatcher> watcher = GetDispatcher(watcher_handle);
+  if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER)
+    return MOJO_RESULT_INVALID_ARGUMENT;
   scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
   if (!dispatcher)
     return MOJO_RESULT_INVALID_ARGUMENT;
-  return dispatcher->CancelWatch(context);
+  return watcher->WatchDispatcher(dispatcher, signals, context);
+}
+
+MojoResult Core::CancelWatch(MojoHandle watcher_handle, uintptr_t context) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> watcher = GetDispatcher(watcher_handle);
+  if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watcher->CancelWatch(context);
+}
+
+MojoResult Core::ArmWatcher(MojoHandle watcher_handle,
+                            uint32_t* num_ready_contexts,
+                            uintptr_t* ready_contexts,
+                            MojoResult* ready_results,
+                            MojoHandleSignalsState* ready_signals_states) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> watcher = GetDispatcher(watcher_handle);
+  if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watcher->Arm(num_ready_contexts, ready_contexts, ready_results,
+                      ready_signals_states);
 }
 
 MojoResult Core::AllocMessage(uint32_t num_bytes,
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 1e20a87..2ef9b7f 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -27,6 +27,7 @@
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/c/system/platform_handle.h"
 #include "mojo/public/c/system/types.h"
+#include "mojo/public/c/system/watcher.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace base {
@@ -145,11 +146,18 @@
                       MojoDeadline deadline,
                       uint32_t* result_index,
                       MojoHandleSignalsState* signals_states);
-  MojoResult Watch(MojoHandle handle,
+  MojoResult CreateWatcher(MojoWatcherCallback callback,
+                           MojoHandle* watcher_handle);
+  MojoResult Watch(MojoHandle watcher_handle,
+                   MojoHandle handle,
                    MojoHandleSignals signals,
-                   MojoWatchCallback callback,
                    uintptr_t context);
-  MojoResult CancelWatch(MojoHandle handle, uintptr_t context);
+  MojoResult CancelWatch(MojoHandle watcher_handle, uintptr_t context);
+  MojoResult ArmWatcher(MojoHandle watcher_handle,
+                        uint32_t* num_ready_contexts,
+                        uintptr_t* ready_contexts,
+                        MojoResult* ready_results,
+                        MojoHandleSignalsState* ready_signals_states);
   MojoResult AllocMessage(uint32_t num_bytes,
                           const MojoHandle* handles,
                           uint32_t num_handles,
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index c908e3a..474e99d 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -80,6 +80,7 @@
       node_controller_(node_controller),
       control_port_(control_port),
       pipe_id_(pipe_id),
+      watchers_(this),
       shared_ring_buffer_(shared_ring_buffer) {
   if (initialized) {
     base::AutoLock lock(lock_);
@@ -97,34 +98,10 @@
   return CloseNoLock();
 }
 
-
-MojoResult DataPipeConsumerDispatcher::Watch(
-    MojoHandleSignals signals,
-    const Watcher::WatchCallback& callback,
-    uintptr_t context) {
-  base::AutoLock lock(lock_);
-
-  if (is_closed_ || in_transit_)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  return awakable_list_.AddWatcher(
-      signals, callback, context, GetHandleSignalsStateNoLock());
-}
-
-MojoResult DataPipeConsumerDispatcher::CancelWatch(uintptr_t context) {
-  base::AutoLock lock(lock_);
-
-  if (is_closed_ || in_transit_)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  return awakable_list_.RemoveWatcher(context);
-}
-
 MojoResult DataPipeConsumerDispatcher::ReadData(void* elements,
                                                 uint32_t* num_bytes,
                                                 MojoReadDataFlags flags) {
   base::AutoLock lock(lock_);
-  new_data_available_ = false;
 
   if (!shared_ring_buffer_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
@@ -132,6 +109,9 @@
   if (in_two_phase_read_)
     return MOJO_RESULT_BUSY;
 
+  const bool had_new_data = new_data_available_;
+  new_data_available_ = false;
+
   if ((flags & MOJO_READ_DATA_FLAG_QUERY)) {
     if ((flags & MOJO_READ_DATA_FLAG_PEEK) ||
         (flags & MOJO_READ_DATA_FLAG_DISCARD))
@@ -140,6 +120,9 @@
     DVLOG_IF(2, elements)
         << "Query mode: ignoring non-null |elements|";
     *num_bytes = static_cast<uint32_t>(bytes_available_);
+
+    if (had_new_data)
+      watchers_.NotifyState(GetHandleSignalsStateNoLock());
     return MOJO_RESULT_OK;
   }
 
@@ -162,12 +145,16 @@
       all_or_none ? max_num_bytes_to_read : 0;
 
   if (min_num_bytes_to_read > bytes_available_) {
+    if (had_new_data)
+      watchers_.NotifyState(GetHandleSignalsStateNoLock());
     return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
                         : MOJO_RESULT_OUT_OF_RANGE;
   }
 
   uint32_t bytes_to_read = std::min(max_num_bytes_to_read, bytes_available_);
   if (bytes_to_read == 0) {
+    if (had_new_data)
+      watchers_.NotifyState(GetHandleSignalsStateNoLock());
     return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
                         : MOJO_RESULT_SHOULD_WAIT;
   }
@@ -199,6 +186,10 @@
     NotifyRead(bytes_to_read);
   }
 
+  // We may have just read the last available data and thus changed the signals
+  // state.
+  watchers_.NotifyState(GetHandleSignalsStateNoLock());
+
   return MOJO_RESULT_OK;
 }
 
@@ -206,7 +197,6 @@
                                                      uint32_t* buffer_num_bytes,
                                                      MojoReadDataFlags flags) {
   base::AutoLock lock(lock_);
-  new_data_available_ = false;
   if (!shared_ring_buffer_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
@@ -219,7 +209,12 @@
       (flags & MOJO_READ_DATA_FLAG_PEEK))
     return MOJO_RESULT_INVALID_ARGUMENT;
 
+  const bool had_new_data = new_data_available_;
+  new_data_available_ = false;
+
   if (bytes_available_ == 0) {
+    if (had_new_data)
+      watchers_.NotifyState(GetHandleSignalsStateNoLock());
     return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
                         : MOJO_RESULT_SHOULD_WAIT;
   }
@@ -237,6 +232,9 @@
   *buffer_num_bytes = bytes_to_read;
   two_phase_max_bytes_read_ = bytes_to_read;
 
+  if (had_new_data)
+    watchers_.NotifyState(GetHandleSignalsStateNoLock());
+
   return MOJO_RESULT_OK;
 }
 
@@ -273,6 +271,7 @@
   HandleSignalsState new_state = GetHandleSignalsStateNoLock();
   if (!new_state.equals(old_state))
     awakable_list_.AwakeForStateChange(new_state);
+  watchers_.NotifyState(new_state);
 
   return rv;
 }
@@ -282,6 +281,24 @@
   return GetHandleSignalsStateNoLock();
 }
 
+MojoResult DataPipeConsumerDispatcher::AddWatcherRef(
+    const scoped_refptr<WatcherDispatcher>& watcher,
+    uintptr_t context) {
+  base::AutoLock lock(lock_);
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watchers_.Add(watcher, context, GetHandleSignalsStateNoLock());
+}
+
+MojoResult DataPipeConsumerDispatcher::RemoveWatcherRef(
+    WatcherDispatcher* watcher,
+    uintptr_t context) {
+  base::AutoLock lock(lock_);
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watchers_.Remove(watcher, context);
+}
+
 MojoResult DataPipeConsumerDispatcher::AddAwakable(
     Awakable* awakable,
     MojoHandleSignals signals,
@@ -464,6 +481,7 @@
   shared_ring_buffer_ = nullptr;
 
   awakable_list_.CancelAll();
+  watchers_.NotifyClosed();
   if (!transferred_) {
     base::AutoUnlock unlock(lock_);
     node_controller_->ClosePort(control_port_);
@@ -581,7 +599,9 @@
     new_data_available_ = true;
 
   if (peer_closed_ != was_peer_closed || has_new_data) {
-    awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+    HandleSignalsState state = GetHandleSignalsStateNoLock();
+    awakable_list_.AwakeForStateChange(state);
+    watchers_.NotifyState(state);
   }
 }
 
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index b323c16..26ceadf 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -20,6 +20,7 @@
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/ports/port_ref.h"
 #include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/watcher_set.h"
 
 namespace mojo {
 namespace edk {
@@ -43,10 +44,6 @@
   // Dispatcher:
   Type GetType() const override;
   MojoResult Close() override;
-  MojoResult Watch(MojoHandleSignals signals,
-                   const Watcher::WatchCallback& callback,
-                   uintptr_t context) override;
-  MojoResult CancelWatch(uintptr_t context) override;
   MojoResult ReadData(void* elements,
                       uint32_t* num_bytes,
                       MojoReadDataFlags flags) override;
@@ -55,6 +52,10 @@
                            MojoReadDataFlags flags) override;
   MojoResult EndReadData(uint32_t num_bytes_read) override;
   HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher,
+                           uintptr_t context) override;
+  MojoResult RemoveWatcherRef(WatcherDispatcher* watcher,
+                              uintptr_t context) override;
   MojoResult AddAwakable(Awakable* awakable,
                          MojoHandleSignals signals,
                          uintptr_t context,
@@ -101,6 +102,7 @@
   mutable base::Lock lock_;
 
   AwakableList awakable_list_;
+  WatcherSet watchers_;
 
   scoped_refptr<PlatformSharedBuffer> shared_ring_buffer_;
   std::unique_ptr<PlatformSharedBufferMapping> ring_buffer_mapping_;
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index 8c1993aa..71c61963e 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -79,6 +79,7 @@
       node_controller_(node_controller),
       control_port_(control_port),
       pipe_id_(pipe_id),
+      watchers_(this),
       shared_ring_buffer_(shared_ring_buffer),
       available_capacity_(options_.capacity_num_bytes) {
   if (initialized) {
@@ -97,28 +98,6 @@
   return CloseNoLock();
 }
 
-MojoResult DataPipeProducerDispatcher::Watch(
-    MojoHandleSignals signals,
-    const Watcher::WatchCallback& callback,
-    uintptr_t context) {
-  base::AutoLock lock(lock_);
-
-  if (is_closed_ || in_transit_)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  return awakable_list_.AddWatcher(
-      signals, callback, context, GetHandleSignalsStateNoLock());
-}
-
-MojoResult DataPipeProducerDispatcher::CancelWatch(uintptr_t context) {
-  base::AutoLock lock(lock_);
-
-  if (is_closed_ || in_transit_)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  return awakable_list_.RemoveWatcher(context);
-}
-
 MojoResult DataPipeProducerDispatcher::WriteData(const void* elements,
                                                  uint32_t* num_bytes,
                                                  MojoWriteDataFlags flags) {
@@ -179,6 +158,7 @@
   HandleSignalsState new_state = GetHandleSignalsStateNoLock();
   if (!new_state.equals(old_state))
     awakable_list_.AwakeForStateChange(new_state);
+  watchers_.NotifyState(new_state);
 
   base::AutoUnlock unlock(lock_);
   NotifyWrite(num_bytes_to_write);
@@ -193,6 +173,11 @@
   base::AutoLock lock(lock_);
   if (!shared_ring_buffer_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
+
+  // These flags may not be used in two-phase mode.
+  if (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
   if (in_two_phase_write_)
     return MOJO_RESULT_BUSY;
   if (peer_closed_)
@@ -251,6 +236,7 @@
   HandleSignalsState new_state = GetHandleSignalsStateNoLock();
   if (new_state.satisfies(MOJO_HANDLE_SIGNAL_WRITABLE))
     awakable_list_.AwakeForStateChange(new_state);
+  watchers_.NotifyState(new_state);
 
   return rv;
 }
@@ -260,6 +246,24 @@
   return GetHandleSignalsStateNoLock();
 }
 
+MojoResult DataPipeProducerDispatcher::AddWatcherRef(
+    const scoped_refptr<WatcherDispatcher>& watcher,
+    uintptr_t context) {
+  base::AutoLock lock(lock_);
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watchers_.Add(watcher, context, GetHandleSignalsStateNoLock());
+}
+
+MojoResult DataPipeProducerDispatcher::RemoveWatcherRef(
+    WatcherDispatcher* watcher,
+    uintptr_t context) {
+  base::AutoLock lock(lock_);
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watchers_.Remove(watcher, context);
+}
+
 MojoResult DataPipeProducerDispatcher::AddAwakable(
     Awakable* awakable,
     MojoHandleSignals signals,
@@ -356,7 +360,10 @@
   DCHECK(in_transit_);
   in_transit_ = false;
   buffer_handle_for_transit_.reset();
-  awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+
+  HandleSignalsState state = GetHandleSignalsStateNoLock();
+  awakable_list_.AwakeForStateChange(state);
+  watchers_.NotifyState(state);
 }
 
 // static
@@ -440,6 +447,7 @@
   shared_ring_buffer_ = nullptr;
 
   awakable_list_.CancelAll();
+  watchers_.NotifyClosed();
   if (!transferred_) {
     base::AutoUnlock unlock(lock_);
     node_controller_->ClosePort(control_port_);
@@ -453,8 +461,7 @@
   lock_.AssertAcquired();
   HandleSignalsState rv;
   if (!peer_closed_) {
-    if (!in_two_phase_write_ && shared_ring_buffer_ &&
-        available_capacity_ > 0)
+    if (!in_two_phase_write_ && shared_ring_buffer_ && available_capacity_ > 0)
       rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
     rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
   } else {
@@ -541,7 +548,9 @@
 
   if (peer_closed_ != was_peer_closed ||
       available_capacity_ != previous_capacity) {
-    awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+    HandleSignalsState state = GetHandleSignalsStateNoLock();
+    awakable_list_.AwakeForStateChange(state);
+    watchers_.NotifyState(state);
   }
 }
 
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h
index a55234a..e472994 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.h
@@ -19,6 +19,7 @@
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/ports/port_ref.h"
 #include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/watcher_set.h"
 
 namespace mojo {
 namespace edk {
@@ -43,10 +44,6 @@
   // Dispatcher:
   Type GetType() const override;
   MojoResult Close() override;
-  MojoResult Watch(MojoHandleSignals signals,
-                   const Watcher::WatchCallback& callback,
-                   uintptr_t context) override;
-  MojoResult CancelWatch(uintptr_t context) override;
   MojoResult WriteData(const void* elements,
                        uint32_t* num_bytes,
                        MojoReadDataFlags flags) override;
@@ -55,6 +52,10 @@
                             MojoWriteDataFlags flags) override;
   MojoResult EndWriteData(uint32_t num_bytes_written) override;
   HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher,
+                           uintptr_t context) override;
+  MojoResult RemoveWatcherRef(WatcherDispatcher* watcher,
+                              uintptr_t context) override;
   MojoResult AddAwakable(Awakable* awakable,
                          MojoHandleSignals signals,
                          uintptr_t context,
@@ -104,6 +105,7 @@
   mutable base::Lock lock_;
 
   AwakableList awakable_list_;
+  WatcherSet watchers_;
 
   bool buffer_requested_ = false;
 
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
index 610aeac..11474962 100644
--- a/mojo/edk/system/data_pipe_unittest.cc
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -21,7 +21,7 @@
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/message_pipe.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace mojo {
@@ -2029,7 +2029,7 @@
 
   base::MessageLoop message_loop;
 
-  // Wait on producer 1 and consumer 1 using Watchers.
+  // Wait on producer 1 and consumer 1 using SimpleWatchers.
   {
     base::RunLoop run_loop;
     int count = 0;
@@ -2040,12 +2040,16 @@
             loop->Quit();
         },
         &run_loop, &count);
-    Watcher producer_watcher(FROM_HERE), consumer_watcher(FROM_HERE);
-    producer_watcher.Start(
-        Handle(producers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, callback);
-    consumer_watcher.Start(
-        Handle(consumers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, callback);
+    SimpleWatcher producer_watcher(FROM_HERE,
+                                   SimpleWatcher::ArmingPolicy::AUTOMATIC);
+    SimpleWatcher consumer_watcher(FROM_HERE,
+                                   SimpleWatcher::ArmingPolicy::AUTOMATIC);
+    producer_watcher.Watch(Handle(producers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                           callback);
+    consumer_watcher.Watch(Handle(consumers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                           callback);
     run_loop.Run();
+    EXPECT_EQ(2, count);
   }
 
   // Wait on producer 2 by polling with MojoWriteData.
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
index 7d701b2..97e87e068 100644
--- a/mojo/edk/system/dispatcher.cc
+++ b/mojo/edk/system/dispatcher.cc
@@ -22,9 +22,9 @@
 
 Dispatcher::DispatcherInTransit::~DispatcherInTransit() {}
 
-MojoResult Dispatcher::Watch(MojoHandleSignals signals,
-                             const Watcher::WatchCallback& callback,
-                             uintptr_t context) {
+MojoResult Dispatcher::WatchDispatcher(scoped_refptr<Dispatcher> dispatcher,
+                                       MojoHandleSignals signals,
+                                       uintptr_t context) {
   return MOJO_RESULT_INVALID_ARGUMENT;
 }
 
@@ -32,6 +32,13 @@
   return MOJO_RESULT_INVALID_ARGUMENT;
 }
 
+MojoResult Dispatcher::Arm(uint32_t* num_ready_contexts,
+                           uintptr_t* ready_contexts,
+                           MojoResult* ready_results,
+                           MojoHandleSignalsState* ready_signals_states) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
 MojoResult Dispatcher::WriteMessage(std::unique_ptr<MessageForTransit> message,
                                     MojoWriteMessageFlags flags) {
   return MOJO_RESULT_INVALID_ARGUMENT;
@@ -115,6 +122,17 @@
   return HandleSignalsState();
 }
 
+MojoResult Dispatcher::AddWatcherRef(
+    const scoped_refptr<WatcherDispatcher>& watcher,
+    uintptr_t context) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::RemoveWatcherRef(WatcherDispatcher* watcher,
+                                        uintptr_t context) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
 MojoResult Dispatcher::AddAwakable(Awakable* awakable,
                                    MojoHandleSignals signals,
                                    uintptr_t context,
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
index 9dca67f..88a9de7 100644
--- a/mojo/edk/system/dispatcher.h
+++ b/mojo/edk/system/dispatcher.h
@@ -20,7 +20,7 @@
 #include "mojo/edk/system/handle_signals_state.h"
 #include "mojo/edk/system/ports/name.h"
 #include "mojo/edk/system/system_impl_export.h"
-#include "mojo/edk/system/watcher.h"
+#include "mojo/edk/system/watch.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/message_pipe.h"
@@ -57,6 +57,7 @@
     DATA_PIPE_CONSUMER,
     SHARED_BUFFER,
     WAIT_SET,
+    WATCHER,
 
     // "Private" types (not exposed via the public interface):
     PLATFORM_HANDLE = -1,
@@ -67,13 +68,16 @@
   virtual Type GetType() const = 0;
   virtual MojoResult Close() = 0;
 
-  ///////////// Watch API ////////////////////
+  ///////////// Watcher API ////////////////////
 
-  virtual MojoResult Watch(MojoHandleSignals signals,
-                           const Watcher::WatchCallback& callback,
-                           uintptr_t context);
-
+  virtual MojoResult WatchDispatcher(scoped_refptr<Dispatcher> dispatcher,
+                                     MojoHandleSignals signals,
+                                     uintptr_t context);
   virtual MojoResult CancelWatch(uintptr_t context);
+  virtual MojoResult Arm(uint32_t* num_ready_contexts,
+                         uintptr_t* ready_contexts,
+                         MojoResult* ready_results,
+                         MojoHandleSignalsState* ready_signals_states);
 
   ///////////// Message pipe API /////////////
 
@@ -158,6 +162,18 @@
   // threads.
   virtual HandleSignalsState GetHandleSignalsState() const;
 
+  // Adds a WatcherDispatcher reference to this dispatcher, to be notified of
+  // all subsequent changes to handle state including signal changes or closure.
+  // The reference is associated with a |context| for disambiguation of
+  // removals.
+  virtual MojoResult AddWatcherRef(
+      const scoped_refptr<WatcherDispatcher>& watcher,
+      uintptr_t context);
+
+  // Removes a WatcherDispatcher reference from this dispatcher.
+  virtual MojoResult RemoveWatcherRef(WatcherDispatcher* watcher,
+                                      uintptr_t context);
+
   // Adds an awakable to this dispatcher, which will be woken up when this
   // object changes state to satisfy |signals| with context |context|. It will
   // also be woken up when it becomes impossible for the object to ever satisfy
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index f27336b..76f4123 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -164,7 +164,8 @@
     : node_controller_(node_controller),
       port_(port),
       pipe_id_(pipe_id),
-      endpoint_(endpoint) {
+      endpoint_(endpoint),
+      watchers_(this) {
   DVLOG(2) << "Creating new MessagePipeDispatcher for port " << port.name()
            << " [pipe_id=" << pipe_id << "; endpoint=" << endpoint << "]";
 
@@ -183,6 +184,7 @@
     port0 = port_;
     port_closed_.Set(true);
     awakables_.CancelAll();
+    watchers_.NotifyClosed();
   }
 
   ports::PortRef port1;
@@ -191,6 +193,7 @@
     port1 = other->port_;
     other->port_closed_.Set(true);
     other->awakables_.CancelAll();
+    other->watchers_.NotifyClosed();
   }
 
   // Both ports are always closed by this call.
@@ -209,27 +212,6 @@
   return CloseNoLock();
 }
 
-MojoResult MessagePipeDispatcher::Watch(MojoHandleSignals signals,
-                                        const Watcher::WatchCallback& callback,
-                                        uintptr_t context) {
-  base::AutoLock lock(signal_lock_);
-
-  if (port_closed_ || in_transit_)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  return awakables_.AddWatcher(
-      signals, callback, context, GetHandleSignalsStateNoLock());
-}
-
-MojoResult MessagePipeDispatcher::CancelWatch(uintptr_t context) {
-  base::AutoLock lock(signal_lock_);
-
-  if (port_closed_ || in_transit_)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  return awakables_.RemoveWatcher(context);
-}
-
 MojoResult MessagePipeDispatcher::WriteMessage(
     std::unique_ptr<MessageForTransit> message,
     MojoWriteMessageFlags flags) {
@@ -299,6 +281,12 @@
   }
 
   if (no_space) {
+    if (may_discard) {
+      // May have been the last message on the pipe. Need to update signals just
+      // in case.
+      base::AutoLock lock(signal_lock_);
+      watchers_.NotifyState(GetHandleSignalsStateNoLock());
+    }
     // |*num_handles| (and/or |*num_bytes| if |read_any_size| is false) wasn't
     // sufficient to hold this message's data. The message will still be in
     // queue unless MOJO_READ_MESSAGE_FLAG_MAY_DISCARD was set.
@@ -319,6 +307,13 @@
   // Alright! We have a message and the caller has provided sufficient storage
   // in which to receive it.
 
+  {
+    // We need to update anyone watching our signals in case that was the last
+    // available message.
+    base::AutoLock lock(signal_lock_);
+    watchers_.NotifyState(GetHandleSignalsStateNoLock());
+  }
+
   std::unique_ptr<PortsMessage> msg(
       static_cast<PortsMessage*>(ports_message.release()));
 
@@ -396,6 +391,23 @@
   return GetHandleSignalsStateNoLock();
 }
 
+MojoResult MessagePipeDispatcher::AddWatcherRef(
+    const scoped_refptr<WatcherDispatcher>& watcher,
+    uintptr_t context) {
+  base::AutoLock lock(signal_lock_);
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watchers_.Add(watcher, context, GetHandleSignalsStateNoLock());
+}
+
+MojoResult MessagePipeDispatcher::RemoveWatcherRef(WatcherDispatcher* watcher,
+                                                   uintptr_t context) {
+  base::AutoLock lock(signal_lock_);
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return watchers_.Remove(watcher, context);
+}
+
 MojoResult MessagePipeDispatcher::AddAwakable(
     Awakable* awakable,
     MojoHandleSignals signals,
@@ -496,7 +508,9 @@
   in_transit_.Set(false);
 
   // Something may have happened while we were waiting for potential transit.
-  awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+  HandleSignalsState state = GetHandleSignalsStateNoLock();
+  awakables_.AwakeForStateChange(state);
+  watchers_.NotifyState(state);
 }
 
 // static
@@ -532,6 +546,7 @@
 
   port_closed_.Set(true);
   awakables_.CancelAll();
+  watchers_.NotifyClosed();
 
   if (!port_transferred_) {
     base::AutoUnlock unlock(signal_lock_);
@@ -596,7 +611,9 @@
   }
 #endif
 
-  awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+  HandleSignalsState state = GetHandleSignalsStateNoLock();
+  awakables_.AwakeForStateChange(state);
+  watchers_.NotifyState(state);
 }
 
 }  // namespace edk
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 6743222b..a7c1e69 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -16,6 +16,7 @@
 #include "mojo/edk/system/dispatcher.h"
 #include "mojo/edk/system/message_for_transit.h"
 #include "mojo/edk/system/ports/port_ref.h"
+#include "mojo/edk/system/watcher_set.h"
 
 namespace mojo {
 namespace edk {
@@ -48,10 +49,6 @@
   // Dispatcher:
   Type GetType() const override;
   MojoResult Close() override;
-  MojoResult Watch(MojoHandleSignals signals,
-                   const Watcher::WatchCallback& callback,
-                   uintptr_t context) override;
-  MojoResult CancelWatch(uintptr_t context) override;
   MojoResult WriteMessage(std::unique_ptr<MessageForTransit> message,
                           MojoWriteMessageFlags flags) override;
   MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message,
@@ -61,6 +58,10 @@
                          MojoReadMessageFlags flags,
                          bool read_any_size) override;
   HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher,
+                           uintptr_t context) override;
+  MojoResult RemoveWatcherRef(WatcherDispatcher* watcher,
+                              uintptr_t context) override;
   MojoResult AddAwakable(Awakable* awakable,
                          MojoHandleSignals signals,
                          uintptr_t context,
@@ -111,6 +112,7 @@
   bool port_transferred_ = false;
   AtomicFlag port_closed_;
   AwakableList awakables_;
+  WatcherSet watchers_;
 
   DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher);
 };
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index 498980c..f8e29d63 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -31,7 +31,7 @@
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 
@@ -1271,15 +1271,17 @@
 
   base::MessageLoop message_loop;
 
-  // Wait on handle 1 using a Watcher.
+  // Wait on handle 1 using a SimpleWatcher.
   {
     base::RunLoop run_loop;
-    Watcher watcher(FROM_HERE);
-    watcher.Start(Handle(handles[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                  base::Bind([] (base::RunLoop* loop, MojoResult result) {
-                    EXPECT_EQ(MOJO_RESULT_OK, result);
-                    loop->Quit();
-                  }, &run_loop));
+    SimpleWatcher watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+    watcher.Watch(Handle(handles[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                  base::Bind(
+                      [](base::RunLoop* loop, MojoResult result) {
+                        EXPECT_EQ(MOJO_RESULT_OK, result);
+                        loop->Quit();
+                      },
+                      &run_loop));
     run_loop.Run();
   }
 
diff --git a/mojo/edk/system/request_context.cc b/mojo/edk/system/request_context.cc
index 6370ab1..5de65d7 100644
--- a/mojo/edk/system/request_context.cc
+++ b/mojo/edk/system/request_context.cc
@@ -36,27 +36,34 @@
     // since we're starting over at the bottom of the stack.
     tls_context_->Set(nullptr);
 
-    MojoWatchNotificationFlags flags = MOJO_WATCH_NOTIFICATION_FLAG_NONE;
+    MojoWatcherNotificationFlags flags = MOJO_WATCHER_NOTIFICATION_FLAG_NONE;
     if (source_ == Source::SYSTEM)
-      flags |= MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM;
+      flags |= MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM;
 
-    // We run all cancellation finalizers first. This is necessary because it's
-    // possible that one of the cancelled watchers has other pending finalizers
+    // We send all cancellation notifications first. This is necessary because
+    // it's possible that cancelled watches have other pending notifications
     // attached to this RequestContext.
     //
-    // From the application's perspective the watch has already been cancelled,
-    // so we have to honor our contract which guarantees no more notifications.
-    for (const scoped_refptr<Watcher>& watcher :
-            watch_cancel_finalizers_.container())
-      watcher->Cancel();
+    // From the application's perspective the watch is cancelled as soon as this
+    // notification is received, and dispatching the cancellation notification
+    // updates some internal Watch state to ensure no further notifications
+    // fire. Because notifications on a single Watch are mutually exclusive,
+    // this is sufficient to guarantee that MOJO_RESULT_CANCELLED is the last
+    // notification received; which is the guarantee the API makes.
+    for (const scoped_refptr<Watch>& watch :
+         watch_cancel_finalizers_.container()) {
+      static const HandleSignalsState closed_state = {0, 0};
+
+      // Establish a new RequestContext to capture and run any new notifications
+      // triggered by the callback invocation.
+      RequestContext inner_context(source_);
+      watch->InvokeCallback(MOJO_RESULT_CANCELLED, closed_state, flags);
+    }
 
     for (const WatchNotifyFinalizer& watch :
-        watch_notify_finalizers_.container()) {
-      // Establish a new request context for the extent of each callback to
-      // ensure that they don't themselves invoke callbacks while holding a
-      // watcher lock.
-      RequestContext request_context(source_);
-      watch.watcher->MaybeInvokeCallback(watch.result, watch.state, flags);
+         watch_notify_finalizers_.container()) {
+      RequestContext inner_context(source_);
+      watch.watch->InvokeCallback(watch.result, watch.state, flags);
     }
   } else {
     // It should be impossible for nested contexts to have finalizers.
@@ -71,18 +78,17 @@
   return g_current_context.Pointer()->Get();
 }
 
-void RequestContext::AddWatchNotifyFinalizer(
-    scoped_refptr<Watcher> watcher,
-    MojoResult result,
-    const HandleSignalsState& state) {
+void RequestContext::AddWatchNotifyFinalizer(scoped_refptr<Watch> watch,
+                                             MojoResult result,
+                                             const HandleSignalsState& state) {
   DCHECK(IsCurrent());
   watch_notify_finalizers_->push_back(
-      WatchNotifyFinalizer(std::move(watcher), result, state));
+      WatchNotifyFinalizer(std::move(watch), result, state));
 }
 
-void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) {
+void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watch> watch) {
   DCHECK(IsCurrent());
-  watch_cancel_finalizers_->push_back(std::move(watcher));
+  watch_cancel_finalizers_->push_back(std::move(watch));
 }
 
 bool RequestContext::IsCurrent() const {
@@ -90,10 +96,10 @@
 }
 
 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
-    scoped_refptr<Watcher> watcher,
+    scoped_refptr<Watch> watch,
     MojoResult result,
     const HandleSignalsState& state)
-    : watcher(std::move(watcher)), result(result), state(state) {}
+    : watch(std::move(watch)), result(result), state(state) {}
 
 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
     const WatchNotifyFinalizer& other) = default;
diff --git a/mojo/edk/system/request_context.h b/mojo/edk/system/request_context.h
index 7aa0e69..d1f43bd 100644
--- a/mojo/edk/system/request_context.h
+++ b/mojo/edk/system/request_context.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "mojo/edk/system/handle_signals_state.h"
 #include "mojo/edk/system/system_impl_export.h"
-#include "mojo/edk/system/watcher.h"
+#include "mojo/edk/system/watch.h"
 
 namespace base {
 template<typename T> class ThreadLocalPointer;
@@ -49,43 +49,44 @@
 
   // Adds a finalizer to this RequestContext corresponding to a watch callback
   // which should be triggered in response to some handle state change. If
-  // the Watcher hasn't been cancelled by the time this RequestContext is
+  // the WatcherDispatcher hasn't been closed by the time this RequestContext is
   // destroyed, its WatchCallback will be invoked with |result| and |state|
   // arguments.
-  void AddWatchNotifyFinalizer(scoped_refptr<Watcher> watcher,
+  void AddWatchNotifyFinalizer(scoped_refptr<Watch> watch,
                                MojoResult result,
                                const HandleSignalsState& state);
 
-  // Adds a finalizer to this RequestContext which cancels a watch.
-  void AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher);
+  // Adds a finalizer to this RequestContext corresponding to a watch callback
+  // which should be triggered to notify of watch cancellation. This appends to
+  // a separate finalizer list from AddWatchNotifyFinalizer, as pending
+  // cancellations must always preempt other pending notifications.
+  void AddWatchCancelFinalizer(scoped_refptr<Watch> watch);
 
  private:
   // Is this request context the current one?
   bool IsCurrent() const;
 
   struct WatchNotifyFinalizer {
-    WatchNotifyFinalizer(scoped_refptr<Watcher> watcher,
+    WatchNotifyFinalizer(scoped_refptr<Watch> watch,
                          MojoResult result,
                          const HandleSignalsState& state);
     WatchNotifyFinalizer(const WatchNotifyFinalizer& other);
     ~WatchNotifyFinalizer();
 
-    scoped_refptr<Watcher> watcher;
+    scoped_refptr<Watch> watch;
     MojoResult result;
     HandleSignalsState state;
   };
 
-  // Chosen by fair dice roll.
-  //
-  // TODO: We should measure the distribution of # of finalizers typical to
-  // any RequestContext and adjust this number accordingly. It's probably
-  // almost always 1, but 4 seems like a harmless upper bound for now.
-  static const size_t kStaticWatchFinalizersCapacity = 4;
+  // NOTE: This upper bound was chosen somewhat arbitrarily after observing some
+  // rare worst-case behavior in Chrome. A vast majority of RequestContexts only
+  // ever accumulate 0 or 1 finalizers.
+  static const size_t kStaticWatchFinalizersCapacity = 8;
 
   using WatchNotifyFinalizerList =
       base::StackVector<WatchNotifyFinalizer, kStaticWatchFinalizersCapacity>;
   using WatchCancelFinalizerList =
-      base::StackVector<scoped_refptr<Watcher>, kStaticWatchFinalizersCapacity>;
+      base::StackVector<scoped_refptr<Watch>, kStaticWatchFinalizersCapacity>;
 
   const Source source_;
 
diff --git a/mojo/edk/system/watch.cc b/mojo/edk/system/watch.cc
new file mode 100644
index 0000000..cf08ac3
--- /dev/null
+++ b/mojo/edk/system/watch.cc
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/watch.h"
+
+#include "mojo/edk/system/request_context.h"
+#include "mojo/edk/system/watcher_dispatcher.h"
+
+namespace mojo {
+namespace edk {
+
+Watch::Watch(const scoped_refptr<WatcherDispatcher>& watcher,
+             const scoped_refptr<Dispatcher>& dispatcher,
+             uintptr_t context,
+             MojoHandleSignals signals)
+    : watcher_(watcher),
+      dispatcher_(dispatcher),
+      context_(context),
+      signals_(signals) {}
+
+bool Watch::NotifyState(const HandleSignalsState& state,
+                        bool allowed_to_call_callback) {
+  AssertWatcherLockAcquired();
+
+  // NOTE: This method must NEVER call into |dispatcher_| directly, because it
+  // may be called while |dispatcher_| holds a lock.
+
+  MojoResult rv = MOJO_RESULT_SHOULD_WAIT;
+  RequestContext* const request_context = RequestContext::current();
+  if (state.satisfies(signals_)) {
+    rv = MOJO_RESULT_OK;
+    if (allowed_to_call_callback && rv != last_known_result_) {
+      request_context->AddWatchNotifyFinalizer(this, MOJO_RESULT_OK, state);
+    }
+  } else if (!state.can_satisfy(signals_)) {
+    rv = MOJO_RESULT_FAILED_PRECONDITION;
+    if (allowed_to_call_callback && rv != last_known_result_) {
+      request_context->AddWatchNotifyFinalizer(
+          this, MOJO_RESULT_FAILED_PRECONDITION, state);
+    }
+  }
+
+  last_known_signals_state_ =
+      *static_cast<const MojoHandleSignalsState*>(&state);
+  last_known_result_ = rv;
+  return ready();
+}
+
+void Watch::Cancel() {
+  RequestContext::current()->AddWatchCancelFinalizer(this);
+}
+
+void Watch::InvokeCallback(MojoResult result,
+                           const HandleSignalsState& state,
+                           MojoWatcherNotificationFlags flags) {
+  // We hold the lock through invocation to ensure that only one notification
+  // callback runs for this context at any given time.
+  base::AutoLock lock(notification_lock_);
+  if (result == MOJO_RESULT_CANCELLED) {
+    // Make sure cancellation is the last notification we dispatch.
+    DCHECK(!is_cancelled_);
+    is_cancelled_ = true;
+  } else if (is_cancelled_) {
+    return;
+  }
+
+  // NOTE: This will acquire |watcher_|'s internal lock. It's safe because a
+  // thread can only enter InvokeCallback() from within a RequestContext
+  // destructor where no dispatcher locks are held.
+  watcher_->InvokeWatchCallback(context_, result, state, flags);
+}
+
+Watch::~Watch() {}
+
+#if DCHECK_IS_ON()
+void Watch::AssertWatcherLockAcquired() const {
+  watcher_->lock_.AssertAcquired();
+}
+#endif
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/watch.h b/mojo/edk/system/watch.h
new file mode 100644
index 0000000..f277de9
--- /dev/null
+++ b/mojo/edk/system/watch.h
@@ -0,0 +1,124 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_SYSTEM_WATCH_H_
+#define MOJO_EDK_SYSTEM_WATCH_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/atomic_flag.h"
+#include "mojo/edk/system/handle_signals_state.h"
+
+namespace mojo {
+namespace edk {
+
+class Dispatcher;
+class WatcherDispatcher;
+
+// Encapsulates the state associated with a single watch context within a
+// watcher.
+//
+// Every Watch has its own cancellation state, and is captured by RequestContext
+// notification finalizers to avoid redundant context resolution during
+// finalizer execution.
+class Watch : public base::RefCountedThreadSafe<Watch> {
+ public:
+  // Constructs a Watch which represents a watch within |watcher| associated
+  // with |context|, watching |dispatcher| for |signals|.
+  Watch(const scoped_refptr<WatcherDispatcher>& watcher,
+        const scoped_refptr<Dispatcher>& dispatcher,
+        uintptr_t context,
+        MojoHandleSignals signals);
+
+  // Notifies the Watch of a potential state change.
+  //
+  // If |allowed_to_call_callback| is true, this may add a notification
+  // finalizer to the current RequestContext to invoke the watcher's callback
+  // with this watch's context. See return values below.
+  //
+  // This is called directly by WatcherDispatcher whenever the Watch's observed
+  // dispatcher notifies the WatcherDispatcher of a state change.
+  //
+  // Returns |true| if the Watch entered or remains in a ready state as a result
+  // of the state change. If |allowed_to_call_callback| was true in this case,
+  // the Watch will have also attached a notification finalizer to the current
+  // RequestContext.
+  //
+  // Returns |false| if the
+  bool NotifyState(const HandleSignalsState& state,
+                   bool allowed_to_call_callback);
+
+  // Notifies the watch of cancellation ASAP. This will always be the last
+  // notification sent for the watch.
+  void Cancel();
+
+  // Finalizer method for RequestContexts. This method is invoked once for every
+  // notification finalizer added to a RequestContext by this object. This calls
+  // down into the WatcherDispatcher to do the actual notification call.
+  void InvokeCallback(MojoResult result,
+                      const HandleSignalsState& state,
+                      MojoWatcherNotificationFlags flags);
+
+  const scoped_refptr<Dispatcher>& dispatcher() const { return dispatcher_; }
+  uintptr_t context() const { return context_; }
+
+  MojoResult last_known_result() const {
+    AssertWatcherLockAcquired();
+    return last_known_result_;
+  }
+
+  MojoHandleSignalsState last_known_signals_state() const {
+    AssertWatcherLockAcquired();
+    return last_known_signals_state_;
+  }
+
+  bool ready() const {
+    AssertWatcherLockAcquired();
+    return last_known_result_ == MOJO_RESULT_OK ||
+           last_known_result_ == MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<Watch>;
+
+  ~Watch();
+
+#if DCHECK_IS_ON()
+  void AssertWatcherLockAcquired() const;
+#else
+  void AssertWatcherLockAcquired() const {}
+#endif
+
+  const scoped_refptr<WatcherDispatcher> watcher_;
+  const scoped_refptr<Dispatcher> dispatcher_;
+  const uintptr_t context_;
+  const MojoHandleSignals signals_;
+
+  // The result code with which this Watch would notify if currently armed,
+  // based on the last known signaling state of |dispatcher_|. Guarded by the
+  // owning WatcherDispatcher's lock.
+  MojoResult last_known_result_ = MOJO_RESULT_UNKNOWN;
+
+  // The last known signaling state of |dispatcher_|. Guarded by the owning
+  // WatcherDispatcher's lock.
+  MojoHandleSignalsState last_known_signals_state_ = {0, 0};
+
+  // Guards |is_cancelled_| below and mutually excludes individual watch
+  // notification executions for this same watch context.
+  //
+  // Note that this should only be acquired from a RequestContext finalizer to
+  // ensure that no other internal locks are already held.
+  base::Lock notification_lock_;
+
+  // Guarded by |notification_lock_|.
+  bool is_cancelled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(Watch);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WATCH_H_
diff --git a/mojo/edk/system/watch_unittest.cc b/mojo/edk/system/watch_unittest.cc
deleted file mode 100644
index ec28d94..0000000
--- a/mojo/edk/system/watch_unittest.cc
+++ /dev/null
@@ -1,480 +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.
-
-#include <functional>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/edk/system/request_context.h"
-#include "mojo/edk/test/mojo_test_base.h"
-#include "mojo/public/c/system/functions.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace edk {
-namespace {
-
-void IgnoreResult(uintptr_t context,
-                  MojoResult result,
-                  MojoHandleSignalsState signals,
-                  MojoWatchNotificationFlags flags) {
-}
-
-// A test helper class for watching a handle. The WatchHelper instance is used
-// as a watch context for a single watch callback.
-class WatchHelper {
- public:
-  using Callback =
-      std::function<void(MojoResult result, MojoHandleSignalsState state)>;
-
-  WatchHelper() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
-  ~WatchHelper() {
-    CHECK(!watching_);
-  }
-
-  void Watch(MojoHandle handle,
-             MojoHandleSignals signals,
-             const Callback& callback) {
-    CHECK(!watching_);
-
-    handle_ = handle;
-    callback_ = callback;
-    watching_ = true;
-    CHECK_EQ(MOJO_RESULT_OK, MojoWatch(handle_, signals, &WatchHelper::OnNotify,
-                                       reinterpret_cast<uintptr_t>(this)));
-  }
-
-  bool is_watching() const { return watching_; }
-
-  void Cancel() {
-    CHECK_EQ(MOJO_RESULT_OK,
-             MojoCancelWatch(handle_, reinterpret_cast<uintptr_t>(this)));
-    CHECK(watching_);
-    watching_ = false;
-  }
-
- private:
-  static void OnNotify(uintptr_t context,
-                       MojoResult result,
-                       MojoHandleSignalsState state,
-                       MojoWatchNotificationFlags flags) {
-    WatchHelper* watcher = reinterpret_cast<WatchHelper*>(context);
-    watcher->task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&NotifyOnMainThread, context, result, state, flags));
-  }
-
-  static void NotifyOnMainThread(uintptr_t context,
-                                 MojoResult result,
-                                 MojoHandleSignalsState state,
-                                 MojoWatchNotificationFlags flags) {
-    WatchHelper* watcher = reinterpret_cast<WatchHelper*>(context);
-    CHECK(watcher->watching_);
-    if (result == MOJO_RESULT_CANCELLED)
-      watcher->watching_ = false;
-    watcher->callback_(result, state);
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  bool watching_ = false;
-  MojoHandle handle_;
-  Callback callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(WatchHelper);
-};
-
-class WatchTest : public test::MojoTestBase {
- public:
-  WatchTest() {}
-  ~WatchTest() override {}
-
- protected:
-
- private:
-  base::MessageLoop message_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(WatchTest);
-};
-
-TEST_F(WatchTest, NotifyBasic) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  base::RunLoop loop;
-  WatchHelper b_watcher;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_TRUE(b_watcher.is_watching());
-        loop.Quit();
-      });
-
-  WriteMessage(a, "Hello!");
-  loop.Run();
-
-  EXPECT_TRUE(b_watcher.is_watching());
-  b_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, NotifyUnsatisfiable) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  base::RunLoop loop;
-  WatchHelper b_watcher;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-        EXPECT_EQ(0u,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_EQ(0u,
-                  state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_TRUE(b_watcher.is_watching());
-        loop.Quit();
-      });
-
-  CloseHandle(a);
-  loop.Run();
-
-  b_watcher.Cancel();
-
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, NotifyCancellation) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  base::RunLoop loop;
-  WatchHelper b_watcher;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
-        EXPECT_EQ(0u, state.satisfied_signals);
-        EXPECT_EQ(0u, state.satisfiable_signals);
-        EXPECT_FALSE(b_watcher.is_watching());
-        loop.Quit();
-      });
-
-  CloseHandle(b);
-  loop.Run();
-
-  CloseHandle(a);
-}
-
-TEST_F(WatchTest, InvalidArguemnts) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  uintptr_t context = reinterpret_cast<uintptr_t>(this);
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
-                                      &IgnoreResult, context));
-
-  // Can't cancel a watch that doesn't exist.
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(a, ~context));
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(b, context));
-
-  CloseHandle(a);
-  CloseHandle(b);
-
-  // Can't watch a handle that doesn't exist.
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoWatch(b, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
-}
-
-TEST_F(WatchTest, NoDuplicateContext) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  // Try to add the same watch twice; should fail.
-  uintptr_t context = reinterpret_cast<uintptr_t>(this);
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
-                                      &IgnoreResult, context));
-  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
-      MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
-
-  // Cancel and add it again; should be OK.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(a, context));
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
-                                      &IgnoreResult, context));
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, MultipleWatches) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  // Add multiple watchers to |b| and see that they are both notified by a
-  // single write to |a|.
-  base::RunLoop loop;
-  int expected_notifications = 2;
-  auto on_readable = [&] (MojoResult result, MojoHandleSignalsState state) {
-    EXPECT_EQ(MOJO_RESULT_OK, result);
-    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-              state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-    EXPECT_GT(expected_notifications, 0);
-    if (--expected_notifications == 0)
-      loop.Quit();
-  };
-  WatchHelper watcher1;
-  WatchHelper watcher2;
-  watcher1.Watch(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
-  watcher2.Watch(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
-
-  WriteMessage(a, "Ping!");
-  loop.Run();
-
-  watcher1.Cancel();
-  watcher2.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, WatchWhileSatisfied) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  // Write to |a| and then start watching |b|. The callback should be invoked
-  // synchronously.
-  WriteMessage(a, "hey");
-  bool signaled = false;
-  WatchHelper b_watcher;
-  base::RunLoop loop;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        signaled = true;
-        loop.Quit();
-      });
-  loop.Run();
-  EXPECT_TRUE(signaled);
-  b_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, WatchWhileUnsatisfiable) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  // Close |a| and then try to watch |b|. MojoWatch() should fail.
-  CloseHandle(a);
-  uintptr_t context = reinterpret_cast<uintptr_t>(this);
-  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            MojoWatch(b, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
-
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, RespondFromCallback) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  // Watch |a| and |b|. Write to |a|, then write to |b| from within the callback
-  // which notifies it of the available message.
-  const std::string kTestMessage = "hello worlds.";
-  base::RunLoop loop;
-  WatchHelper b_watcher;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_TRUE(b_watcher.is_watching());
-
-        // Echo a's message back to it.
-        WriteMessage(b, ReadMessage(b));
-      });
-
-  WatchHelper a_watcher;
-  a_watcher.Watch(
-      a, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_TRUE(a_watcher.is_watching());
-
-        // Expect to receive back the message that was originally sent to |b|.
-        EXPECT_EQ(kTestMessage, ReadMessage(a));
-
-        loop.Quit();
-      });
-
-  WriteMessage(a, kTestMessage);
-  loop.Run();
-
-  a_watcher.Cancel();
-  b_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, WatchDataPipeConsumer) {
-  MojoHandle a, b;
-  CreateDataPipe(&a, &b, 64);
-
-  base::RunLoop loop;
-  WatchHelper b_watcher;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_TRUE(b_watcher.is_watching());
-        loop.Quit();
-      });
-
-  WriteData(a, "Hello!");
-  loop.Run();
-
-  EXPECT_TRUE(b_watcher.is_watching());
-  b_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, WatchDataPipeProducer) {
-  MojoHandle a, b;
-  CreateDataPipe(&a, &b, 8);
-
-  // Fill the pipe to capacity so writes will block.
-  WriteData(a, "xxxxxxxx");
-
-  base::RunLoop loop;
-  WatchHelper a_watcher;
-  a_watcher.Watch(
-      a, MOJO_HANDLE_SIGNAL_WRITABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
-        EXPECT_TRUE(a_watcher.is_watching());
-        loop.Quit();
-      });
-
-  EXPECT_EQ("xxxxxxxx", ReadData(b, 8));
-  loop.Run();
-
-  EXPECT_TRUE(a_watcher.is_watching());
-  a_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, WakeUpSelfWithinWatchCallback) {
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-
-  int expected_notifications = 2;
-  base::RunLoop loop;
-  WatchHelper b_watcher;
-  b_watcher.Watch(
-      b, MOJO_HANDLE_SIGNAL_READABLE,
-      [&] (MojoResult result, MojoHandleSignalsState state) {
-        EXPECT_EQ(MOJO_RESULT_OK, result);
-        EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
-                  state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-        EXPECT_TRUE(b_watcher.is_watching());
-        if (--expected_notifications == 0) {
-          loop.Quit();
-        } else {
-          // Trigger b's watch again from within this callback. This should be
-          // safe to do.
-          WriteMessage(a, "hey");
-        }
-      });
-
-  WriteMessage(a, "hey hey hey");
-  loop.Run();
-
-  b_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(b);
-}
-
-TEST_F(WatchTest, NestedCancellation) {
-  // Verifies that cancellations in nested system request contexts preempt
-  // other notifications for the same watcher. This tests against the condition
-  // hit by http://crbug.com/622298.
-
-  MojoHandle a, b, c, d;
-  CreateMessagePipe(&a, &b);
-  CreateMessagePipe(&c, &d);
-
-  base::RunLoop loop;
-  bool a_watcher_run = false;
-  WatchHelper a_watcher;
-  a_watcher.Watch(
-      a, MOJO_HANDLE_SIGNAL_READABLE,
-      [&](MojoResult result, MojoHandleSignalsState state) {
-        a_watcher_run = true;
-      });
-
-  WatchHelper c_watcher;
-  c_watcher.Watch(
-      c, MOJO_HANDLE_SIGNAL_READABLE,
-      [&](MojoResult result, MojoHandleSignalsState state) {
-        // This will trigger a notification on |a_watcher| above to be executed
-        // once this handler finishes running...
-        CloseHandle(b);
-
-        // ...but this should prevent that notification from dispatching because
-        // |a_watcher| is now cancelled.
-        a_watcher.Cancel();
-
-        loop.Quit();
-      });
-
-  {
-    // Force "system" notifications for the synchronous behavior required to
-    // test this case.
-    mojo::edk::RequestContext request_context(
-        mojo::edk::RequestContext::Source::SYSTEM);
-
-    // Trigger the |c_watcher| callback above.
-    CloseHandle(d);
-  }
-
-  loop.Run();
-
-  EXPECT_FALSE(a_watcher.is_watching());
-  EXPECT_FALSE(a_watcher_run);
-
-  c_watcher.Cancel();
-
-  CloseHandle(a);
-  CloseHandle(c);
-}
-
-}  // namespace
-}  // namespace edk
-}  // namespace mojo
diff --git a/mojo/edk/system/watcher.cc b/mojo/edk/system/watcher.cc
deleted file mode 100644
index 25c227641..0000000
--- a/mojo/edk/system/watcher.cc
+++ /dev/null
@@ -1,53 +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.
-
-#include "mojo/edk/system/watcher.h"
-
-#include "mojo/edk/system/handle_signals_state.h"
-#include "mojo/edk/system/request_context.h"
-
-namespace mojo {
-namespace edk {
-
-Watcher::Watcher(MojoHandleSignals signals, const WatchCallback& callback)
-    : signals_(signals), callback_(callback) {
-}
-
-void Watcher::MaybeInvokeCallback(MojoResult result,
-                                  const HandleSignalsState& state,
-                                  MojoWatchNotificationFlags flags) {
-  base::AutoLock lock(lock_);
-  if (is_cancelled_)
-    return;
-
-  callback_.Run(result, state, flags);
-}
-
-void Watcher::NotifyForStateChange(const HandleSignalsState& signals_state) {
-  RequestContext* request_context = RequestContext::current();
-  if (signals_state.satisfies(signals_)) {
-    request_context->AddWatchNotifyFinalizer(
-        make_scoped_refptr(this), MOJO_RESULT_OK, signals_state);
-  } else if (!signals_state.can_satisfy(signals_)) {
-    request_context->AddWatchNotifyFinalizer(
-        make_scoped_refptr(this), MOJO_RESULT_FAILED_PRECONDITION,
-        signals_state);
-  }
-}
-
-void Watcher::NotifyClosed() {
-  static const HandleSignalsState closed_state = {0, 0};
-  RequestContext::current()->AddWatchNotifyFinalizer(
-      make_scoped_refptr(this), MOJO_RESULT_CANCELLED, closed_state);
-}
-
-void Watcher::Cancel() {
-  base::AutoLock lock(lock_);
-  is_cancelled_ = true;
-}
-
-Watcher::~Watcher() {}
-
-}  // namespace edk
-}  // namespace mojo
diff --git a/mojo/edk/system/watcher.h b/mojo/edk/system/watcher.h
deleted file mode 100644
index b6dc2e46..0000000
--- a/mojo/edk/system/watcher.h
+++ /dev/null
@@ -1,88 +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 MOJO_EDK_SYSTEM_WATCHER_H_
-#define MOJO_EDK_SYSTEM_WATCHER_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "mojo/public/c/system/functions.h"
-#include "mojo/public/c/system/types.h"
-
-namespace mojo {
-namespace edk {
-
-struct HandleSignalsState;
-
-// This object corresponds to a watch added by a single call to |MojoWatch()|.
-//
-// An event may occur at any time which should trigger a Watcher to run its
-// callback, but the callback needs to be deferred until all EDK locks are
-// released. At the same time, a watch may be cancelled at any time by
-// |MojoCancelWatch()| and it is not OK for the callback to be invoked after
-// that happens.
-//
-// Therefore a Watcher needs to have some associated thread-safe state to track
-// its cancellation, which is why it's ref-counted.
-class Watcher : public base::RefCountedThreadSafe<Watcher> {
- public:
-  using WatchCallback = base::Callback<void(MojoResult,
-                                            const HandleSignalsState&,
-                                            MojoWatchNotificationFlags)>;
-
-  // Constructs a new Watcher which watches for |signals| to be satisfied on a
-  // handle and which invokes |callback| either when one such signal is
-  // satisfied, or all such signals become unsatisfiable.
-  Watcher(MojoHandleSignals signals, const WatchCallback& callback);
-
-  // Runs the Watcher's callback with the given arguments if it hasn't been
-  // cancelled yet.
-  void MaybeInvokeCallback(MojoResult result,
-                           const HandleSignalsState& state,
-                           MojoWatchNotificationFlags flags);
-
-  // Notifies the Watcher of a state change. This may result in the Watcher
-  // adding a finalizer to the current RequestContext to invoke its callback,
-  // cancellation notwithstanding.
-  void NotifyForStateChange(const HandleSignalsState& signals_state);
-
-  // Notifies the Watcher of handle closure. This always results in the Watcher
-  // adding a finalizer to the current RequestContext to invoke its callback,
-  // cancellation notwithstanding.
-  void NotifyClosed();
-
-  // Explicitly cancels the watch, guaranteeing that its callback will never be
-  // be invoked again.
-  void Cancel();
-
- private:
-  friend class base::RefCountedThreadSafe<Watcher>;
-
-  ~Watcher();
-
-  // The set of signals which are watched by this Watcher.
-  const MojoHandleSignals signals_;
-
-  // The callback to invoke with a result and signal state any time signals in
-  // |signals_| are satisfied or become permanently unsatisfiable.
-  const WatchCallback callback_;
-
-  // Guards |is_cancelled_|.
-  base::Lock lock_;
-
-  // Indicates whether the watch has been cancelled. A |Watcher| may exist for a
-  // brief period of time after being cancelled if it's been attached as a
-  // RequestContext finalizer. In such cases the callback must not be invoked,
-  // hence this flag.
-  bool is_cancelled_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(Watcher);
-};
-
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_SYSTEM_WATCHER_H_
diff --git a/mojo/edk/system/watcher_dispatcher.cc b/mojo/edk/system/watcher_dispatcher.cc
new file mode 100644
index 0000000..e4cc875
--- /dev/null
+++ b/mojo/edk/system/watcher_dispatcher.cc
@@ -0,0 +1,219 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/watcher_dispatcher.h"
+
+#include <limits>
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "mojo/edk/system/watch.h"
+
+namespace mojo {
+namespace edk {
+
+WatcherDispatcher::WatcherDispatcher(MojoWatcherCallback callback)
+    : callback_(callback) {}
+
+void WatcherDispatcher::NotifyHandleState(Dispatcher* dispatcher,
+                                          const HandleSignalsState& state) {
+  base::AutoLock lock(lock_);
+  auto it = watched_handles_.find(dispatcher);
+  if (it == watched_handles_.end())
+    return;
+
+  // Maybe fire a notification to the watch assoicated with this dispatcher,
+  // provided we're armed it cares about the new state.
+  if (it->second->NotifyState(state, armed_)) {
+    ready_watches_.insert(it->second.get());
+
+    // If we were armed and got here, we notified the watch. Disarm.
+    armed_ = false;
+  } else {
+    ready_watches_.erase(it->second.get());
+  }
+}
+
+void WatcherDispatcher::NotifyHandleClosed(Dispatcher* dispatcher) {
+  scoped_refptr<Watch> watch;
+  {
+    base::AutoLock lock(lock_);
+    auto it = watched_handles_.find(dispatcher);
+    if (it == watched_handles_.end())
+      return;
+
+    watch = std::move(it->second);
+
+    // Wipe out all state associated with the closed dispatcher.
+    watches_.erase(watch->context());
+    ready_watches_.erase(watch.get());
+    watched_handles_.erase(it);
+  }
+
+  // NOTE: It's important that this is called outside of |lock_| since it
+  // acquires internal Watch locks.
+  watch->Cancel();
+}
+
+void WatcherDispatcher::InvokeWatchCallback(
+    uintptr_t context,
+    MojoResult result,
+    const HandleSignalsState& state,
+    MojoWatcherNotificationFlags flags) {
+  {
+    // We avoid holding the lock during dispatch. It's OK for notification
+    // callbacks to close this watcher, and it's OK for notifications to race
+    // with closure, if for example the watcher is closed from another thread
+    // between this test and the invocation of |callback_| below.
+    //
+    // Because cancellation synchronously blocks all future notifications, and
+    // because notifications themselves are mutually exclusive for any given
+    // context, we still guarantee that a single MOJO_RESULT_CANCELLED result
+    // is the last notification received for any given context.
+    //
+    // This guarantee is sufficient to make safe, synchronized, per-context
+    // state management possible in user code.
+    base::AutoLock lock(lock_);
+    if (closed_ && result != MOJO_RESULT_CANCELLED)
+      return;
+  }
+
+  callback_(context, result, static_cast<MojoHandleSignalsState>(state), flags);
+}
+
+Dispatcher::Type WatcherDispatcher::GetType() const {
+  return Type::WATCHER;
+}
+
+MojoResult WatcherDispatcher::Close() {
+  // We swap out all the watched handle information onto the stack so we can
+  // call into their dispatchers without our own lock held.
+  std::map<uintptr_t, scoped_refptr<Watch>> watches;
+  {
+    base::AutoLock lock(lock_);
+    DCHECK(!closed_);
+    closed_ = true;
+    std::swap(watches, watches_);
+    watched_handles_.clear();
+  }
+
+  // Remove all refs from our watched dispatchers and fire cancellations.
+  for (auto& entry : watches) {
+    entry.second->dispatcher()->RemoveWatcherRef(this, entry.first);
+    entry.second->Cancel();
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WatcherDispatcher::WatchDispatcher(
+    scoped_refptr<Dispatcher> dispatcher,
+    MojoHandleSignals signals,
+    uintptr_t context) {
+  // NOTE: Because it's critical to avoid acquiring any other dispatcher locks
+  // while |lock_| is held, we defer adding oursevles to the dispatcher until
+  // after we've updated all our own relevant state and released |lock_|.
+  {
+    base::AutoLock lock(lock_);
+    if (watches_.count(context) || watched_handles_.count(dispatcher.get()))
+      return MOJO_RESULT_ALREADY_EXISTS;
+
+    scoped_refptr<Watch> watch = new Watch(this, dispatcher, context, signals);
+    watches_.insert({context, watch});
+    auto result =
+        watched_handles_.insert(std::make_pair(dispatcher.get(), watch));
+    DCHECK(result.second);
+  }
+
+  MojoResult rv = dispatcher->AddWatcherRef(this, context);
+  if (rv != MOJO_RESULT_OK) {
+    // Oops. This was not a valid handle to watch. Undo the above work and
+    // fail gracefully.
+    base::AutoLock lock(lock_);
+    watches_.erase(context);
+    watched_handles_.erase(dispatcher.get());
+    return rv;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WatcherDispatcher::CancelWatch(uintptr_t context) {
+  // We may remove the last stored ref to the Watch below, so we retain
+  // a reference on the stack.
+  scoped_refptr<Watch> watch;
+  {
+    base::AutoLock lock(lock_);
+    auto it = watches_.find(context);
+    if (it == watches_.end())
+      return MOJO_RESULT_NOT_FOUND;
+    watch = it->second;
+    watches_.erase(it);
+  }
+
+  // Mark the watch as cancelled so no further notifications get through.
+  watch->Cancel();
+
+  // We remove the watcher ref for this context before updating any more
+  // internal watcher state, ensuring that we don't receiving further
+  // notifications for this context.
+  watch->dispatcher()->RemoveWatcherRef(this, context);
+
+  {
+    base::AutoLock lock(lock_);
+    auto handle_it = watched_handles_.find(watch->dispatcher().get());
+    DCHECK(handle_it != watched_handles_.end());
+    ready_watches_.erase(handle_it->second.get());
+    watched_handles_.erase(handle_it);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WatcherDispatcher::Arm(
+    uint32_t* num_ready_contexts,
+    uintptr_t* ready_contexts,
+    MojoResult* ready_results,
+    MojoHandleSignalsState* ready_signals_states) {
+  base::AutoLock lock(lock_);
+  if (num_ready_contexts &&
+      (!ready_contexts || !ready_results || !ready_signals_states)) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  if (watched_handles_.empty())
+    return MOJO_RESULT_NOT_FOUND;
+
+  if (ready_watches_.empty()) {
+    // Fast path: No watches are ready to notify, so we're done.
+    armed_ = true;
+    return MOJO_RESULT_OK;
+  }
+
+  if (num_ready_contexts) {
+    uint32_t max_ready_contexts = *num_ready_contexts;
+    *num_ready_contexts = 0;
+    for (auto& entry : watched_handles_) {
+      if (max_ready_contexts == *num_ready_contexts)
+        break;
+
+      const Watch* watch = entry.second.get();
+      if (!watch->ready())
+        continue;
+
+      *(ready_contexts++) = watch->context();
+      *(ready_results++) = watch->last_known_result();
+      *(ready_signals_states++) = watch->last_known_signals_state();
+      ++(*num_ready_contexts);
+    }
+  }
+
+  return MOJO_RESULT_FAILED_PRECONDITION;
+}
+
+WatcherDispatcher::~WatcherDispatcher() {}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/watcher_dispatcher.h b/mojo/edk/system/watcher_dispatcher.h
new file mode 100644
index 0000000..9698818
--- /dev/null
+++ b/mojo/edk/system/watcher_dispatcher.h
@@ -0,0 +1,91 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_
+
+#include <map>
+#include <set>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/watcher.h"
+
+namespace mojo {
+namespace edk {
+
+class Watch;
+
+// The dispatcher type which backs watcher handles.
+class WatcherDispatcher : public Dispatcher {
+ public:
+  // Constructs a new WatcherDispatcher which invokes |callback| when a
+  // registered watch observes some relevant state change.
+  explicit WatcherDispatcher(MojoWatcherCallback callback);
+
+  // Methods used by watched dispatchers to notify watchers of events.
+  void NotifyHandleState(Dispatcher* dispatcher,
+                         const HandleSignalsState& state);
+  void NotifyHandleClosed(Dispatcher* dispatcher);
+
+  // Method used by RequestContext (indirectly, via Watch) to complete
+  // notification operations from a safe stack frame to avoid reentrancy.
+  void InvokeWatchCallback(uintptr_t context,
+                           MojoResult result,
+                           const HandleSignalsState& state,
+                           MojoWatcherNotificationFlags flags);
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  MojoResult WatchDispatcher(scoped_refptr<Dispatcher> dispatcher,
+                             MojoHandleSignals signals,
+                             uintptr_t context) override;
+  MojoResult CancelWatch(uintptr_t context) override;
+  MojoResult Arm(uint32_t* num_ready_contexts,
+                 uintptr_t* ready_contexts,
+                 MojoResult* ready_results,
+                 MojoHandleSignalsState* ready_signals_states) override;
+
+ private:
+  friend class Watch;
+
+  ~WatcherDispatcher() override;
+
+  const MojoWatcherCallback callback_;
+
+  // Guards access to the fields below.
+  //
+  // NOTE: This may be acquired while holding another dispatcher's lock, as
+  // watched dispatchers call into WatcherDispatcher methods which lock this
+  // when issuing state change notifications. WatcherDispatcher must therefore
+  // take caution to NEVER acquire other dispatcher locks while this is held.
+  base::Lock lock_;
+
+  bool armed_ = false;
+  bool closed_ = false;
+
+  // A mapping from context to Watch.
+  std::map<uintptr_t, scoped_refptr<Watch>> watches_;
+
+  // A mapping from watched dispatcher to Watch.
+  std::map<Dispatcher*, scoped_refptr<Watch>> watched_handles_;
+
+  // The set of all Watch instances which are currently ready to signal. This is
+  // used for efficient arming behavior, as it allows for O(1) discovery of
+  // whether or not arming can succeed and quick determination of who's
+  // responsible if it can't.
+  std::set<Watch*> ready_watches_;
+
+  DISALLOW_COPY_AND_ASSIGN(WatcherDispatcher);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_
diff --git a/mojo/edk/system/watcher_set.cc b/mojo/edk/system/watcher_set.cc
index 878f29a..0355b58 100644
--- a/mojo/edk/system/watcher_set.cc
+++ b/mojo/edk/system/watcher_set.cc
@@ -4,54 +4,79 @@
 
 #include "mojo/edk/system/watcher_set.h"
 
-#include "mojo/edk/system/request_context.h"
-#include "mojo/public/c/system/types.h"
+#include <utility>
 
 namespace mojo {
 namespace edk {
 
-WatcherSet::WatcherSet() {}
+WatcherSet::WatcherSet(Dispatcher* owner) : owner_(owner) {}
 
-WatcherSet::~WatcherSet() {}
+WatcherSet::~WatcherSet() = default;
 
-void WatcherSet::NotifyForStateChange(const HandleSignalsState& state) {
+void WatcherSet::NotifyState(const HandleSignalsState& state) {
+  // Avoid notifying watchers if they have already seen this state.
+  if (last_known_state_.has_value() && state.equals(last_known_state_.value()))
+    return;
+  last_known_state_ = state;
   for (const auto& entry : watchers_)
-    entry.second->NotifyForStateChange(state);
+    entry.first->NotifyHandleState(owner_, state);
 }
 
 void WatcherSet::NotifyClosed() {
   for (const auto& entry : watchers_)
-    entry.second->NotifyClosed();
+    entry.first->NotifyHandleClosed(owner_);
 }
 
-MojoResult WatcherSet::Add(MojoHandleSignals signals,
-                           const Watcher::WatchCallback& callback,
+MojoResult WatcherSet::Add(const scoped_refptr<WatcherDispatcher>& watcher,
                            uintptr_t context,
                            const HandleSignalsState& current_state) {
-  auto it = watchers_.find(context);
-  if (it != watchers_.end())
+  auto it = watchers_.find(watcher.get());
+  if (it == watchers_.end()) {
+    auto result =
+        watchers_.insert(std::make_pair(watcher.get(), Entry{watcher}));
+    it = result.first;
+  }
+
+  if (!it->second.contexts.insert(context).second)
     return MOJO_RESULT_ALREADY_EXISTS;
 
-  if (!current_state.can_satisfy(signals))
-    return MOJO_RESULT_FAILED_PRECONDITION;
-
-  scoped_refptr<Watcher> watcher(new Watcher(signals, callback));
-  watchers_.insert(std::make_pair(context, watcher));
-
-  watcher->NotifyForStateChange(current_state);
-
+  if (last_known_state_.has_value() &&
+      !current_state.equals(last_known_state_.value())) {
+    // This new state may be relevant to everyone, in which case we just
+    // notify everyone.
+    NotifyState(current_state);
+  } else {
+    // Otherwise only notify the newly added Watcher.
+    watcher->NotifyHandleState(owner_, current_state);
+  }
   return MOJO_RESULT_OK;
 }
 
-MojoResult WatcherSet::Remove(uintptr_t context) {
-  auto it = watchers_.find(context);
+MojoResult WatcherSet::Remove(WatcherDispatcher* watcher, uintptr_t context) {
+  auto it = watchers_.find(watcher);
   if (it == watchers_.end())
-    return MOJO_RESULT_INVALID_ARGUMENT;
+    return MOJO_RESULT_NOT_FOUND;
 
-  RequestContext::current()->AddWatchCancelFinalizer(it->second);
-  watchers_.erase(it);
+  ContextSet& contexts = it->second.contexts;
+  auto context_it = contexts.find(context);
+  if (context_it == contexts.end())
+    return MOJO_RESULT_NOT_FOUND;
+
+  contexts.erase(context_it);
+  if (contexts.empty())
+    watchers_.erase(it);
+
   return MOJO_RESULT_OK;
 }
 
+WatcherSet::Entry::Entry(const scoped_refptr<WatcherDispatcher>& dispatcher)
+    : dispatcher(dispatcher) {}
+
+WatcherSet::Entry::Entry(Entry&& other) = default;
+
+WatcherSet::Entry::~Entry() = default;
+
+WatcherSet::Entry& WatcherSet::Entry::operator=(Entry&& other) = default;
+
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/watcher_set.h b/mojo/edk/system/watcher_set.h
index 8ae54a1b..2b7ef2c5a 100644
--- a/mojo/edk/system/watcher_set.h
+++ b/mojo/edk/system/watcher_set.h
@@ -5,45 +5,62 @@
 #ifndef MOJO_EDK_SYSTEM_WATCHER_SET_H_
 #define MOJO_EDK_SYSTEM_WATCHER_SET_H_
 
-#include <unordered_map>
+#include <map>
 
-#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "mojo/edk/system/handle_signals_state.h"
-#include "mojo/edk/system/watcher.h"
-#include "mojo/public/c/system/types.h"
+#include "mojo/edk/system/watcher_dispatcher.h"
 
 namespace mojo {
 namespace edk {
 
-// A WatcherSet maintains a set of Watchers attached to a single handle and
-// keyed on an arbitrary user context.
+// A WatcherSet maintains a set of references to WatcherDispatchers to be
+// notified when a handle changes state.
+//
+// Dispatchers which may be watched by a watcher should own a WatcherSet and
+// notify it of all relevant state changes.
 class WatcherSet {
  public:
-  WatcherSet();
+  // |owner| is the Dispatcher who owns this WatcherSet.
+  explicit WatcherSet(Dispatcher* owner);
   ~WatcherSet();
 
-  // Notifies all Watchers of a state change.
-  void NotifyForStateChange(const HandleSignalsState& state);
+  // Notifies all watchers of the handle's current signals state.
+  void NotifyState(const HandleSignalsState& state);
 
-  // Notifies all Watchers that their watched handle has been closed.
+  // Notifies all watchers that this handle has been closed.
   void NotifyClosed();
 
-  // Adds a new watcher to watch for signals in |signals| to be satisfied or
-  // unsatisfiable. |current_state| is the current signals state of the
-  // handle being watched.
-  MojoResult Add(MojoHandleSignals signals,
-                 const Watcher::WatchCallback& callback,
+  // Adds a new watcher+context.
+  MojoResult Add(const scoped_refptr<WatcherDispatcher>& watcher,
                  uintptr_t context,
                  const HandleSignalsState& current_state);
 
-  // Removes a watcher from the set.
-  MojoResult Remove(uintptr_t context);
+  // Removes a watcher+context.
+  MojoResult Remove(WatcherDispatcher* watcher, uintptr_t context);
 
  private:
-  // A map of watchers keyed on context value.
-  std::unordered_map<uintptr_t, scoped_refptr<Watcher>> watchers_;
+  using ContextSet = std::set<uintptr_t>;
+
+  struct Entry {
+    Entry(const scoped_refptr<WatcherDispatcher>& dispatcher);
+    Entry(Entry&& other);
+    ~Entry();
+
+    Entry& operator=(Entry&& other);
+
+    scoped_refptr<WatcherDispatcher> dispatcher;
+    ContextSet contexts;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Entry);
+  };
+
+  Dispatcher* const owner_;
+  std::map<WatcherDispatcher*, Entry> watchers_;
+  base::Optional<HandleSignalsState> last_known_state_;
 
   DISALLOW_COPY_AND_ASSIGN(WatcherSet);
 };
diff --git a/mojo/edk/system/watcher_unittest.cc b/mojo/edk/system/watcher_unittest.cc
new file mode 100644
index 0000000..a3f2742
--- /dev/null
+++ b/mojo/edk/system/watcher_unittest.cc
@@ -0,0 +1,1590 @@
+// Copyright 2017 The Chromium 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 <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/c/system/watcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+using WatcherTest = test::MojoTestBase;
+
+class WatchHelper {
+ public:
+  using ContextCallback =
+      base::Callback<void(MojoResult, MojoHandleSignalsState)>;
+
+  WatchHelper() {}
+  ~WatchHelper() {}
+
+  MojoResult CreateWatcher(MojoHandle* handle) {
+    return MojoCreateWatcher(&Notify, handle);
+  }
+
+  uintptr_t CreateContext(const ContextCallback& callback) {
+    return CreateContextWithCancel(callback, base::Closure());
+  }
+
+  uintptr_t CreateContextWithCancel(const ContextCallback& callback,
+                                    const base::Closure& cancel_callback) {
+    auto context = base::MakeUnique<NotificationContext>(callback);
+    NotificationContext* raw_context = context.get();
+    raw_context->SetCancelCallback(base::Bind(
+        [](std::unique_ptr<NotificationContext> context,
+           const base::Closure& cancel_callback) {
+          if (cancel_callback)
+            cancel_callback.Run();
+        },
+        base::Passed(&context), cancel_callback));
+    return reinterpret_cast<uintptr_t>(raw_context);
+  }
+
+ private:
+  class NotificationContext {
+   public:
+    explicit NotificationContext(const ContextCallback& callback)
+        : callback_(callback) {}
+
+    ~NotificationContext() {}
+
+    void SetCancelCallback(const base::Closure& cancel_callback) {
+      cancel_callback_ = cancel_callback;
+    }
+
+    void Notify(MojoResult result, MojoHandleSignalsState state) {
+      if (result == MOJO_RESULT_CANCELLED)
+        cancel_callback_.Run();
+      else
+        callback_.Run(result, state);
+    }
+
+   private:
+    const ContextCallback callback_;
+    base::Closure cancel_callback_;
+
+    DISALLOW_COPY_AND_ASSIGN(NotificationContext);
+  };
+
+  static void Notify(uintptr_t context,
+                     MojoResult result,
+                     MojoHandleSignalsState state,
+                     MojoWatcherNotificationFlags flags) {
+    reinterpret_cast<NotificationContext*>(context)->Notify(result, state);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WatchHelper);
+};
+
+class ThreadedRunner : public base::SimpleThread {
+ public:
+  explicit ThreadedRunner(const base::Closure& callback)
+      : SimpleThread("ThreadedRunner"), callback_(callback) {}
+  ~ThreadedRunner() override {}
+
+  void Run() override { callback_.Run(); }
+
+ private:
+  const base::Closure callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadedRunner);
+};
+
+void ExpectNoNotification(uintptr_t context,
+                          MojoResult result,
+                          MojoHandleSignalsState state,
+                          MojoWatcherNotificationFlags flags) {
+  NOTREACHED();
+}
+
+void ExpectOnlyCancel(uintptr_t context,
+                      MojoResult result,
+                      MojoHandleSignalsState state,
+                      MojoWatcherNotificationFlags flags) {
+  EXPECT_EQ(result, MOJO_RESULT_CANCELLED);
+}
+
+TEST_F(WatcherTest, InvalidArguments) {
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoCreateWatcher(&ExpectNoNotification, nullptr));
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w));
+
+  // Try to watch unwatchable handles.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWatch(w, w, MOJO_HANDLE_SIGNAL_READABLE, 0));
+  MojoHandle buffer_handle = CreateBuffer(42);
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWatch(w, buffer_handle, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  // Try to cancel a watch on an invalid watcher handle.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(buffer_handle, 0));
+
+  // Try to arm an invalid handle.
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      MojoArmWatcher(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr, nullptr));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoArmWatcher(buffer_handle, nullptr, nullptr, nullptr, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(buffer_handle));
+
+  // Try to arm with a non-null count but at least one null output buffer.
+  uint32_t num_ready_contexts = 1;
+  uintptr_t ready_context;
+  MojoResult ready_result;
+  MojoHandleSignalsState ready_state;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoArmWatcher(w, &num_ready_contexts, nullptr, &ready_result,
+                           &ready_state));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoArmWatcher(w, &num_ready_contexts, &ready_context, nullptr,
+                           &ready_state));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoArmWatcher(w, &num_ready_contexts, &ready_context,
+                           &ready_result, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, WatchMessagePipeReadable) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  int num_expected_notifications = 1;
+  const uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, int* expected_count, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_GT(*expected_count, 0);
+        *expected_count -= 1;
+
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        event->Signal();
+      },
+      &event, &num_expected_notifications));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  const char kMessage1[] = "hey hey hey hey";
+  const char kMessage2[] = "i said hey";
+  const char kMessage3[] = "what's goin' on?";
+
+  // Writing to |b| multiple times should notify exactly once.
+  WriteMessage(b, kMessage1);
+  WriteMessage(b, kMessage2);
+  event.Wait();
+
+  // This also shouldn't fire a notification; the watcher is still disarmed.
+  WriteMessage(b, kMessage3);
+
+  // Arming should fail with relevant information.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(readable_a_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+
+  // Flush the three messages from above.
+  EXPECT_EQ(kMessage1, ReadMessage(a));
+  EXPECT_EQ(kMessage2, ReadMessage(a));
+  EXPECT_EQ(kMessage3, ReadMessage(a));
+
+  // Now we can rearm the watcher.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+}
+
+TEST_F(WatcherTest, CloseWatchedMessagePipeHandle) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  const uintptr_t readable_a_context = helper.CreateContextWithCancel(
+      WatchHelper::ContextCallback(),
+      base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+
+  // Test that closing a watched handle fires an appropriate notification, even
+  // when the watcher is unarmed.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, CloseWatchedMessagePipeHandlePeer) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  const uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+        event->Signal();
+      },
+      &event));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+
+  // Test that closing a watched handle's peer with an armed watcher fires an
+  // appropriate notification.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  event.Wait();
+
+  // And now arming should fail with correct information about |a|'s state.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(readable_a_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals &
+              MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+  EXPECT_FALSE(ready_states[0].satisfiable_signals &
+               MOJO_HANDLE_SIGNAL_READABLE);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+}
+
+TEST_F(WatcherTest, WatchDataPipeConsumerReadable) {
+  constexpr size_t kTestPipeCapacity = 64;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  int num_expected_notifications = 1;
+  const uintptr_t readable_consumer_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, int* expected_count, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_GT(*expected_count, 0);
+        *expected_count -= 1;
+
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        event->Signal();
+      },
+      &event, &num_expected_notifications));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE,
+                                      readable_consumer_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  const char kMessage1[] = "hey hey hey hey";
+  const char kMessage2[] = "i said hey";
+  const char kMessage3[] = "what's goin' on?";
+
+  // Writing to |producer| multiple times should notify exactly once.
+  WriteData(producer, kMessage1);
+  WriteData(producer, kMessage2);
+  event.Wait();
+
+  // This also shouldn't fire a notification; the watcher is still disarmed.
+  WriteData(producer, kMessage3);
+
+  // Arming should fail with relevant information.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(readable_consumer_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+
+  // Flush the three messages from above.
+  EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1));
+  EXPECT_EQ(kMessage2, ReadData(consumer, sizeof(kMessage2) - 1));
+  EXPECT_EQ(kMessage3, ReadData(consumer, sizeof(kMessage3) - 1));
+
+  // Now we can rearm the watcher.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+}
+
+TEST_F(WatcherTest, WatchDataPipeConsumerNewDataReadable) {
+  constexpr size_t kTestPipeCapacity = 64;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  int num_new_data_notifications = 0;
+  const uintptr_t new_data_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, int* notification_count, MojoResult result,
+         MojoHandleSignalsState state) {
+        *notification_count += 1;
+
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        event->Signal();
+      },
+      &event, &num_new_data_notifications));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+                      new_data_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  const char kMessage1[] = "hey hey hey hey";
+  const char kMessage2[] = "i said hey";
+  const char kMessage3[] = "what's goin' on?";
+
+  // Writing to |producer| multiple times should notify exactly once.
+  WriteData(producer, kMessage1);
+  WriteData(producer, kMessage2);
+  event.Wait();
+
+  // This also shouldn't fire a notification; the watcher is still disarmed.
+  WriteData(producer, kMessage3);
+
+  // Arming should fail with relevant information.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(new_data_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+
+  // Attempt to read more data than is available. Should fail but clear the
+  // NEW_DATA_READABLE signal.
+  char large_buffer[512];
+  uint32_t large_read_size = 512;
+  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            MojoReadData(consumer, large_buffer, &large_read_size,
+                         MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+  // Attempt to arm again. Should succeed.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  // Write more data. Should notify.
+  event.Reset();
+  WriteData(producer, kMessage1);
+  event.Wait();
+
+  // Reading some data should clear NEW_DATA_READABLE again so we can rearm.
+  EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  EXPECT_EQ(2, num_new_data_notifications);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+}
+
+TEST_F(WatcherTest, WatchDataPipeProducerWritable) {
+  constexpr size_t kTestPipeCapacity = 8;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  // Half the capacity of the data pipe.
+  const char kTestData[] = "aaaa";
+  static_assert((sizeof(kTestData) - 1) * 2 == kTestPipeCapacity,
+                "Invalid test data for this test.");
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  int num_expected_notifications = 1;
+  const uintptr_t writable_producer_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, int* expected_count, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_GT(*expected_count, 0);
+        *expected_count -= 1;
+
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        event->Signal();
+      },
+      &event, &num_expected_notifications));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE,
+                                      writable_producer_context));
+
+  // The producer is already writable, so arming should fail with relevant
+  // information.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(writable_producer_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  // Write some data, but don't fill the pipe yet. Arming should fail again.
+  WriteData(producer, kTestData);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(writable_producer_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  // Write more data, filling the pipe to capacity. Arming should succeed now.
+  WriteData(producer, kTestData);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  // Now read from the pipe, making the producer writable again. Should notify.
+  EXPECT_EQ(kTestData, ReadData(consumer, sizeof(kTestData) - 1));
+  event.Wait();
+
+  // Arming should fail again.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(writable_producer_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  // Fill the pipe once more and arm the watcher. Should succeed.
+  WriteData(producer, kTestData);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+};
+
+TEST_F(WatcherTest, CloseWatchedDataPipeConsumerHandle) {
+  constexpr size_t kTestPipeCapacity = 8;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  const uintptr_t readable_consumer_context = helper.CreateContextWithCancel(
+      WatchHelper::ContextCallback(),
+      base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE,
+                                      readable_consumer_context));
+
+  // Closing the consumer should fire a cancellation notification.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, CloseWatcherDataPipeConsumerHandlePeer) {
+  constexpr size_t kTestPipeCapacity = 8;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  const uintptr_t readable_consumer_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+        event->Signal();
+      },
+      &event));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE,
+                                      readable_consumer_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  // Closing the producer should fire a notification for an unsatisfiable watch.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+  event.Wait();
+
+  // Now attempt to rearm and expect appropriate error feedback.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(readable_consumer_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]);
+  EXPECT_FALSE(ready_states[0].satisfiable_signals &
+               MOJO_HANDLE_SIGNAL_READABLE);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+}
+
+TEST_F(WatcherTest, CloseWatchedDataPipeProducerHandle) {
+  constexpr size_t kTestPipeCapacity = 8;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  const uintptr_t writable_producer_context = helper.CreateContextWithCancel(
+      WatchHelper::ContextCallback(),
+      base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE,
+                                      writable_producer_context));
+
+  // Closing the consumer should fire a cancellation notification.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, CloseWatchedDataPipeProducerHandlePeer) {
+  constexpr size_t kTestPipeCapacity = 8;
+  MojoHandle producer, consumer;
+  CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
+
+  const char kTestMessageFullCapacity[] = "xxxxxxxx";
+  static_assert(sizeof(kTestMessageFullCapacity) - 1 == kTestPipeCapacity,
+                "Invalid test message size for this test.");
+
+  // Make the pipe unwritable initially.
+  WriteData(producer, kTestMessageFullCapacity);
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  const uintptr_t writable_producer_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+        event->Signal();
+      },
+      &event));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE,
+                                      writable_producer_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  // Closing the consumer should fire a notification for an unsatisfiable watch,
+  // as the full data pipe can never be read from again and is therefore
+  // permanently full and unwritable.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
+  event.Wait();
+
+  // Now attempt to rearm and expect appropriate error feedback.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(writable_producer_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]);
+  EXPECT_FALSE(ready_states[0].satisfiable_signals &
+               MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+}
+
+TEST_F(WatcherTest, ArmWithNoWatches) {
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w));
+  EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, WatchDuplicateContext) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, 0));
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+}
+
+TEST_F(WatcherTest, CancelUnknownWatch) {
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w));
+  EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoCancelWatch(w, 1234));
+}
+
+TEST_F(WatcherTest, ArmWithWatchAlreadySatisfied) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+  // |a| is always writable, so we can never arm this watcher.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(0u, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+}
+
+TEST_F(WatcherTest, ArmWithWatchAlreadyUnsatisfiable) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+
+  // |b| is closed and never wrote any messages, so |a| won't be readable again.
+  // MojoArmWatcher() should fail, incidcating as much.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = kMaxReadyContexts;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(0u, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals &
+              MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+  EXPECT_FALSE(ready_states[0].satisfiable_signals &
+               MOJO_HANDLE_SIGNAL_READABLE);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+}
+
+TEST_F(WatcherTest, MultipleWatches) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  base::WaitableEvent a_event(base::WaitableEvent::ResetPolicy::MANUAL,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::WaitableEvent b_event(base::WaitableEvent::ResetPolicy::MANUAL,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+  WatchHelper helper;
+  int num_a_notifications = 0;
+  int num_b_notifications = 0;
+  auto notify_callback =
+      base::Bind([](base::WaitableEvent* event, int* notification_count,
+                    MojoResult result, MojoHandleSignalsState state) {
+        *notification_count += 1;
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        event->Signal();
+      });
+  uintptr_t readable_a_context = helper.CreateContext(
+      base::Bind(notify_callback, &a_event, &num_a_notifications));
+  uintptr_t readable_b_context = helper.CreateContext(
+      base::Bind(notify_callback, &b_event, &num_b_notifications));
+
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  // Add two independent watch contexts to watch for |a| or |b| readability.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  const char kMessage1[] = "things are happening";
+  const char kMessage2[] = "ok. ok. ok. ok.";
+  const char kMessage3[] = "plz wake up";
+
+  // Writing to |b| should signal |a|'s watch.
+  WriteMessage(b, kMessage1);
+  a_event.Wait();
+  a_event.Reset();
+
+  // Subsequent messages on |b| should not trigger another notification.
+  WriteMessage(b, kMessage2);
+  WriteMessage(b, kMessage3);
+
+  // Messages on |a| also shouldn't trigger |b|'s notification, since the
+  // watcher should be disarmed by now.
+  WriteMessage(a, kMessage1);
+  WriteMessage(a, kMessage2);
+  WriteMessage(a, kMessage3);
+
+  // Arming should fail. Since we only ask for at most one context's information
+  // that's all we should get back. Which one we get is unspecified.
+  constexpr size_t kMaxReadyContexts = 10;
+  uint32_t num_ready_contexts = 1;
+  uintptr_t ready_contexts[kMaxReadyContexts];
+  MojoResult ready_results[kMaxReadyContexts];
+  MojoHandleSignalsState ready_states[kMaxReadyContexts];
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_TRUE(ready_contexts[0] == readable_a_context ||
+              ready_contexts[0] == readable_b_context);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  // Now try arming again, verifying that both contexts are returned.
+  num_ready_contexts = kMaxReadyContexts;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(2u, num_ready_contexts);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[1]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+  EXPECT_TRUE(ready_states[1].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+  EXPECT_TRUE((ready_contexts[0] == readable_a_context &&
+               ready_contexts[1] == readable_b_context) ||
+              (ready_contexts[0] == readable_b_context &&
+               ready_contexts[1] == readable_a_context));
+
+  // Flush out the test messages so we should be able to successfully rearm.
+  EXPECT_EQ(kMessage1, ReadMessage(a));
+  EXPECT_EQ(kMessage2, ReadMessage(a));
+  EXPECT_EQ(kMessage3, ReadMessage(a));
+  EXPECT_EQ(kMessage1, ReadMessage(b));
+  EXPECT_EQ(kMessage2, ReadMessage(b));
+  EXPECT_EQ(kMessage3, ReadMessage(b));
+
+  // Add a watch which is always satisfied, so we can't arm. Arming should fail
+  // with only this new watch's information.
+  uintptr_t writable_c_context = helper.CreateContext(base::Bind(
+      [](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); }));
+  MojoHandle c, d;
+  CreateMessagePipe(&c, &d);
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, c, MOJO_HANDLE_SIGNAL_WRITABLE, writable_c_context));
+  num_ready_contexts = kMaxReadyContexts;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+                           ready_results, ready_states));
+  EXPECT_EQ(1u, num_ready_contexts);
+  EXPECT_EQ(writable_c_context, ready_contexts[0]);
+  EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+  EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  // Cancel the new watch and arming should succeed once again.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, writable_c_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(WatcherTest, NotifyOtherFromNotificationCallback) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA[] = "hello a";
+  static const char kTestMessageToB[] = "hello b";
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WatchHelper helper;
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](MojoHandle w, MojoHandle a, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ("hello a", ReadMessage(a));
+
+        // Re-arm the watcher and signal |b|.
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+        WriteMessage(a, kTestMessageToB);
+      },
+      w, a));
+
+  uintptr_t readable_b_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoHandle w, MojoHandle b,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ(kTestMessageToB, ReadMessage(b));
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+        event->Signal();
+      },
+      &event, w, b));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  // Send a message to |a|. The relevant watch context should be notified, and
+  // should in turn send a message to |b|, waking up the other context. The
+  // second context signals |event|.
+  WriteMessage(b, kTestMessageToA);
+  event.Wait();
+}
+
+TEST_F(WatcherTest, NotifySelfFromNotificationCallback) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA[] = "hello a";
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WatchHelper helper;
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  int expected_notifications = 10;
+  uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](int* expected_count, MojoHandle w, MojoHandle a, MojoHandle b,
+         base::WaitableEvent* event, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ("hello a", ReadMessage(a));
+
+        EXPECT_GT(*expected_count, 0);
+        *expected_count -= 1;
+        if (*expected_count == 0) {
+          event->Signal();
+          return;
+        } else {
+          // Re-arm the watcher and signal |a| again.
+          EXPECT_EQ(MOJO_RESULT_OK,
+                    MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+          WriteMessage(b, kTestMessageToA);
+        }
+      },
+      &expected_notifications, w, a, b, &event));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  // Send a message to |a|. When the watch above is notified, it will rearm and
+  // send another message to |a|. This will happen until
+  // |expected_notifications| reaches 0.
+  WriteMessage(b, kTestMessageToA);
+  event.Wait();
+}
+
+TEST_F(WatcherTest, ImplicitCancelOtherFromNotificationCallback) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle c, d;
+  CreateMessagePipe(&c, &d);
+
+  static const char kTestMessageToA[] = "hi a";
+  static const char kTestMessageToC[] = "hi c";
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WatchHelper helper;
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  uintptr_t readable_a_context = helper.CreateContextWithCancel(
+      base::Bind([](MojoResult result, MojoHandleSignalsState state) {
+        NOTREACHED();
+      }),
+      base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event));
+
+  uintptr_t readable_c_context = helper.CreateContext(base::Bind(
+      [](MojoHandle w, MojoHandle a, MojoHandle b, MojoHandle c,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ(kTestMessageToC, ReadMessage(c));
+
+        // Now rearm the watcher.
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // Must result in exactly ONE notification on the above context, for
+        // CANCELLED only. Because we cannot dispatch notifications until the
+        // stack unwinds, and because we must never dispatch non-cancellation
+        // notifications for a handle once it's been closed, we must be certain
+        // that cancellation due to closure preemptively invalidates any
+        // pending non-cancellation notifications queued on the current
+        // RequestContext, such as the one resulting from the WriteMessage here.
+        WriteMessage(b, kTestMessageToA);
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+
+        // Rearming should be fine since |a|'s watch should already be
+        // implicitly cancelled (even though the notification will not have
+        // been invoked yet.)
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // Nothing interesting should happen as a result of this.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+      },
+      w, a, b, c));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(d, kTestMessageToC);
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(WatcherTest, ExplicitCancelOtherFromNotificationCallback) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle c, d;
+  CreateMessagePipe(&c, &d);
+
+  static const char kTestMessageToA[] = "hi a";
+  static const char kTestMessageToC[] = "hi c";
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WatchHelper helper;
+  MojoHandle w;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); }));
+
+  uintptr_t readable_c_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, uintptr_t readable_a_context, MojoHandle w,
+         MojoHandle a, MojoHandle b, MojoHandle c, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ(kTestMessageToC, ReadMessage(c));
+
+        // Now rearm the watcher.
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // Should result in no notifications on the above context, because the
+        // watch will have been cancelled by the time the notification callback
+        // can execute.
+        WriteMessage(b, kTestMessageToA);
+        WriteMessage(b, kTestMessageToA);
+        EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context));
+
+        // Rearming should be fine now.
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // Nothing interesting should happen as a result of these.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+
+        event->Signal();
+      },
+      &event, readable_a_context, w, a, b, c));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(d, kTestMessageToC);
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(WatcherTest, NestedCancellation) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle c, d;
+  CreateMessagePipe(&c, &d);
+
+  static const char kTestMessageToA[] = "hey a";
+  static const char kTestMessageToC[] = "hey c";
+  static const char kTestMessageToD[] = "hey d";
+
+  // This is a tricky test. It establishes a watch on |b| using one watcher and
+  // watches on |c| and |d| using another watcher.
+  //
+  // A message is written to |d| to wake up |c|'s watch, and the notification
+  // handler for that event does the following:
+  //   1. Writes to |a| to eventually wake up |b|'s watcher.
+  //   2. Rearms |c|'s watcher.
+  //   3. Writes to |d| to eventually wake up |c|'s watcher again.
+  //
+  // Meanwhile, |b|'s watch notification handler cancels |c|'s watch altogether
+  // before writing to |c| to wake up |d|.
+  //
+  // The net result should be that |c|'s context only gets notified once (from
+  // the first write to |d| above) and everyone else gets notified as expected.
+
+  MojoHandle b_watcher;
+  MojoHandle cd_watcher;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&b_watcher));
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&cd_watcher));
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  uintptr_t readable_d_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoHandle d, MojoResult result,
+         MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ(kTestMessageToD, ReadMessage(d));
+        event->Signal();
+      },
+      &event, d));
+
+  static int num_expected_c_notifications = 1;
+  uintptr_t readable_c_context = helper.CreateContext(base::Bind(
+      [](MojoHandle cd_watcher, MojoHandle a, MojoHandle c, MojoHandle d,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_GT(num_expected_c_notifications--, 0);
+
+        // Trigger an eventual |readable_b_context| notification.
+        WriteMessage(a, kTestMessageToA);
+
+        EXPECT_EQ(kTestMessageToC, ReadMessage(c));
+        EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr,
+                                                 nullptr, nullptr));
+
+        // Trigger another eventual |readable_c_context| notification.
+        WriteMessage(d, kTestMessageToC);
+      },
+      cd_watcher, a, c, d));
+
+  uintptr_t readable_b_context = helper.CreateContext(base::Bind(
+      [](MojoHandle cd_watcher, uintptr_t readable_c_context, MojoHandle c,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoCancelWatch(cd_watcher, readable_c_context));
+
+        EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr,
+                                                 nullptr, nullptr));
+
+        WriteMessage(c, kTestMessageToD);
+      },
+      cd_watcher, readable_c_context, c));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE,
+                                      readable_b_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(cd_watcher, c, MOJO_HANDLE_SIGNAL_READABLE,
+                      readable_c_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(cd_watcher, d, MOJO_HANDLE_SIGNAL_READABLE,
+                      readable_d_context));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(b_watcher, nullptr, nullptr, nullptr, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(cd_watcher, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(d, kTestMessageToC);
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(cd_watcher));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_watcher));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(WatcherTest, CancelSelfInNotificationCallback) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA[] = "hey a";
+
+  MojoHandle w;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  static uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoHandle w, MojoHandle a,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+
+        // There should be no problem cancelling this watch from its own
+        // notification invocation.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context));
+        EXPECT_EQ(kTestMessageToA, ReadMessage(a));
+
+        // Arming should fail because there are no longer any registered
+        // watches on the watcher.
+        EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // And closing |a| should be fine (and should not invoke this
+        // notification with MOJO_RESULT_CANCELLED) for the same reason.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+
+        event->Signal();
+      },
+      &event, w, a));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(b, kTestMessageToA);
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, CloseWatcherInNotificationCallback) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA1[] = "hey a";
+  static const char kTestMessageToA2[] = "hey a again";
+
+  MojoHandle w;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, MojoHandle b,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ(kTestMessageToA1, ReadMessage(a));
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // There should be no problem closing this watcher from its own
+        // notification callback.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+
+        // And these should not trigger more notifications, because |w| has been
+        // closed already.
+        WriteMessage(b, kTestMessageToA2);
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+
+        event->Signal();
+      },
+      &event, w, a, b));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(b, kTestMessageToA1);
+  event.Wait();
+}
+
+TEST_F(WatcherTest, CloseWatcherAfterImplicitCancel) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA[] = "hey a";
+
+  MojoHandle w;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  uintptr_t readable_a_context = helper.CreateContext(base::Bind(
+      [](base::WaitableEvent* event, MojoHandle w, MojoHandle a,
+         MojoResult result, MojoHandleSignalsState state) {
+        EXPECT_EQ(MOJO_RESULT_OK, result);
+        EXPECT_EQ(kTestMessageToA, ReadMessage(a));
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+        // This will cue up a notification for |MOJO_RESULT_CANCELLED|...
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+
+        // ...but it should never fire because we close the watcher here.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+
+        event->Signal();
+      },
+      &event, w, a));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(b, kTestMessageToA);
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+}
+
+TEST_F(WatcherTest, OtherThreadCancelDuringNotification) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA[] = "hey a";
+
+  MojoHandle w;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  base::WaitableEvent wait_for_notification(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  base::WaitableEvent wait_for_cancellation(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  static bool callback_done = false;
+  uintptr_t readable_a_context = helper.CreateContextWithCancel(
+      base::Bind(
+          [](base::WaitableEvent* wait_for_notification, MojoHandle w,
+             MojoHandle a, MojoResult result, MojoHandleSignalsState state) {
+            EXPECT_EQ(MOJO_RESULT_OK, result);
+            EXPECT_EQ(kTestMessageToA, ReadMessage(a));
+
+            wait_for_notification->Signal();
+
+            // Give the other thread sufficient time to race with the completion
+            // of this callback. There should be no race, since the cancellation
+            // notification must be mutually exclusive to this notification.
+            base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+
+            callback_done = true;
+          },
+          &wait_for_notification, w, a),
+      base::Bind(
+          [](base::WaitableEvent* wait_for_cancellation) {
+            EXPECT_TRUE(callback_done);
+            wait_for_cancellation->Signal();
+          },
+          &wait_for_cancellation));
+
+  ThreadedRunner runner(base::Bind(
+      [](base::WaitableEvent* wait_for_notification,
+         base::WaitableEvent* wait_for_cancellation, MojoHandle w,
+         uintptr_t readable_a_context) {
+        wait_for_notification->Wait();
+
+        // Cancel the watch while the notification is still running.
+        EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context));
+
+        wait_for_cancellation->Wait();
+
+        EXPECT_TRUE(callback_done);
+      },
+      &wait_for_notification, &wait_for_cancellation, w, readable_a_context));
+  runner.Start();
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr));
+
+  WriteMessage(b, kTestMessageToA);
+  runner.Join();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+}
+
+TEST_F(WatcherTest, WatchesCancelEachOtherFromNotifications) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  static const char kTestMessageToA[] = "hey a";
+  static const char kTestMessageToB[] = "hey b";
+
+  base::WaitableEvent wait_for_a_to_notify(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::WaitableEvent wait_for_b_to_notify(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::WaitableEvent wait_for_a_to_cancel(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::WaitableEvent wait_for_b_to_cancel(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  MojoHandle a_watcher;
+  MojoHandle b_watcher;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&a_watcher));
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&b_watcher));
+
+  // We set up two watchers, one on |a| and one on |b|. They cancel each other
+  // from within their respective watch notifications. This should be safe,
+  // i.e., it should not deadlock, in spite of the fact that we also guarantee
+  // mutually exclusive notification execution (including cancellations) on any
+  // given watch.
+  bool a_cancelled = false;
+  bool b_cancelled = false;
+  static uintptr_t readable_b_context;
+  uintptr_t readable_a_context = helper.CreateContextWithCancel(
+      base::Bind(
+          [](base::WaitableEvent* wait_for_a_to_notify,
+             base::WaitableEvent* wait_for_b_to_notify, MojoHandle b_watcher,
+             MojoHandle a, MojoResult result, MojoHandleSignalsState state) {
+            EXPECT_EQ(MOJO_RESULT_OK, result);
+            EXPECT_EQ(kTestMessageToA, ReadMessage(a));
+            wait_for_a_to_notify->Signal();
+            wait_for_b_to_notify->Wait();
+            EXPECT_EQ(MOJO_RESULT_OK,
+                      MojoCancelWatch(b_watcher, readable_b_context));
+            EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_watcher));
+          },
+          &wait_for_a_to_notify, &wait_for_b_to_notify, b_watcher, a),
+      base::Bind(
+          [](base::WaitableEvent* wait_for_a_to_cancel,
+             base::WaitableEvent* wait_for_b_to_cancel, bool* a_cancelled) {
+            *a_cancelled = true;
+            wait_for_a_to_cancel->Signal();
+            wait_for_b_to_cancel->Wait();
+          },
+          &wait_for_a_to_cancel, &wait_for_b_to_cancel, &a_cancelled));
+
+  readable_b_context = helper.CreateContextWithCancel(
+      base::Bind(
+          [](base::WaitableEvent* wait_for_a_to_notify,
+             base::WaitableEvent* wait_for_b_to_notify,
+             uintptr_t readable_a_context, MojoHandle a_watcher, MojoHandle b,
+             MojoResult result, MojoHandleSignalsState state) {
+            EXPECT_EQ(MOJO_RESULT_OK, result);
+            EXPECT_EQ(kTestMessageToB, ReadMessage(b));
+            wait_for_b_to_notify->Signal();
+            wait_for_a_to_notify->Wait();
+            EXPECT_EQ(MOJO_RESULT_OK,
+                      MojoCancelWatch(a_watcher, readable_a_context));
+            EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_watcher));
+          },
+          &wait_for_a_to_notify, &wait_for_b_to_notify, readable_a_context,
+          a_watcher, b),
+      base::Bind(
+          [](base::WaitableEvent* wait_for_a_to_cancel,
+             base::WaitableEvent* wait_for_b_to_cancel, bool* b_cancelled) {
+            *b_cancelled = true;
+            wait_for_b_to_cancel->Signal();
+            wait_for_a_to_cancel->Wait();
+          },
+          &wait_for_a_to_cancel, &wait_for_b_to_cancel, &b_cancelled));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a_watcher, a, MOJO_HANDLE_SIGNAL_READABLE,
+                                      readable_a_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(a_watcher, nullptr, nullptr, nullptr, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE,
+                                      readable_b_context));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoArmWatcher(b_watcher, nullptr, nullptr, nullptr, nullptr));
+
+  ThreadedRunner runner(
+      base::Bind([](MojoHandle b) { WriteMessage(b, kTestMessageToA); }, b));
+  runner.Start();
+
+  WriteMessage(a, kTestMessageToB);
+
+  wait_for_a_to_cancel.Wait();
+  wait_for_b_to_cancel.Wait();
+  runner.Join();
+
+  EXPECT_TRUE(a_cancelled);
+  EXPECT_TRUE(b_cancelled);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+}
+
+TEST_F(WatcherTest, AlwaysCancel) {
+  // Basic sanity check to ensure that all possible ways to cancel a watch
+  // result in a final MOJO_RESULT_CANCELLED notification.
+
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle w;
+  WatchHelper helper;
+  EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w));
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  const base::Closure signal_event =
+      base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event));
+
+  // Cancel via |MojoCancelWatch()|.
+  uintptr_t context = helper.CreateContextWithCancel(
+      WatchHelper::ContextCallback(), signal_event);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, context));
+  event.Wait();
+  event.Reset();
+
+  // Cancel by closing the watched handle.
+  context = helper.CreateContextWithCancel(WatchHelper::ContextCallback(),
+                                           signal_event);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  event.Wait();
+  event.Reset();
+
+  // Cancel by closing the watcher handle.
+  context = helper.CreateContextWithCancel(WatchHelper::ContextCallback(),
+                                           signal_event);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, context));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w));
+  event.Wait();
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
index c3b3d5f..7b971e0 100644
--- a/mojo/public/c/system/BUILD.gn
+++ b/mojo/public/c/system/BUILD.gn
@@ -18,6 +18,7 @@
     "thunks.h",
     "types.h",
     "wait_set.h",
+    "watcher.h",
   ]
 
   defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
diff --git a/mojo/public/c/system/core.h b/mojo/public/c/system/core.h
index 77d452b0..80a2e9a 100644
--- a/mojo/public/c/system/core.h
+++ b/mojo/public/c/system/core.h
@@ -18,5 +18,6 @@
 #include "mojo/public/c/system/system_export.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/c/system/wait_set.h"
+#include "mojo/public/c/system/watcher.h"
 
 #endif  // MOJO_PUBLIC_C_SYSTEM_CORE_H_
diff --git a/mojo/public/c/system/functions.h b/mojo/public/c/system/functions.h
index f0a23d9..750a29ee 100644
--- a/mojo/public/c/system/functions.h
+++ b/mojo/public/c/system/functions.h
@@ -19,14 +19,6 @@
 extern "C" {
 #endif
 
-// A callback used to notify watchers registered via |MojoWatch()|. Called when
-// some watched signals are satisfied or become unsatisfiable. See the
-// documentation for |MojoWatch()| for more details.
-typedef void (*MojoWatchCallback)(uintptr_t context,
-                                  MojoResult result,
-                                  struct MojoHandleSignalsState signals_state,
-                                  MojoWatchNotificationFlags flags);
-
 // Note: Pointer parameters that are labelled "optional" may be null (at least
 // under some circumstances). Non-const pointer parameters are also labeled
 // "in", "out", or "in/out", to indicate how they are used. (Note that how/if
@@ -138,74 +130,6 @@
              uint32_t* result_index,                          // Optional out
              struct MojoHandleSignalsState* signals_states);  // Optional out
 
-// Watches the given handle for one of the following events to happen:
-//   - A signal indicated by |signals| is satisfied.
-//   - It becomes known that no signal indicated by |signals| will ever be
-//     satisfied. (See the description of the |MOJO_RESULT_CANCELLED| and
-//     |MOJO_RESULT_FAILED_PRECONDITION| return values below.)
-//   - The handle is closed.
-//
-// |handle|: The handle to watch. Must be an open message pipe or data pipe
-//     handle.
-// |signals|: The signals to watch for.
-// |callback|: A function to be called any time one of the above events happens.
-//     The function must be safe to call from any thread at any time.
-// |context|: User-provided context passed to |callback| when called. |context|
-//     is used to uniquely identify a registered watch and can be used to cancel
-//     the watch later using |MojoCancelWatch()|.
-//
-// Returns:
-//   |MOJO_RESULT_OK| if the watch has been successfully registered. Note that
-//       if the signals are already satisfied this may synchronously invoke
-//       |callback| before returning.
-//   |MOJO_RESULT_CANCELLED| if the watch was cancelled. In this case it is not
-//       necessary to explicitly call |MojoCancelWatch()|, and in fact it may be
-//       an error to do so as the handle may have been closed.
-//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not an open message pipe
-//       handle.
-//   |MOJO_RESULT_FAILED_PRECONDITION| if it is already known that |signals| can
-//       never be satisfied.
-//   |MOJO_RESULT_ALREADY_EXISTS| if there is already a watch registered for
-//       the same combination of |handle| and |context|.
-//
-// Callback result codes:
-//   The callback may be called at any time on any thread with one of the
-//   following result codes to indicate various events:
-//
-//   |MOJO_RESULT_OK| indicates that some signal in |signals| has been
-//       satisfied.
-//   |MOJO_RESULT_FAILED_PRECONDITION| indicates that no signals in |signals|
-//       can ever be satisfied again.
-//   |MOJO_RESULT_CANCELLED| indicates that the handle has been closed. In this
-//       case the watch is implicitly cancelled and there is no need to call
-//       |MojoCancelWatch()|.
-MOJO_SYSTEM_EXPORT MojoResult
-MojoWatch(MojoHandle handle,
-          MojoHandleSignals signals,
-          MojoWatchCallback callback,
-          uintptr_t context);
-
-// Cancels a handle watch corresponding to some prior call to |MojoWatch()|.
-//
-// NOTE: If the watch callback corresponding to |context| is currently running
-// this will block until the callback completes execution. It is therefore
-// illegal to call |MojoCancelWatch()| on a given |handle| and |context| from
-// within the associated callback itself, as this will always deadlock.
-//
-// After |MojoCancelWatch()| function returns, the watch's associated callback
-// will NEVER be called again by Mojo.
-//
-// |context|: The same user-provided context given to some prior call to
-//     |MojoWatch()|. Only the watch corresponding to this context will be
-//     cancelled.
-//
-// Returns:
-//     |MOJO_RESULT_OK| if the watch corresponding to |context| was cancelled.
-//     |MOJO_RESULT_INVALID_ARGUMENT| if no watch was registered with |context|
-//         for the given |handle|, or if |handle| is invalid.
-MOJO_SYSTEM_EXPORT MojoResult
-MojoCancelWatch(MojoHandle handle, uintptr_t context);
-
 // Retrieves system properties. See the documentation for |MojoPropertyType| for
 // supported property types and their corresponding output value type.
 //
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc
index d6bfd95..1e92954 100644
--- a/mojo/public/c/system/thunks.cc
+++ b/mojo/public/c/system/thunks.cc
@@ -185,17 +185,33 @@
                                   signals_states);
 }
 
-MojoResult MojoWatch(MojoHandle handle,
-                     MojoHandleSignals signals,
-                     MojoWatchCallback callback,
-                     uintptr_t context) {
-  assert(g_thunks.Watch);
-  return g_thunks.Watch(handle, signals, callback, context);
+MojoResult MojoCreateWatcher(MojoWatcherCallback callback,
+                             MojoHandle* watcher_handle) {
+  assert(g_thunks.CreateWatcher);
+  return g_thunks.CreateWatcher(callback, watcher_handle);
 }
 
-MojoResult MojoCancelWatch(MojoHandle handle, uintptr_t context) {
+MojoResult MojoWatch(MojoHandle watcher_handle,
+                     MojoHandle handle,
+                     MojoHandleSignals signals,
+                     uintptr_t context) {
+  assert(g_thunks.Watch);
+  return g_thunks.Watch(watcher_handle, handle, signals, context);
+}
+
+MojoResult MojoCancelWatch(MojoHandle watcher_handle, uintptr_t context) {
   assert(g_thunks.CancelWatch);
-  return g_thunks.CancelWatch(handle, context);
+  return g_thunks.CancelWatch(watcher_handle, context);
+}
+
+MojoResult MojoArmWatcher(MojoHandle watcher_handle,
+                          uint32_t* num_ready_contexts,
+                          uintptr_t* ready_contexts,
+                          MojoResult* ready_results,
+                          MojoHandleSignalsState* ready_signals_states) {
+  assert(g_thunks.ArmWatcher);
+  return g_thunks.ArmWatcher(watcher_handle, num_ready_contexts, ready_contexts,
+                             ready_results, ready_signals_states);
 }
 
 MojoResult MojoFuseMessagePipes(MojoHandle handle0, MojoHandle handle1) {
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h
index 161faf1..31d2289 100644
--- a/mojo/public/c/system/thunks.h
+++ b/mojo/public/c/system/thunks.h
@@ -13,28 +13,10 @@
 #include "mojo/public/c/system/core.h"
 #include "mojo/public/c/system/system_export.h"
 
-// The embedder needs to bind the basic Mojo Core functions of a DSO to those of
-// the embedder when loading a DSO that is dependent on mojo_system.
-// The typical usage would look like:
-// base::ScopedNativeLibrary app_library(
-//     base::LoadNativeLibrary(app_path_, &error));
-// typedef MojoResult (*MojoSetSystemThunksFn)(MojoSystemThunks*);
-// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
-//     reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
-//         "MojoSetSystemThunks"));
-// MojoSystemThunks system_thunks = MojoMakeSystemThunks();
-// size_t expected_size = mojo_set_system_thunks_fn(&system_thunks);
-// if (expected_size > sizeof(MojoSystemThunks)) {
-//   LOG(ERROR)
-//       << "Invalid DSO. Expected MojoSystemThunks size: "
-//       << expected_size;
-//   break;
-// }
-
-// Structure used to bind the basic Mojo Core functions of a DSO to those of
-// the embedder.
-// This is the ABI between the embedder and the DSO. It can only have new
-// functions added to the end. No other changes are supported.
+// Structure used to bind the basic Mojo Core functions to an embedder
+// implementation. This is intended to eventually be used as a stable ABI
+// between a Mojo embedder and some loaded application code, but for now it is
+// still effectively safe to rearrange entries as needed.
 #pragma pack(push, 8)
 struct MojoSystemThunks {
   size_t size;  // Should be set to sizeof(MojoSystemThunks).
@@ -115,11 +97,18 @@
                                 MojoHandle* handles,
                                 MojoResult* results,
                                 struct MojoHandleSignalsState* signals_states);
-  MojoResult (*Watch)(MojoHandle handle,
+  MojoResult (*CreateWatcher)(MojoWatcherCallback callback,
+                              MojoHandle* watcher_handle);
+  MojoResult (*Watch)(MojoHandle watcher_handle,
+                      MojoHandle handle,
                       MojoHandleSignals signals,
-                      MojoWatchCallback callback,
                       uintptr_t context);
-  MojoResult (*CancelWatch)(MojoHandle handle, uintptr_t context);
+  MojoResult (*CancelWatch)(MojoHandle watcher_handle, uintptr_t context);
+  MojoResult (*ArmWatcher)(MojoHandle watcher_handle,
+                           uint32_t* num_ready_contexts,
+                           uintptr_t* ready_contexts,
+                           MojoResult* ready_results,
+                           MojoHandleSignalsState* ready_signals_states);
   MojoResult (*FuseMessagePipes)(MojoHandle handle0, MojoHandle handle1);
   MojoResult (*WriteMessageNew)(MojoHandle message_pipe_handle,
                                 MojoMessageHandle message,
diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h
index 7e02eeb..967ce15 100644
--- a/mojo/public/c/system/types.h
+++ b/mojo/public/c/system/types.h
@@ -189,24 +189,25 @@
 MOJO_STATIC_ASSERT(sizeof(MojoHandleSignalsState) == 8,
                    "MojoHandleSignalsState has wrong size");
 
-// |MojoWatchNotificationFlags|: Passed to a callback invoked as a result of
-// signals being raised on a handle watched by |MojoWatch()|. May take the
-// following values:
-//   |MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM| - The callback is being invoked
-//       as a result of a system-level event rather than a direct API call from
-//       user code. This may be used as an indication that user code is safe to
-//       call without fear of reentry.
+// |MojoWatcherNotificationFlags|: Passed to a callback invoked by a watcher
+// when some observed signals are raised or a watched handle is closed. May take
+// on any combination of the following values:
+//
+//   |MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM| - The callback is being
+//       invoked as a result of a system-level event rather than a direct API
+//       call from user code. This may be used as an indication that user code
+//       is safe to call without fear of reentry.
 
-typedef uint32_t MojoWatchNotificationFlags;
+typedef uint32_t MojoWatcherNotificationFlags;
 
 #ifdef __cplusplus
-const MojoWatchNotificationFlags MOJO_WATCH_NOTIFICATION_FLAG_NONE = 0;
-const MojoWatchNotificationFlags MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM =
+const MojoWatcherNotificationFlags MOJO_WATCHER_NOTIFICATION_FLAG_NONE = 0;
+const MojoWatcherNotificationFlags MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM =
     1 << 0;
 #else
-#define MOJO_WATCH_NOTIFICATION_FLAG_NONE ((MojoWatchNotificationFlags)0)
-#define MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM \
-    ((MojoWatchNotificationFlags)1 << 0);
+#define MOJO_WATCHER_NOTIFICATION_FLAG_NONE ((MojoWatcherNotificationFlags)0)
+#define MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM \
+  ((MojoWatcherNotificationFlags)1 << 0);
 #endif
 
 // |MojoPropertyType|: Property types that can be passed to |MojoGetProperty()|
diff --git a/mojo/public/c/system/watcher.h b/mojo/public/c/system/watcher.h
new file mode 100644
index 0000000..f152c50
--- /dev/null
+++ b/mojo/public/c/system/watcher.h
@@ -0,0 +1,183 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_WATCHER_H_
+#define MOJO_PUBLIC_C_SYSTEM_WATCHER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// A callback used to notify watchers about events on their watched handles.
+//
+// See documentation for |MojoWatcherNotificationFlags| for details regarding
+// the possible values of |flags|.
+//
+// See documentation for |MojoWatch()| for details regarding the other arguments
+// this callback receives when called.
+typedef void (*MojoWatcherCallback)(uintptr_t context,
+                                    MojoResult result,
+                                    struct MojoHandleSignalsState signals_state,
+                                    MojoWatcherNotificationFlags flags);
+
+// Creates a new watcher.
+//
+// Watchers are used to trigger arbitrary code execution when one or more
+// handles change state to meet certain conditions.
+//
+// A newly registered watcher is initially disarmed and may be armed using
+// |MojoArmWatcher()|. A watcher is also always disarmed immediately before any
+// invocation of one or more notification callbacks in response to a single
+// handle's state changing in some relevant way.
+//
+// Parameters:
+//   |callback|: The |MojoWatcherCallback| to invoke any time the watcher is
+//       notified of an event. See |MojoWatch()| for details regarding arguments
+//       passed to the callback. Note that this may be called from any arbitrary
+//       thread.
+//   |watcher_handle|: The address at which to store the MojoHandle
+//       corresponding to the new watcher if successfully created.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the watcher has been successfully created.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a handle could not be allocated for
+//       this watcher.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateWatcher(MojoWatcherCallback callback,
+                                                MojoHandle* watcher_handle);
+
+// Adds a watch to a watcher. This allows the watcher to fire notifications
+// regarding state changes on the handle corresponding to the arguments given.
+//
+// Note that notifications for a given watch context are guaranteed to be
+// mutually exclusive in execution: the callback will never be entered for a
+// given context while another invocation of the callback is still executing for
+// the same context. As a result it is generally a good idea to ensure that
+// callbacks do as little work as necessary in order to process the
+// notification.
+//
+// Parameters:
+//   |watcher_handle|: The watcher to which |handle| is to be added.
+//   |handle|: The handle to add to the watcher.
+//   |signals|: The signals to watch for on |handle|.
+//   |context|: An arbitrary context value given to any invocation of the
+//       watcher's callback when invoked as a result of some state change
+//       relevant to this combination of |handle| and |signals|. Must be
+//       unique within any given watcher.
+//
+// Callback parameters (see |MojoWatcherNotificationCallback| above):
+//   When the watcher invokes its callback as a result of some notification
+//   relevant to this watch operation, |context| receives the value given here
+//   and |signals_state| receives the last known signals state of this handle.
+//
+//   |result| is one of the following:
+//     |MOJO_RESULT_OK| if at least one of the watched signals is satisfied. The
+//         watcher must be armed for this notification to fire.
+//     |MOJO_RESULT_FAILED_PRECONDITION| if all of the watched signals are
+//         permanently unsatisfiable. The watcher must be armed for this
+//         notification to fire.
+//     |MOJO_RESULT_CANCELLED| if the watch has been cancelled. The may occur if
+//         the watcher has been closed, the watched handle has been closed, or
+//         the watch for |context| has been explicitly cancelled. This is always
+//         the last result received for any given context, and it is guaranteed
+//         to be received exactly once per watch, regardless of how the watch
+//         was cancelled.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the handle is now being watched by the watcher.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |watcher_handle| is not a watcher handle,
+//       |handle| is not a valid message pipe or data pipe handle.
+//   |MOJO_RESULT_ALREADY_EXISTS| if the watcher already has a watch registered
+//       for the given value of |context| or for the given |handle|.
+MOJO_SYSTEM_EXPORT MojoResult MojoWatch(MojoHandle watcher_handle,
+                                        MojoHandle handle,
+                                        MojoHandleSignals signals,
+                                        uintptr_t context);
+
+// Removes a watch from a watcher.
+//
+// This ensures that the watch is cancelled as soon as possible. Cancellation
+// may be deferred (or may even block) an aritrarily long time if the watch is
+// already dispatching one or more notifications.
+//
+// When cancellation is complete, the watcher's callback is invoked one final
+// time for |context|, with the result |MOJO_RESULT_CANCELLED|.
+//
+// The same behavior can be elicted by either closing the watched handle
+// associated with this context, or by closing |watcher_handle| itself. In the
+// lastter case, all registered contexts on the watcher are implicitly cancelled
+// in a similar fashion.
+//
+// Parameters:
+//   |watcher_handle|: The handle of the watcher from which to remove a watch.
+//   |context|: The context of the watch to be removed.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the watch has been cancelled.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |watcher_handle| is not a watcher handle.
+//   |MOJO_RESULT_NOT_FOUND| if there is no watch registered on this watcher for
+//       the given value of |context|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCancelWatch(MojoHandle watcher_handle,
+                                              uintptr_t context);
+
+// Arms a watcher, enabling a single future event on one of the watched handles
+// to trigger a single notification for each relevant watch context associated
+// with that handle.
+//
+// Parameters:
+//   |watcher_handle|: The handle of the watcher.
+//   |num_ready_contexts|: An address pointing to the number of elements
+//       available for storage in the remaining output buffers. Optional and
+//       only used on failure. See |MOJO_RESULT_FAILED_PRECONDITION| below for
+//       more details.
+//   |ready_contexts|: An output buffer for contexts corresponding to the
+//       watches which would have notified if the watcher were armed. Optional
+//       and only uesd on failure. See |MOJO_RESULT_FAILED_PRECONDITION| below
+//       for more details.
+//   |ready_results|: An output buffer for MojoResult values corresponding to
+//       each context in |ready_contexts|. Optional and only used on failure.
+//       See |MOJO_RESULT_FAILED_PRECONDITION| below for more details.
+//   |ready_signals_states|: An output buffer for |MojoHandleSignalsState|
+//       structures corresponding to each context in |ready_contexts|. Optional
+//       and only used on failure. See |MOJO_RESULT_FAILED_PRECONDITION| below
+//       for more details.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the watcher has been successfully armed. All arguments
+//       other than |watcher_handle| are ignored in this case.
+//   |MOJO_RESULT_NOT_FOUND| if the watcher does not have any registered watch
+//       contexts. All arguments other than |watcher_handle| are ignored in this
+//       case.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |watcher_handle| is not a valid watcher
+//       handle, or if |num_ready_contexts| is non-null but any of the output
+//       buffer paramters is null.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if one or more watches would have
+//       notified immediately upon arming the watcher. If |num_handles| is
+//       non-null, this assumes there is enough space for |*num_handles| entries
+//       in each of the subsequent output buffer arguments.
+//
+//       At most that many entries are placed in the output buffers,
+//       corresponding to the watches which would have signalled if the watcher
+//       had been armed successfully. The actual number of entries placed in the
+//       output buffers is written to |*num_ready_contexts| before returning.
+//
+//       If more than (input) |*num_ready_contexts| watch contexts were ready to
+//       notify, the subset presented in output buffers is arbitrary and
+//       implementation-defined.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoArmWatcher(MojoHandle watcher_handle,
+               uint32_t* num_ready_contexts,
+               uintptr_t* ready_contexts,
+               MojoResult* ready_results,
+               struct MojoHandleSignalsState* ready_signals_states);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_WATCHER_H_
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
index 1da331b..ba5954c2 100644
--- a/mojo/public/cpp/bindings/binding.h
+++ b/mojo/public/cpp/bindings/binding.h
@@ -208,6 +208,10 @@
     internal_state_.CloseWithReason(custom_reason, description);
   }
 
+  void EnableNestedDispatch(bool enabled) {
+    internal_state_.EnableNestedDispatch(enabled);
+  }
+
   // Unbinds the underlying pipe from this binding and returns it so it can be
   // used in another context, such as on another thread or with a different
   // implementation. Put this object into a state where it can be rebound to a
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 01e92367..fdfeb73 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -18,7 +18,7 @@
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
 #include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/watcher.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 
 namespace base {
 class Lock;
@@ -155,8 +155,14 @@
   // |tag| must be a const string literal.
   void SetWatcherHeapProfilerTag(const char* tag);
 
+  // Enables support for nested message dispatch so that the Connector can
+  // continue dispatching inbound messages even if one of them spins a nested
+  // message loop. This should be enabled only when needed, as dispatch in this
+  // mode is generally less efficient.
+  void EnableNestedDispatch(bool enabled);
+
  private:
-  // Callback of mojo::Watcher.
+  // Callback of mojo::SimpleWatcher.
   void OnWatcherHandleReady(MojoResult result);
   // Callback of SyncHandleWatcher.
   void OnSyncHandleWatcherHandleReady(MojoResult result);
@@ -188,7 +194,7 @@
   MessageReceiver* incoming_receiver_ = nullptr;
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  std::unique_ptr<Watcher> handle_watcher_;
+  std::unique_ptr<SimpleWatcher> handle_watcher_;
 
   bool error_ = false;
   bool drop_writes_ = false;
@@ -196,6 +202,8 @@
 
   bool paused_ = false;
 
+  bool nested_dispatch_enabled_ = false;
+
   // If sending messages is allowed from multiple threads, |lock_| is used to
   // protect modifications to |message_pipe_| and |drop_writes_|.
   base::Optional<base::Lock> lock_;
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
index e88be74..a56b9dd 100644
--- a/mojo/public/cpp/bindings/interface_ptr.h
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -193,6 +193,10 @@
     return !(*this) && !other;
   }
 
+  void EnableNestedDispatch(bool enabled) {
+    internal_state_.EnableNestedDispatch(enabled);
+  }
+
   // DO NOT USE. Exposed only for internal use and for testing.
   internal::InterfacePtrState<Interface>* internal_state() {
     return &internal_state_;
diff --git a/mojo/public/cpp/bindings/lib/binding_state.cc b/mojo/public/cpp/bindings/lib/binding_state.cc
index b34cb47..85392cd 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.cc
+++ b/mojo/public/cpp/bindings/lib/binding_state.cc
@@ -51,6 +51,10 @@
   Close();
 }
 
+void BindingStateBase::EnableNestedDispatch(bool enabled) {
+  router_->EnableNestedDispatch(enabled);
+}
+
 void BindingStateBase::FlushForTesting() {
   endpoint_client_->FlushForTesting();
 }
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index 0b0dbee0..ca7d5a11 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -68,6 +68,8 @@
     return router_->handle();
   }
 
+  void EnableNestedDispatch(bool enabled);
+
   void FlushForTesting();
 
   void EnableTestingMode();
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 4426def..ff9867a 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -188,6 +188,12 @@
   }
 }
 
+void Connector::EnableNestedDispatch(bool enabled) {
+  nested_dispatch_enabled_ = enabled;
+  handle_watcher_.reset();
+  WaitToReadMore();
+}
+
 void Connector::OnWatcherHandleReady(MojoResult result) {
   OnHandleReadyInternal(result);
 }
@@ -211,6 +217,7 @@
     HandleError(result != MOJO_RESULT_FAILED_PRECONDITION, false);
     return;
   }
+
   ReadAllAvailableMessages();
   // At this point, this object might have been deleted. Return.
 }
@@ -219,10 +226,11 @@
   CHECK(!paused_);
   DCHECK(!handle_watcher_);
 
-  handle_watcher_.reset(new Watcher(FROM_HERE, task_runner_));
+  handle_watcher_.reset(new SimpleWatcher(
+      FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, task_runner_));
   if (heap_profiler_tag_)
     handle_watcher_->set_heap_profiler_tag(heap_profiler_tag_);
-  MojoResult rv = handle_watcher_->Start(
+  MojoResult rv = handle_watcher_->Watch(
       message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::Bind(&Connector::OnWatcherHandleReady, base::Unretained(this)));
 
@@ -232,6 +240,8 @@
     task_runner_->PostTask(
         FROM_HERE,
         base::Bind(&Connector::OnWatcherHandleReady, weak_self_, rv));
+  } else {
+    handle_watcher_->ArmOrNotify();
   }
 
   if (allow_woken_up_by_others_) {
@@ -253,6 +263,13 @@
   const MojoResult rv = ReadMessage(message_pipe_.get(), &message);
   *read_result = rv;
 
+  if (nested_dispatch_enabled_) {
+    // When supporting nested dispatch, we have to rearm the Watcher immediately
+    // after reading each message (i.e. before dispatch) to ensure that the next
+    // inbound message can trigger OnHandleReady on the nested loop.
+    handle_watcher_->ArmOrNotify();
+  }
+
   if (rv == MOJO_RESULT_OK) {
     receiver_result =
         incoming_receiver_ && incoming_receiver_->Accept(&message);
@@ -278,19 +295,36 @@
 
 void Connector::ReadAllAvailableMessages() {
   while (!error_) {
+    base::WeakPtr<Connector> weak_self = weak_self_;
     MojoResult rv;
 
-    if (!ReadSingleMessage(&rv)) {
-      // Return immediately without touching any members. |this| may have been
-      // destroyed.
+    // May delete |this.|
+    if (!ReadSingleMessage(&rv))
       return;
+
+    if (!weak_self || paused_)
+      return;
+
+    DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_SHOULD_WAIT);
+
+    if (rv == MOJO_RESULT_SHOULD_WAIT) {
+      // Attempt to re-arm the Watcher.
+      MojoResult ready_result;
+      MojoResult arm_result = handle_watcher_->Arm(&ready_result);
+      if (arm_result == MOJO_RESULT_OK)
+        return;
+
+      // The watcher is already ready to notify again.
+      DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, arm_result);
+
+      if (ready_result == MOJO_RESULT_FAILED_PRECONDITION) {
+        HandleError(false, false);
+        return;
+      }
+
+      // There's more to read now, so we'll just keep looping.
+      DCHECK_EQ(MOJO_RESULT_OK, ready_result);
     }
-
-    if (paused_)
-      return;
-
-    if (rv == MOJO_RESULT_SHOULD_WAIT)
-      break;
   }
 }
 
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
index 8f5b4ff..d1cd3e6 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -150,6 +150,11 @@
     return endpoint_client_->associated_group();
   }
 
+  void EnableNestedDispatch(bool enabled) {
+    ConfigureProxyIfNecessary();
+    router_->EnableNestedDispatch(enabled);
+  }
+
   void EnableTestingMode() {
     ConfigureProxyIfNecessary();
     router_->EnableTestingMode();
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 2da459a..40d07887 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -605,6 +605,11 @@
   return !base::ContainsKey(endpoints_, kMasterInterfaceId);
 }
 
+void MultiplexRouter::EnableNestedDispatch(bool enabled) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  connector_.EnableNestedDispatch(enabled);
+}
+
 void MultiplexRouter::EnableTestingMode() {
   DCHECK(thread_checker_.CalledOnValidThread());
   MayAutoLock locker(&lock_);
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index cac138bcb..8f1f793 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -130,6 +130,8 @@
   // Whether there are any associated interfaces running currently.
   bool HasAssociatedEndpoints() const;
 
+  void EnableNestedDispatch(bool enabled);
+
   // Sets this object to testing mode.
   // In testing mode, the object doesn't disconnect the underlying message pipe
   // when it receives unexpected or invalid messages.
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 74ecb7a..a081d1d095 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -568,6 +568,7 @@
                                      run_loop.QuitClosure()));
   connector1.set_incoming_receiver(&accumulator);
 
+  connector1.EnableNestedDispatch(true);
   run_loop.Run();
 
   ASSERT_EQ(2u, accumulator.size());
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
index 0dc7af9d..2fc9075 100644
--- a/mojo/public/cpp/system/BUILD.gn
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -33,6 +33,8 @@
     "message_pipe.h",
     "platform_handle.cc",
     "platform_handle.h",
+    "simple_watcher.cc",
+    "simple_watcher.h",
     "system_export.h",
     "watcher.cc",
     "watcher.h",
diff --git a/mojo/public/cpp/system/simple_watcher.cc b/mojo/public/cpp/system/simple_watcher.cc
new file mode 100644
index 0000000..218a350f
--- /dev/null
+++ b/mojo/public/cpp/system/simple_watcher.cc
@@ -0,0 +1,273 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/simple_watcher.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/heap_profiler.h"
+#include "mojo/public/c/system/watcher.h"
+
+namespace mojo {
+
+// Thread-safe Context object used to dispatch watch notifications from a
+// arbitrary threads.
+class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> {
+ public:
+  // Creates a |Context| instance for a new watch on |watcher|, to watch
+  // |handle| for |signals|.
+  static scoped_refptr<Context> Create(
+      base::WeakPtr<SimpleWatcher> watcher,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      WatcherHandle watcher_handle,
+      Handle handle,
+      MojoHandleSignals signals,
+      MojoResult* watch_result) {
+    scoped_refptr<Context> context = new Context(watcher, task_runner);
+
+    // If MojoWatch succeeds, it assumes ownership of a reference to |context|.
+    // In that case, this reference is balanced in CallNotify() when |result| is
+    // |MOJO_RESULT_CANCELLED|.
+    context->AddRef();
+
+    *watch_result = MojoWatch(watcher_handle.value(), handle.value(), signals,
+                              context->value());
+    if (*watch_result != MOJO_RESULT_OK) {
+      // Balanced by the AddRef() above since watching failed.
+      context->Release();
+      return nullptr;
+    }
+
+    return context;
+  }
+
+  static void CallNotify(uintptr_t context_value,
+                         MojoResult result,
+                         MojoHandleSignalsState signals_state,
+                         MojoWatcherNotificationFlags flags) {
+    auto* context = reinterpret_cast<Context*>(context_value);
+    context->Notify(result, signals_state, flags);
+
+    // That was the last notification for the context. We can release the ref
+    // owned by the watch, which may in turn delete the Context.
+    if (result == MOJO_RESULT_CANCELLED)
+      context->Release();
+  }
+
+  uintptr_t value() const { return reinterpret_cast<uintptr_t>(this); }
+
+  void DisableCancellationNotifications() {
+    base::AutoLock lock(lock_);
+    enable_cancellation_notifications_ = false;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<Context>;
+
+  Context(base::WeakPtr<SimpleWatcher> weak_watcher,
+          scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : weak_watcher_(weak_watcher), task_runner_(task_runner) {}
+  ~Context() {}
+
+  void Notify(MojoResult result,
+              MojoHandleSignalsState signals_state,
+              MojoWatcherNotificationFlags flags) {
+    if (result == MOJO_RESULT_CANCELLED) {
+      // The SimpleWatcher may have explicitly cancelled this watch, so we don't
+      // bother dispatching the notification - it would be ignored anyway.
+      //
+      // TODO(rockot): This shouldn't really be necessary, but there are already
+      // instances today where bindings object may be bound and subsequently
+      // closed due to pipe error, all before the thread's TaskRunner has been
+      // properly initialized.
+      base::AutoLock lock(lock_);
+      if (!enable_cancellation_notifications_)
+        return;
+    }
+
+    if ((flags & MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM) &&
+        task_runner_->RunsTasksOnCurrentThread() && weak_watcher_ &&
+        weak_watcher_->is_default_task_runner_) {
+      // System notifications will trigger from the task runner passed to
+      // mojo::edk::InitIPCSupport(). In Chrome this happens to always be the
+      // default task runner for the IO thread.
+      weak_watcher_->OnHandleReady(make_scoped_refptr(this), result);
+    } else {
+      task_runner_->PostTask(
+          FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady, weak_watcher_,
+                                make_scoped_refptr(this), result));
+    }
+  }
+
+  const base::WeakPtr<SimpleWatcher> weak_watcher_;
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::Lock lock_;
+  bool enable_cancellation_notifications_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+SimpleWatcher::SimpleWatcher(const tracked_objects::Location& from_here,
+                             ArmingPolicy arming_policy,
+                             scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : arming_policy_(arming_policy),
+      task_runner_(std::move(runner)),
+      is_default_task_runner_(task_runner_ ==
+                              base::ThreadTaskRunnerHandle::Get()),
+      heap_profiler_tag_(from_here.file_name()),
+      weak_factory_(this) {
+  MojoResult rv = CreateWatcher(&Context::CallNotify, &watcher_handle_);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(task_runner_->BelongsToCurrentThread());
+}
+
+SimpleWatcher::~SimpleWatcher() {
+  if (IsWatching())
+    Cancel();
+}
+
+bool SimpleWatcher::IsWatching() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return context_ != nullptr;
+}
+
+MojoResult SimpleWatcher::Watch(Handle handle,
+                                MojoHandleSignals signals,
+                                const ReadyCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!IsWatching());
+  DCHECK(!callback.is_null());
+
+  callback_ = callback;
+  handle_ = handle;
+
+  MojoResult watch_result = MOJO_RESULT_UNKNOWN;
+  context_ =
+      Context::Create(weak_factory_.GetWeakPtr(), task_runner_,
+                      watcher_handle_.get(), handle_, signals, &watch_result);
+  if (!context_) {
+    handle_.set_value(kInvalidHandleValue);
+    callback_.Reset();
+    DCHECK_EQ(MOJO_RESULT_INVALID_ARGUMENT, watch_result);
+    return watch_result;
+  }
+
+  if (arming_policy_ == ArmingPolicy::AUTOMATIC)
+    ArmOrNotify();
+
+  return MOJO_RESULT_OK;
+}
+
+void SimpleWatcher::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // The watcher may have already been cancelled if the handle was closed.
+  if (!context_)
+    return;
+
+  // Prevent the cancellation notification from being dispatched to
+  // OnHandleReady() when cancellation is explicit. See the note in the
+  // implementation of DisableCancellationNotifications() above.
+  context_->DisableCancellationNotifications();
+
+  handle_.set_value(kInvalidHandleValue);
+  callback_.Reset();
+
+  // Ensure |context_| is unset by the time we call MojoCancelWatch, as may
+  // re-enter the notification callback and we want to ensure |context_| is
+  // unset by then.
+  scoped_refptr<Context> context;
+  std::swap(context, context_);
+  MojoResult rv =
+      MojoCancelWatch(watcher_handle_.get().value(), context->value());
+
+  // It's possible this cancellation could race with a handle closure
+  // notification, in which case the watch may have already been implicitly
+  // cancelled.
+  DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
+}
+
+MojoResult SimpleWatcher::Arm(MojoResult* ready_result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  uint32_t num_ready_contexts = 1;
+  uintptr_t ready_context;
+  MojoResult local_ready_result;
+  MojoHandleSignalsState ready_state;
+  MojoResult rv =
+      MojoArmWatcher(watcher_handle_.get().value(), &num_ready_contexts,
+                     &ready_context, &local_ready_result, &ready_state);
+  if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+    DCHECK(context_);
+    DCHECK_EQ(1u, num_ready_contexts);
+    DCHECK_EQ(context_->value(), ready_context);
+    if (ready_result)
+      *ready_result = local_ready_result;
+  }
+
+  return rv;
+}
+
+void SimpleWatcher::ArmOrNotify() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Already cancelled, nothing to do.
+  if (!IsWatching())
+    return;
+
+  MojoResult ready_result;
+  MojoResult rv = Arm(&ready_result);
+  if (rv == MOJO_RESULT_OK)
+    return;
+
+  DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
+  task_runner_->PostTask(FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady,
+                                               weak_factory_.GetWeakPtr(),
+                                               context_, ready_result));
+}
+
+void SimpleWatcher::OnHandleReady(scoped_refptr<const Context> context,
+                                  MojoResult result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // This notification may be for a previously watched context, in which case
+  // we just ignore it.
+  if (context != context_)
+    return;
+
+  ReadyCallback callback = callback_;
+  if (result == MOJO_RESULT_CANCELLED) {
+    // Implicit cancellation due to someone closing the watched handle. We clear
+    // the SimppleWatcher's state before dispatching this.
+    context_ = nullptr;
+    handle_.set_value(kInvalidHandleValue);
+    callback_.Reset();
+  }
+
+  // NOTE: It's legal for |callback| to delete |this|.
+  if (!callback.is_null()) {
+    TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
+
+    base::WeakPtr<SimpleWatcher> weak_self = weak_factory_.GetWeakPtr();
+    callback.Run(result);
+    if (!weak_self)
+      return;
+
+    if (unsatisfiable_)
+      return;
+
+    // Prevent |MOJO_RESULT_FAILED_PRECONDITION| task spam by only notifying
+    // at most once in AUTOMATIC arming mode.
+    if (result == MOJO_RESULT_FAILED_PRECONDITION)
+      unsatisfiable_ = true;
+
+    if (arming_policy_ == ArmingPolicy::AUTOMATIC && IsWatching())
+      ArmOrNotify();
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/simple_watcher.h b/mojo/public/cpp/system/simple_watcher.h
new file mode 100644
index 0000000..5e5a7ea3
--- /dev/null
+++ b/mojo/public/cpp/system/simple_watcher.h
@@ -0,0 +1,211 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_SIMPLE_WATCHER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_SIMPLE_WATCHER_H_
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/system_export.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace mojo {
+
+// This provides a convenient thread-bound watcher implementation to safely
+// watch a single handle, dispatching state change notifications to an arbitrary
+// SingleThreadTaskRunner running on the same thread as the SimpleWatcher.
+//
+// SimpleWatcher exposes the concept of "arming" from the low-level Watcher API.
+// In general, a SimpleWatcher must be "armed" in order to dispatch a single
+// notification, and must then be rearmed before it will dispatch another. For
+// more details, see the documentation for ArmingPolicy and the Arm() and
+// ArmOrNotify() methods below.
+class MOJO_CPP_SYSTEM_EXPORT SimpleWatcher {
+ public:
+  // A callback to be called any time a watched handle changes state in some
+  // interesting way. The |result| argument indicates one of the following
+  // conditions depending on its value:
+  //
+  //   |MOJO_RESULT_OK|: One or more of the signals being watched is satisfied.
+  //
+  //   |MOJO_RESULT_FAILED_PRECONDITION|: None of the signals being watched can
+  //       ever be satisfied again.
+  //
+  //   |MOJO_RESULT_CANCELLED|: The watched handle has been closed. No further
+  //       notifications will be fired, as this equivalent to an implicit
+  //       CancelWatch().
+  //
+  // Note that unlike the first two conditions, this callback may be invoked
+  // with |MOJO_RESULT_CANCELLED| even while the SimpleWatcher is disarmed.
+  using ReadyCallback = base::Callback<void(MojoResult result)>;
+
+  // Selects how this SimpleWatcher is to be armed.
+  enum class ArmingPolicy {
+    // The SimpleWatcher is armed automatically on Watch() and rearmed again
+    // after every invocation of the ReadyCallback. There is no need to manually
+    // call Arm() on a SimpleWatcher using this policy. This mode is equivalent
+    // to calling ArmOrNotify() once after Watch() and once again after every
+    // dispatched notification in MANUAL mode.
+    //
+    // This provides a reasonable approximation of edge-triggered behavior,
+    // mitigating (but not completely eliminating) the potential for redundant
+    // notifications.
+    //
+    // NOTE: It is important when using AUTOMATIC policy that your ReadyCallback
+    // always attempt to change the state of the handle (e.g. read available
+    // messages on a message pipe.) Otherwise this will result in a potentially
+    // large number of avoidable redundant tasks.
+    //
+    // For perfect edge-triggered behavior, use MANUAL policy and manually Arm()
+    // the SimpleWatcher as soon as it becomes possible to do so again.
+    AUTOMATIC,
+
+    // The SimpleWatcher is never armed automatically. Arm() or ArmOrNotify()
+    // must be called manually before any non-cancellation notification can be
+    // dispatched to the ReadyCallback. See the documentation for Arm() and
+    // ArmNotify() methods below for more details.
+    MANUAL,
+  };
+
+  SimpleWatcher(const tracked_objects::Location& from_here,
+                ArmingPolicy arming_policy,
+                scoped_refptr<base::SingleThreadTaskRunner> runner =
+                    base::ThreadTaskRunnerHandle::Get());
+  ~SimpleWatcher();
+
+  // Indicates if the SimpleWatcher is currently watching a handle.
+  bool IsWatching() const;
+
+  // Starts watching |handle|. A SimpleWatcher may only watch one handle at a
+  // time, but it is safe to call this more than once as long as the previous
+  // watch has been cancelled (i.e. |IsWatching()| returns |false|.)
+  //
+  // If |handle| is not a valid watchable (message or data pipe) handle or
+  // |signals| is not a valid set of signals to watch, this returns
+  // |MOJO_RESULT_INVALID_ARGUMENT|.
+  //
+  // Otherwise |MOJO_RESULT_OK| is returned and the handle will be watched until
+  // either |handle| is closed, the SimpleWatcher is destroyed, or Cancel() is
+  // explicitly called.
+  //
+  // Once the watch is started, |callback| may be called at any time on the
+  // current thread until |Cancel()| is called or the handle is closed. Note
+  // that |callback| can be called for results other than
+  // |MOJO_RESULT_CANCELLED| only if the SimpleWatcher is currently armed. Use
+  // ArmingPolicy to configure how a SimpleWatcher is armed.
+  //
+  // |MOJO_RESULT_CANCELLED| may be dispatched even while the SimpleWatcher
+  // is disarmed, and no further notifications will be dispatched after that.
+  //
+  // Destroying the SimpleWatcher implicitly calls |Cancel()|.
+  MojoResult Watch(Handle handle,
+                   MojoHandleSignals signals,
+                   const ReadyCallback& callback);
+
+  // Cancels the current watch. Once this returns, the ReadyCallback previously
+  // passed to |Watch()| will never be called again for this SimpleWatcher.
+  //
+  // Note that when cancelled with an explicit call to |Cancel()| the
+  // ReadyCallback will not be invoked with a |MOJO_RESULT_CANCELLED| result.
+  void Cancel();
+
+  // Manually arms the SimpleWatcher.
+  //
+  // Arming the SimpleWatcher allows it to fire a single notification regarding
+  // some future relevant change in the watched handle's state. It's only valid
+  // to call Arm() while a handle is being watched (see Watch() above.)
+  //
+  // SimpleWatcher is always disarmed immediately before invoking its
+  // ReadyCallback and must be rearmed again before another notification can
+  // fire.
+  //
+  // If the watched handle already meets the watched signaling conditions -
+  // i.e., if it would have notified immediately once armed - the SimpleWatcher
+  // is NOT armed, and this call fails with a return value of
+  // |MOJO_RESULT_FAILED_PRECONDITION|. In that case, what would have been the
+  // result code for that immediate notification is instead placed in
+  // |*ready_result| if |ready_result| is non-null.
+  //
+  // If the watcher is successfully armed, this returns |MOJO_RESULT_OK| and
+  // |ready_result| is ignored.
+  MojoResult Arm(MojoResult* ready_result = nullptr);
+
+  // Manually arms the SimpleWatcher OR posts a task to invoke the ReadyCallback
+  // with the ready result of the failed arming attempt.
+  //
+  // This is meant as a convenient helper for a common usage of Arm(), and it
+  // ensures that the ReadyCallback will be invoked asynchronously again as soon
+  // as the watch's conditions are satisfied, assuming the SimpleWatcher isn't
+  // cancelled first.
+  //
+  // Unlike Arm() above, this can never fail.
+  void ArmOrNotify();
+
+  Handle handle() const { return handle_; }
+  ReadyCallback ready_callback() const { return callback_; }
+
+  // Sets the tag used by the heap profiler.
+  // |tag| must be a const string literal.
+  void set_heap_profiler_tag(const char* heap_profiler_tag) {
+    heap_profiler_tag_ = heap_profiler_tag;
+  }
+
+ private:
+  class Context;
+
+  void OnHandleReady(scoped_refptr<const Context> context, MojoResult result);
+
+  base::ThreadChecker thread_checker_;
+
+  // The policy used to determine how this SimpleWatcher is armed.
+  const ArmingPolicy arming_policy_;
+
+  // The TaskRunner of this SimpleWatcher's owning thread. This field is safe to
+  // access from any thread.
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Whether |task_runner_| is the same as base::ThreadTaskRunnerHandle::Get()
+  // for the thread.
+  const bool is_default_task_runner_;
+
+  ScopedWatcherHandle watcher_handle_;
+
+  // A thread-safe context object corresponding to the currently active watch,
+  // if any.
+  scoped_refptr<Context> context_;
+
+  // Fields below must only be accessed on the SimpleWatcher's owning thread.
+
+  // The handle currently under watch. Not owned.
+  Handle handle_;
+
+  // The callback to call when the handle is signaled.
+  ReadyCallback callback_;
+
+  // Tracks if the SimpleWatcher has already notified of unsatisfiability. This
+  // is used to prevent redundant notifications in AUTOMATIC mode.
+  bool unsatisfiable_ = false;
+
+  // Tag used to ID memory allocations that originated from notifications in
+  // this watcher.
+  const char* heap_profiler_tag_ = nullptr;
+
+  base::WeakPtrFactory<SimpleWatcher> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleWatcher);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_SIMPLE_WATCHER_H_
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn
index 8f98b92..3c3d410 100644
--- a/mojo/public/cpp/system/tests/BUILD.gn
+++ b/mojo/public/cpp/system/tests/BUILD.gn
@@ -7,7 +7,7 @@
 
   sources = [
     "core_unittest.cc",
-    "watcher_unittest.cc",
+    "simple_watcher_unittest.cc",
   ]
 
   deps = [
diff --git a/mojo/public/cpp/system/tests/simple_watcher_unittest.cc b/mojo/public/cpp/system/tests/simple_watcher_unittest.cc
new file mode 100644
index 0000000..795f262
--- /dev/null
+++ b/mojo/public/cpp/system/tests/simple_watcher_unittest.cc
@@ -0,0 +1,277 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/simple_watcher.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+template <typename Handler>
+void RunResultHandler(Handler f, MojoResult result) {
+  f(result);
+}
+
+template <typename Handler>
+SimpleWatcher::ReadyCallback OnReady(Handler f) {
+  return base::Bind(&RunResultHandler<Handler>, f);
+}
+
+SimpleWatcher::ReadyCallback NotReached() {
+  return OnReady([](MojoResult) { NOTREACHED(); });
+}
+
+class SimpleWatcherTest : public testing::Test {
+ public:
+  SimpleWatcherTest() {}
+  ~SimpleWatcherTest() override {}
+
+ private:
+  base::MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleWatcherTest);
+};
+
+TEST_F(SimpleWatcherTest, WatchBasic) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  bool notified = false;
+  base::RunLoop run_loop;
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([&](MojoResult result) {
+                              EXPECT_EQ(MOJO_RESULT_OK, result);
+                              notified = true;
+                              run_loop.Quit();
+                            })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  run_loop.Run();
+  EXPECT_TRUE(notified);
+
+  b_watcher.Cancel();
+}
+
+TEST_F(SimpleWatcherTest, WatchUnsatisfiable) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+  a.reset();
+
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, b_watcher.Arm());
+}
+
+TEST_F(SimpleWatcherTest, WatchInvalidHandle) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+  a.reset();
+  b.reset();
+
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+  EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(SimpleWatcherTest, Cancel) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  base::RunLoop run_loop;
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+  EXPECT_TRUE(b_watcher.IsWatching());
+  b_watcher.Cancel();
+  EXPECT_FALSE(b_watcher.IsWatching());
+
+  // This should never trigger the watcher.
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, CancelOnClose) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  base::RunLoop run_loop;
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([&](MojoResult result) {
+                              EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+                              run_loop.Quit();
+                            })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  // This should trigger the watcher above.
+  b.reset();
+
+  run_loop.Run();
+
+  EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(SimpleWatcherTest, CancelOnDestruction) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+  base::RunLoop run_loop;
+  {
+    SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+    EXPECT_TRUE(b_watcher.IsWatching());
+
+    // |b_watcher| should be cancelled when it goes out of scope.
+  }
+
+  // This should never trigger the watcher above.
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, CloseAndCancel) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([](MojoResult result) { FAIL(); })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  // This should trigger the watcher above...
+  b.reset();
+  // ...but the watcher is cancelled first.
+  b_watcher.Cancel();
+
+  EXPECT_FALSE(b_watcher.IsWatching());
+
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SimpleWatcherTest, UnarmedCancel) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+  base::RunLoop loop;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            base::Bind(
+                                [](base::RunLoop* loop, MojoResult result) {
+                                  EXPECT_EQ(result, MOJO_RESULT_CANCELLED);
+                                  loop->Quit();
+                                },
+                                &loop)));
+
+  // This message write will not wake up the watcher since the watcher isn't
+  // armed. Instead, the cancellation will dispatch due to the reset below.
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  b.reset();
+  loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, ManualArming) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+  base::RunLoop loop;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            base::Bind(
+                                [](base::RunLoop* loop, MojoResult result) {
+                                  EXPECT_EQ(result, MOJO_RESULT_OK);
+                                  loop->Quit();
+                                },
+                                &loop)));
+  EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Arm());
+
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, ManualArmOrNotifyWhileSignaled) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  base::RunLoop loop1;
+  SimpleWatcher b_watcher1(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+  bool notified1 = false;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher1.Watch(
+                b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                base::Bind(
+                    [](base::RunLoop* loop, bool* notified, MojoResult result) {
+                      EXPECT_EQ(result, MOJO_RESULT_OK);
+                      *notified = true;
+                      loop->Quit();
+                    },
+                    &loop1, &notified1)));
+
+  base::RunLoop loop2;
+  SimpleWatcher b_watcher2(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+  bool notified2 = false;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher2.Watch(
+                b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                base::Bind(
+                    [](base::RunLoop* loop, bool* notified, MojoResult result) {
+                      EXPECT_EQ(result, MOJO_RESULT_OK);
+                      *notified = true;
+                      loop->Quit();
+                    },
+                    &loop2, &notified2)));
+
+  // First ensure that |b| is readable.
+  EXPECT_EQ(MOJO_RESULT_OK, b_watcher1.Arm());
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  loop1.Run();
+
+  EXPECT_TRUE(notified1);
+  EXPECT_FALSE(notified2);
+  notified1 = false;
+
+  // Now verify that ArmOrNotify results in a notification.
+  b_watcher2.ArmOrNotify();
+  loop2.Run();
+
+  EXPECT_FALSE(notified1);
+  EXPECT_TRUE(notified2);
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/tests/watcher_unittest.cc b/mojo/public/cpp/system/tests/watcher_unittest.cc
deleted file mode 100644
index 9b59240..0000000
--- a/mojo/public/cpp/system/tests/watcher_unittest.cc
+++ /dev/null
@@ -1,181 +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.
-
-#include "mojo/public/cpp/system/watcher.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace {
-
-template <typename Handler>
-void RunResultHandler(Handler f, MojoResult result) { f(result); }
-
-template <typename Handler>
-Watcher::ReadyCallback OnReady(Handler f) {
-  return base::Bind(&RunResultHandler<Handler>, f);
-}
-
-Watcher::ReadyCallback NotReached() {
-  return OnReady([] (MojoResult) { NOTREACHED(); });
-}
-
-class WatcherTest : public testing::Test {
- public:
-  WatcherTest() {}
-  ~WatcherTest() override {}
-
- private:
-  base::MessageLoop message_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(WatcherTest);
-};
-
-TEST_F(WatcherTest, WatchBasic) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-
-  bool notified = false;
-  base::RunLoop run_loop;
-  Watcher b_watcher(FROM_HERE);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                            OnReady([&] (MojoResult result) {
-                              EXPECT_EQ(MOJO_RESULT_OK, result);
-                              notified = true;
-                              run_loop.Quit();
-                            })));
-  EXPECT_TRUE(b_watcher.IsWatching());
-
-  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
-                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
-  run_loop.Run();
-  EXPECT_TRUE(notified);
-
-  b_watcher.Cancel();
-}
-
-TEST_F(WatcherTest, WatchUnsatisfiable) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-  a.reset();
-
-  Watcher b_watcher(FROM_HERE);
-  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                            NotReached()));
-  EXPECT_FALSE(b_watcher.IsWatching());
-}
-
-TEST_F(WatcherTest, WatchInvalidHandle) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-  a.reset();
-  b.reset();
-
-  Watcher b_watcher(FROM_HERE);
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                            NotReached()));
-  EXPECT_FALSE(b_watcher.IsWatching());
-}
-
-TEST_F(WatcherTest, Cancel) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-
-  base::RunLoop run_loop;
-  Watcher b_watcher(FROM_HERE);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                            NotReached()));
-  EXPECT_TRUE(b_watcher.IsWatching());
-  b_watcher.Cancel();
-  EXPECT_FALSE(b_watcher.IsWatching());
-
-  // This should never trigger the watcher.
-  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
-                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, run_loop.QuitClosure());
-  run_loop.Run();
-}
-
-TEST_F(WatcherTest, CancelOnClose) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-
-  base::RunLoop run_loop;
-  Watcher b_watcher(FROM_HERE);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                            OnReady([&] (MojoResult result) {
-                              EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
-                              run_loop.Quit();
-                            })));
-  EXPECT_TRUE(b_watcher.IsWatching());
-
-  // This should trigger the watcher above.
-  b.reset();
-
-  run_loop.Run();
-
-  EXPECT_FALSE(b_watcher.IsWatching());
-}
-
-TEST_F(WatcherTest, CancelOnDestruction) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-  base::RunLoop run_loop;
-  {
-    Watcher b_watcher(FROM_HERE);
-    EXPECT_EQ(MOJO_RESULT_OK,
-              b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                              NotReached()));
-    EXPECT_TRUE(b_watcher.IsWatching());
-
-    // |b_watcher| should be cancelled when it goes out of scope.
-  }
-
-  // This should never trigger the watcher above.
-  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
-                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, run_loop.QuitClosure());
-  run_loop.Run();
-}
-
-TEST_F(WatcherTest, CloseAndCancel) {
-  ScopedMessagePipeHandle a, b;
-  CreateMessagePipe(nullptr, &a, &b);
-
-  Watcher b_watcher(FROM_HERE);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
-                            OnReady([](MojoResult result) { FAIL(); })));
-  EXPECT_TRUE(b_watcher.IsWatching());
-
-  // This should trigger the watcher above...
-  b.reset();
-  // ...but the watcher is cancelled first.
-  b_watcher.Cancel();
-
-  EXPECT_FALSE(b_watcher.IsWatching());
-
-  base::RunLoop().RunUntilIdle();
-}
-
-}  // namespace
-}  // namespace mojo
diff --git a/mojo/public/cpp/system/watcher.cc b/mojo/public/cpp/system/watcher.cc
index 55dcf40..0c62ba8 100644
--- a/mojo/public/cpp/system/watcher.cc
+++ b/mojo/public/cpp/system/watcher.cc
@@ -4,114 +4,17 @@
 
 #include "mojo/public/cpp/system/watcher.h"
 
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/trace_event/heap_profiler.h"
 #include "mojo/public/c/system/functions.h"
 
 namespace mojo {
 
-Watcher::Watcher(const tracked_objects::Location& from_here,
-                 scoped_refptr<base::SingleThreadTaskRunner> runner)
-    : task_runner_(std::move(runner)),
-      is_default_task_runner_(task_runner_ ==
-                              base::ThreadTaskRunnerHandle::Get()),
-      heap_profiler_tag_(from_here.file_name()),
-      weak_factory_(this) {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  weak_self_ = weak_factory_.GetWeakPtr();
-}
-
-Watcher::~Watcher() {
-  if(IsWatching())
-    Cancel();
-}
-
-bool Watcher::IsWatching() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return handle_.is_valid();
-}
-
-MojoResult Watcher::Start(Handle handle,
-                          MojoHandleSignals signals,
-                          const ReadyCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!IsWatching());
-  DCHECK(!callback.is_null());
-
-  callback_ = callback;
-  handle_ = handle;
-  MojoResult result = MojoWatch(handle_.value(), signals,
-                                &Watcher::CallOnHandleReady,
-                                reinterpret_cast<uintptr_t>(this));
-  if (result != MOJO_RESULT_OK) {
-    handle_.set_value(kInvalidHandleValue);
-    callback_.Reset();
-    DCHECK(result == MOJO_RESULT_FAILED_PRECONDITION ||
-           result == MOJO_RESULT_INVALID_ARGUMENT);
-    return result;
-  }
-
-  return MOJO_RESULT_OK;
-}
-
-void Watcher::Cancel() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // The watch may have already been cancelled if the handle was closed.
-  if (!handle_.is_valid())
-    return;
-
-  MojoResult result =
-      MojoCancelWatch(handle_.value(), reinterpret_cast<uintptr_t>(this));
-  // |result| may be MOJO_RESULT_INVALID_ARGUMENT if |handle_| has closed, but
-  // OnHandleReady has not yet been called.
-  DCHECK(result == MOJO_RESULT_INVALID_ARGUMENT || result == MOJO_RESULT_OK);
-  handle_.set_value(kInvalidHandleValue);
-  callback_.Reset();
-}
-
-void Watcher::OnHandleReady(MojoResult result) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  ReadyCallback callback = callback_;
-  if (result == MOJO_RESULT_CANCELLED) {
-    handle_.set_value(kInvalidHandleValue);
-    callback_.Reset();
-  }
-
-  // NOTE: It's legal for |callback| to delete |this|.
-  if (!callback.is_null()) {
-    TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
-    callback.Run(result);
-  }
-}
-
-// static
-void Watcher::CallOnHandleReady(uintptr_t context,
-                                MojoResult result,
-                                MojoHandleSignalsState signals_state,
-                                MojoWatchNotificationFlags flags) {
-  // NOTE: It is safe to assume the Watcher still exists because this callback
-  // will never be run after the Watcher's destructor.
-  //
-  // TODO: Maybe we should also expose |signals_state| through the Watcher API.
-  // Current HandleWatcher users have no need for it, so it's omitted here.
-  Watcher* watcher = reinterpret_cast<Watcher*>(context);
-
-  if ((flags & MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM) &&
-      watcher->task_runner_->RunsTasksOnCurrentThread() &&
-      watcher->is_default_task_runner_) {
-    // System notifications will trigger from the task runner passed to
-    // mojo::edk::InitIPCSupport(). In Chrome this happens to always be the
-    // default task runner for the IO thread.
-    watcher->OnHandleReady(result);
-  } else {
-    watcher->task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&Watcher::OnHandleReady, watcher->weak_self_, result));
-  }
+MojoResult CreateWatcher(MojoWatcherCallback callback,
+                         ScopedWatcherHandle* watcher_handle) {
+  MojoHandle handle;
+  MojoResult rv = MojoCreateWatcher(callback, &handle);
+  if (rv == MOJO_RESULT_OK)
+    watcher_handle->reset(WatcherHandle(handle));
+  return rv;
 }
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/system/watcher.h b/mojo/public/cpp/system/watcher.h
index 236788b..d0a2578 100644
--- a/mojo/public/cpp/system/watcher.h
+++ b/mojo/public/cpp/system/watcher.h
@@ -5,121 +5,33 @@
 #ifndef MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
 #define MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
 
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "mojo/public/c/system/types.h"
+#include "mojo/public/c/system/watcher.h"
 #include "mojo/public/cpp/system/handle.h"
 #include "mojo/public/cpp/system/system_export.h"
 
 namespace mojo {
 
-// A Watcher watches a single Mojo handle for signal state changes.
-//
-// NOTE: Watchers may only be used on threads which have a running MessageLoop.
-class MOJO_CPP_SYSTEM_EXPORT Watcher {
+// A strongly-typed representation of a |MojoHandle| for a watcher.
+class WatcherHandle : public Handle {
  public:
-  // A callback to be called any time a watched handle changes state in some
-  // interesting way. The |result| argument indicates one of the following
-  // conditions depending on its value:
-  //
-  //   |MOJO_RESULT_OK|: One or more of the signals being watched is satisfied.
-  //
-  //   |MOJO_RESULT_FAILED_PRECONDITION|: None of the signals being watched can
-  //       ever be satisfied again.
-  //
-  //   |MOJO_RESULT_CANCELLED|: The handle has been closed and the watch has
-  //       been cancelled implicitly.
-  using ReadyCallback = base::Callback<void(MojoResult result)>;
+  WatcherHandle() = default;
+  explicit WatcherHandle(MojoHandle value) : Handle(value) {}
 
-  Watcher(const tracked_objects::Location& from_here,
-          scoped_refptr<base::SingleThreadTaskRunner> runner =
-              base::ThreadTaskRunnerHandle::Get());
-
-  // NOTE: This destructor automatically calls |Cancel()| if the Watcher is
-  // still active.
-  ~Watcher();
-
-  // Indicates if the Watcher is currently watching a handle.
-  bool IsWatching() const;
-
-  // Starts watching |handle|. A Watcher may only watch one handle at a time,
-  // but it is safe to call this more than once as long as the previous watch
-  // has been cancelled (i.e. |is_watching()| returns |false|.)
-  //
-  // If no signals in |signals| can ever be satisfied for |handle|, this returns
-  // |MOJO_RESULT_FAILED_PRECONDITION|.
-  //
-  // If |handle| is not a valid watchable (message or data pipe) handle, this
-  // returns |MOJO_RESULT_INVALID_ARGUMENT|.
-  //
-  // Otherwise |MOJO_RESULT_OK| is returned and the handle will be watched until
-  // closure or cancellation.
-  //
-  // Once the watch is started, |callback| may be called at any time on the
-  // current thread until |Cancel()| is called or the handle is closed.
-  //
-  // Destroying the Watcher implicitly calls |Cancel()|.
-  MojoResult Start(Handle handle,
-                   MojoHandleSignals signals,
-                   const ReadyCallback& callback);
-
-  // Cancels the current watch. Once this returns, the callback previously
-  // passed to |Start()| will never be called again for this Watcher.
-  void Cancel();
-
-  Handle handle() const { return handle_; }
-  ReadyCallback ready_callback() const { return callback_; }
-
- // Sets the tag used by the heap profiler.
- // |tag| must be a const string literal.
- void set_heap_profiler_tag(const char* heap_profiler_tag) {
-   heap_profiler_tag_ = heap_profiler_tag;
- }
-
- private:
-  void OnHandleReady(MojoResult result);
-
-  static void CallOnHandleReady(uintptr_t context,
-                                MojoResult result,
-                                MojoHandleSignalsState signals_state,
-                                MojoWatchNotificationFlags flags);
-
-  base::ThreadChecker thread_checker_;
-
-  // The TaskRunner of this Watcher's owning thread. This field is safe to
-  // access from any thread.
-  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  // Whether |task_runner_| is the same as base::ThreadTaskRunnerHandle::Get()
-  // for the thread.
-  const bool is_default_task_runner_;
-
-  // A persistent weak reference to this Watcher which can be passed to the
-  // Dispatcher any time this object should be signalled. Safe to access (but
-  // not to dereference!) from any thread.
-  base::WeakPtr<Watcher> weak_self_;
-
-  // Fields below must only be accessed on the Watcher's owning thread.
-
-  // The handle currently under watch. Not owned.
-  Handle handle_;
-
-  // The callback to call when the handle is signaled.
-  ReadyCallback callback_;
-
-  // Tag used to ID memory allocations that originated from notifications in
-  // this watcher.
-  const char* heap_profiler_tag_ = nullptr;
-
-  base::WeakPtrFactory<Watcher> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(Watcher);
+  // Copying and assignment allowed.
 };
 
+static_assert(sizeof(WatcherHandle) == sizeof(Handle),
+              "Bad size for C++ WatcherHandle");
+
+typedef ScopedHandleBase<WatcherHandle> ScopedWatcherHandle;
+static_assert(sizeof(ScopedWatcherHandle) == sizeof(WatcherHandle),
+              "Bad size for C++ ScopedWatcherHandle");
+
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+CreateWatcher(MojoWatcherCallback callback,
+              ScopedWatcherHandle* watcher_handle);
+
 }  // namespace mojo
 
 #endif  // MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 091518f..2cf5735b 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -5108,7 +5108,6 @@
       "cookies/cookie_monster_perftest.cc",
       "disk_cache/disk_cache_perftest.cc",
       "extras/sqlite/sqlite_persistent_cookie_store_perftest.cc",
-      "proxy/proxy_resolver_perftest.cc",
       "socket/udp_socket_perftest.cc",
     ]
 
@@ -5131,12 +5130,6 @@
       sources += [ "websockets/websocket_frame_perftest.cc" ]
     }
 
-    if (use_v8_in_net) {
-      deps += [ ":net_with_v8" ]
-    } else {
-      sources -= [ "proxy/proxy_resolver_perftest.cc" ]
-    }
-
     # Some linker failures have been observed for this target on the Win64
     # continuous builder, see crbug.com/659369.
     # TODO(sebmarchand): Remove this once we have some data.
diff --git a/net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java b/net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java
index 9f840a2f..d398947 100644
--- a/net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java
+++ b/net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java
@@ -15,5 +15,6 @@
         register();
     }
 
+    @Override
     protected void destroy() {}
 }
diff --git a/net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java b/net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java
index 140d478c..7a5c5006 100644
--- a/net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java
+++ b/net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java
@@ -23,6 +23,7 @@
         onApplicationStateChange(getApplicationState());
     }
 
+    @Override
     protected void destroy() {
         if (mDestroyed) return;
         ApplicationStatus.unregisterApplicationStateListener(this);
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc
index f5e258e..b209a5a 100644
--- a/net/http/http_proxy_client_socket_wrapper.cc
+++ b/net/http/http_proxy_client_socket_wrapper.cc
@@ -424,7 +424,9 @@
   if (tunnel_) {
     SpdySessionKey key(GetDestination().host_port_pair(), ProxyServer::Direct(),
                        PRIVACY_MODE_DISABLED);
-    if (spdy_session_pool_->FindAvailableSession(key, GURL(), net_log_)) {
+    if (spdy_session_pool_->FindAvailableSession(
+            key, GURL(),
+            /* enable_ip_based_pooling = */ true, net_log_)) {
       using_spdy_ = true;
       next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
       return OK;
@@ -522,7 +524,9 @@
   SpdySessionKey key(GetDestination().host_port_pair(), ProxyServer::Direct(),
                      PRIVACY_MODE_DISABLED);
   base::WeakPtr<SpdySession> spdy_session =
-      spdy_session_pool_->FindAvailableSession(key, GURL(), net_log_);
+      spdy_session_pool_->FindAvailableSession(
+          key, GURL(),
+          /* enable_ip_based_pooling = */ true, net_log_);
   // It's possible that a session to the proxy has recently been created
   if (spdy_session) {
     if (transport_socket_handle_.get()) {
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index c837f75..d50d24f 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -541,8 +541,9 @@
   // It is OK to dereference spdy_session_pool, because the
   // ClientSocketPoolManager will be destroyed in the same callback that
   // destroys the SpdySessionPool.
-  return spdy_session_pool->FindAvailableSession(spdy_session_key, origin_url,
-                                                 net_log)
+  return spdy_session_pool->FindAvailableSession(
+             spdy_session_key, origin_url, /* enable_ip_based_pooling = */ true,
+             net_log)
              ? ERR_SPDY_SESSION_ALREADY_EXISTS
              : OK;
 }
@@ -952,7 +953,8 @@
   if (CanUseExistingSpdySession()) {
     base::WeakPtr<SpdySession> spdy_session =
         session_->spdy_session_pool()->FindAvailableSession(
-            spdy_session_key, origin_url_, net_log_);
+            spdy_session_key, origin_url_, /* enable_ip_based_pooling = */ true,
+            net_log_);
     if (spdy_session) {
       // If we're preconnecting, but we already have a SpdySession, we don't
       // actually need to preconnect any sockets, so we're done.
@@ -1034,7 +1036,8 @@
     SpdySessionKey spdy_session_key = GetSpdySessionKey();
     existing_spdy_session_ =
         session_->spdy_session_pool()->FindAvailableSession(
-            spdy_session_key, origin_url_, net_log_);
+            spdy_session_key, origin_url_,
+            /* enable_ip_based_pooling = */ true, net_log_);
     if (existing_spdy_session_) {
       using_spdy_ = true;
       next_state_ = STATE_CREATE_STREAM;
@@ -1246,7 +1249,8 @@
   if (!existing_spdy_session_) {
     existing_spdy_session_ =
         session_->spdy_session_pool()->FindAvailableSession(
-            spdy_session_key, origin_url_, net_log_);
+            spdy_session_key, origin_url_,
+            /* enable_ip_based_pooling = */ true, net_log_);
   }
   bool direct = !IsHttpsProxyAndHttpUrl();
   if (existing_spdy_session_.get()) {
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 8bd94fb..ded5447 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -1834,4 +1834,20 @@
   return "";
 }
 
+base::Optional<base::TimeDelta>
+NetworkQualityEstimator::NetworkQualityProvider::GetHttpRTT() const {
+  return base::Optional<base::TimeDelta>();
+}
+
+base::Optional<base::TimeDelta>
+NetworkQualityEstimator::NetworkQualityProvider::GetTransportRTT() const {
+  return base::Optional<base::TimeDelta>();
+}
+
+base::Optional<int32_t>
+NetworkQualityEstimator::NetworkQualityProvider::GetDownstreamThroughputKbps()
+    const {
+  return base::Optional<int32_t>();
+}
+
 }  // namespace net
diff --git a/net/nqe/network_quality_estimator.h b/net/nqe/network_quality_estimator.h
index d44c1a55..5c2718a 100644
--- a/net/nqe/network_quality_estimator.h
+++ b/net/nqe/network_quality_estimator.h
@@ -157,11 +157,11 @@
   // Provides simple interface to obtain the effective connection type.
   class NET_EXPORT NetworkQualityProvider {
    public:
+    virtual ~NetworkQualityProvider() {}
+
     // Returns the current effective connection type.
     virtual EffectiveConnectionType GetEffectiveConnectionType() const = 0;
 
-    virtual ~NetworkQualityProvider() {}
-
     // Adds |observer| to a list of effective connection type observers.
     virtual void AddEffectiveConnectionTypeObserver(
         EffectiveConnectionTypeObserver* observer) = 0;
@@ -170,6 +170,19 @@
     virtual void RemoveEffectiveConnectionTypeObserver(
         EffectiveConnectionTypeObserver* observer) = 0;
 
+    // Returns the current HTTP RTT estimate. If the estimate is unavailable,
+    // the returned optional value is null.
+    virtual base::Optional<base::TimeDelta> GetHttpRTT() const;
+
+    // Returns the current transport RTT estimate. If the estimate is
+    // unavailable, the returned optional value is null.
+    virtual base::Optional<base::TimeDelta> GetTransportRTT() const;
+
+    // Returns the current downstream throughput estimate (in kilobits per
+    // second). If the estimate is unavailable, the returned optional value is
+    // null.
+    virtual base::Optional<int32_t> GetDownstreamThroughputKbps() const;
+
     // Adds |observer| to the list of RTT and throughput estimate observers.
     // |observer| would be notified of the current RTT and throughput estimates
     // in the next message pump.
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc
deleted file mode 100644
index a910d45..0000000
--- a/net/proxy/proxy_resolver_perftest.cc
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <utility>
-
-#include "base/base_paths.h"
-#include "base/compiler_specific.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
-#include "base/strings/string_util.h"
-#include "base/test/perf_time_logger.h"
-#include "net/base/net_errors.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/log/net_log_with_source.h"
-#include "net/proxy/proxy_info.h"
-#include "net/proxy/proxy_resolver.h"
-#include "net/proxy/proxy_resolver_factory.h"
-#include "net/proxy/proxy_resolver_v8.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/gtest_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_WIN)
-#include "net/proxy/proxy_resolver_winhttp.h"
-#elif defined(OS_MACOSX)
-#include "net/proxy/proxy_resolver_mac.h"
-#endif
-
-using net::test::IsOk;
-
-namespace net {
-
-namespace {
-
-// This class holds the URL to use for resolving, and the expected result.
-// We track the expected result in order to make sure the performance
-// test is actually resolving URLs properly, otherwise the perf numbers
-// are meaningless :-)
-struct PacQuery {
-  const char* query_url;
-  const char* expected_result;
-};
-
-// Entry listing which PAC scripts to load, and which URLs to try resolving.
-// |queries| should be terminated by {NULL, NULL}. A sentinel is used
-// rather than a length, to simplify using initializer lists.
-struct PacPerfTest {
-  const char* pac_name;
-  PacQuery queries[100];
-
-  // Returns the actual number of entries in |queries| (assumes NULL sentinel).
-  int NumQueries() const;
-};
-
-// List of performance tests.
-static PacPerfTest kPerfTests[] = {
-  // This test uses an ad-blocker PAC script. This script is very heavily
-  // regular expression oriented, and has no dependencies on the current
-  // IP address, or DNS resolving of hosts.
-  { "no-ads.pac",
-    { // queries:
-      {"http://www.google.com", "DIRECT"},
-      {"http://www.imdb.com/photos/cmsicons/x", "PROXY 0.0.0.0:3421"},
-      {"http://www.imdb.com/x", "DIRECT"},
-      {"http://www.staples.com/", "DIRECT"},
-      {"http://www.staples.com/pixeltracker/x", "PROXY 0.0.0.0:3421"},
-      {"http://www.staples.com/pixel/x", "DIRECT"},
-      {"http://www.foobar.com", "DIRECT"},
-      {"http://www.foobarbaz.com/x/y/z", "DIRECT"},
-      {"http://www.testurl1.com/index.html", "DIRECT"},
-      {"http://www.testurl2.com", "DIRECT"},
-      {"https://www.sample/pirate/arrrrrr", "DIRECT"},
-      {NULL, NULL}
-    },
-  },
-};
-
-int PacPerfTest::NumQueries() const {
-  for (size_t i = 0; i < arraysize(queries); ++i) {
-    if (queries[i].query_url == NULL)
-      return i;
-  }
-  NOTREACHED();  // Bad definition.
-  return 0;
-}
-
-// The number of URLs to resolve when testing a PAC script.
-const int kNumIterations = 500;
-
-// Helper class to run through all the performance tests using the specified
-// proxy resolver implementation.
-class PacPerfSuiteRunner {
- public:
-  // |resolver_name| is the label used when logging the results.
-  PacPerfSuiteRunner(ProxyResolverFactory* factory,
-                     const std::string& resolver_name)
-      : factory_(factory), resolver_name_(resolver_name) {
-    test_server_.ServeFilesFromSourceDirectory(
-        "net/data/proxy_resolver_perftest");
-  }
-
-  void RunAllTests() {
-    ASSERT_TRUE(test_server_.Start());
-    for (size_t i = 0; i < arraysize(kPerfTests); ++i) {
-      const PacPerfTest& test_data = kPerfTests[i];
-      RunTest(test_data.pac_name,
-              test_data.queries,
-              test_data.NumQueries());
-    }
-  }
-
- private:
-  void RunTest(const std::string& script_name,
-               const PacQuery* queries,
-               int queries_len) {
-    std::unique_ptr<ProxyResolver> resolver;
-    if (!factory_->expects_pac_bytes()) {
-      GURL pac_url = test_server_.GetURL(std::string("/") + script_name);
-      int rv = factory_->CreateProxyResolver(
-          ProxyResolverScriptData::FromURL(pac_url), &resolver,
-          CompletionCallback(), nullptr);
-      EXPECT_THAT(rv, IsOk());
-    } else {
-      resolver = LoadPacScriptAndCreateResolver(script_name);
-    }
-    ASSERT_TRUE(resolver);
-
-    // Do a query to warm things up. In the case of internal-fetch proxy
-    // resolvers, the first resolve will be slow since it has to download
-    // the PAC script.
-    {
-      ProxyInfo proxy_info;
-      int result = resolver->GetProxyForURL(GURL("http://www.warmup.com"),
-                                            &proxy_info, CompletionCallback(),
-                                            NULL, NetLogWithSource());
-      ASSERT_THAT(result, IsOk());
-    }
-
-    // Start the perf timer.
-    std::string perf_test_name = resolver_name_ + "_" + script_name;
-    base::PerfTimeLogger timer(perf_test_name.c_str());
-
-    for (int i = 0; i < kNumIterations; ++i) {
-      // Round-robin between URLs to resolve.
-      const PacQuery& query = queries[i % queries_len];
-
-      // Resolve.
-      ProxyInfo proxy_info;
-      int result = resolver->GetProxyForURL(GURL(query.query_url), &proxy_info,
-                                            CompletionCallback(), NULL,
-                                            NetLogWithSource());
-
-      // Check that the result was correct. Note that ToPacString() and
-      // ASSERT_EQ() are fast, so they won't skew the results.
-      ASSERT_THAT(result, IsOk());
-      ASSERT_EQ(query.expected_result, proxy_info.ToPacString());
-    }
-
-    // Print how long the test ran for.
-    timer.Done();
-  }
-
-  // Read the PAC script from disk and initialize the proxy resolver with it.
-  std::unique_ptr<ProxyResolver> LoadPacScriptAndCreateResolver(
-      const std::string& script_name) {
-    base::FilePath path;
-    PathService::Get(base::DIR_SOURCE_ROOT, &path);
-    path = path.AppendASCII("net");
-    path = path.AppendASCII("data");
-    path = path.AppendASCII("proxy_resolver_perftest");
-    path = path.AppendASCII(script_name);
-
-    // Try to read the file from disk.
-    std::string file_contents;
-    bool ok = base::ReadFileToString(path, &file_contents);
-
-    // If we can't load the file from disk, something is misconfigured.
-    LOG_IF(ERROR, !ok) << "Failed to read file: " << path.value();
-    if (!ok)
-      return nullptr;
-
-    // Load the PAC script into the ProxyResolver.
-    std::unique_ptr<ProxyResolver> resolver;
-    int rv = factory_->CreateProxyResolver(
-        ProxyResolverScriptData::FromUTF8(file_contents), &resolver,
-        CompletionCallback(), nullptr);
-    EXPECT_THAT(rv, IsOk());
-    return resolver;
-  }
-
-  ProxyResolverFactory* factory_;
-  std::string resolver_name_;
-  EmbeddedTestServer test_server_;
-};
-
-#if defined(OS_WIN)
-TEST(ProxyResolverPerfTest, ProxyResolverWinHttp) {
-  ProxyResolverFactoryWinHttp factory;
-  PacPerfSuiteRunner runner(&factory, "ProxyResolverWinHttp");
-  runner.RunAllTests();
-}
-#elif defined(OS_MACOSX)
-TEST(ProxyResolverPerfTest, ProxyResolverMac) {
-  ProxyResolverFactoryMac factory;
-  PacPerfSuiteRunner runner(&factory, "ProxyResolverMac");
-  runner.RunAllTests();
-}
-#endif
-
-class MockJSBindings : public ProxyResolverV8::JSBindings {
- public:
-  MockJSBindings() {}
-
-  void Alert(const base::string16& message) override { CHECK(false); }
-
-  bool ResolveDns(const std::string& host,
-                  ResolveDnsOperation op,
-                  std::string* output,
-                  bool* terminate) override {
-    CHECK(false);
-    return false;
-  }
-
-  void OnError(int line_number, const base::string16& message) override {
-    CHECK(false);
-  }
-};
-
-class ProxyResolverV8Wrapper : public ProxyResolver {
- public:
-  ProxyResolverV8Wrapper(std::unique_ptr<ProxyResolverV8> resolver,
-                         std::unique_ptr<MockJSBindings> bindings)
-      : resolver_(std::move(resolver)), bindings_(std::move(bindings)) {}
-
-  int GetProxyForURL(const GURL& url,
-                     ProxyInfo* results,
-                     const CompletionCallback& /*callback*/,
-                     std::unique_ptr<Request>* /*request*/,
-                     const NetLogWithSource& net_log) override {
-    return resolver_->GetProxyForURL(url, results, bindings_.get());
-  }
-
- private:
-  std::unique_ptr<ProxyResolverV8> resolver_;
-  std::unique_ptr<MockJSBindings> bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8Wrapper);
-};
-
-class ProxyResolverV8Factory : public ProxyResolverFactory {
- public:
-  ProxyResolverV8Factory() : ProxyResolverFactory(true) {}
-  int CreateProxyResolver(
-      const scoped_refptr<ProxyResolverScriptData>& pac_script,
-      std::unique_ptr<ProxyResolver>* resolver,
-      const net::CompletionCallback& callback,
-      std::unique_ptr<Request>* request) override {
-    std::unique_ptr<ProxyResolverV8> v8_resolver;
-    std::unique_ptr<MockJSBindings> js_bindings_(new MockJSBindings);
-    int result =
-        ProxyResolverV8::Create(pac_script, js_bindings_.get(), &v8_resolver);
-    if (result == OK) {
-      resolver->reset(new ProxyResolverV8Wrapper(std::move(v8_resolver),
-                                                 std::move(js_bindings_)));
-    }
-    return result;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8Factory);
-};
-
-TEST(ProxyResolverPerfTest, ProxyResolverV8) {
-  base::MessageLoop message_loop;
-  ProxyResolverV8Factory factory;
-  PacPerfSuiteRunner runner(&factory, "ProxyResolverV8");
-  runner.RunAllTests();
-}
-
-}  // namespace
-
-}  // namespace net
diff --git a/net/quic/core/congestion_control/bbr_sender.cc b/net/quic/core/congestion_control/bbr_sender.cc
index 08699c7e..9fd9d3b 100644
--- a/net/quic/core/congestion_control/bbr_sender.cc
+++ b/net/quic/core/congestion_control/bbr_sender.cc
@@ -79,6 +79,9 @@
       last_sent_packet_(0),
       current_round_trip_end_(0),
       max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
+      max_ack_spacing_(kBandwidthWindowSize, QuicTime::Delta::Zero(), 0),
+      largest_acked_time_(QuicTime::Zero()),
+      largest_acked_sent_time_(QuicTime::Zero()),
       min_rtt_(QuicTime::Delta::Zero()),
       min_rtt_timestamp_(QuicTime::Zero()),
       congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS),
@@ -202,6 +205,10 @@
     min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets);
     UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
                         is_round_start);
+    if (FLAGS_quic_reloadable_flag_quic_bbr_ack_spacing2) {
+      QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_spacing2, 1, 2);
+      UpdateAckSpacing(event_time, last_acked_packet, acked_packets);
+    }
   }
 
   // Handle logic specific to PROBE_BW mode.
@@ -466,6 +473,41 @@
   }
 }
 
+// TODO(ianswett): Move this logic into BandwidthSampler.
+void BbrSender::UpdateAckSpacing(QuicTime ack_time,
+                                 QuicPacketNumber largest_newly_acked,
+                                 const CongestionVector& acked_packets) {
+  // Ignore acks of reordered packets.
+  if (largest_newly_acked < unacked_packets_->largest_observed()) {
+    return;
+  }
+  // Ignore acks of only one packet to filter out delayed acks.
+  if (acked_packets.size() == 1) {
+    return;
+  }
+  QuicTime largest_newly_acked_sent_time =
+      unacked_packets_->GetTransmissionInfo(largest_newly_acked).sent_time;
+  // Initialize on the first ack.
+  if (!largest_acked_time_.IsInitialized()) {
+    largest_acked_time_ = ack_time;
+    largest_acked_sent_time_ = largest_newly_acked_sent_time;
+    return;
+  }
+  QuicTime::Delta ack_delta = ack_time - largest_acked_time_;
+  QuicTime::Delta send_delta =
+      largest_newly_acked_sent_time - largest_acked_sent_time_;
+  largest_acked_time_ = ack_time;
+  largest_acked_sent_time_ = largest_newly_acked_sent_time;
+  if (ack_delta <= send_delta) {
+    return;
+  }
+
+  // Limit the ack spacing to SRTT to filter outliers.
+  QuicTime::Delta ack_spacing =
+      std::min(ack_delta - send_delta, rtt_stats_->smoothed_rtt());
+  max_ack_spacing_.Update(ack_spacing, round_trip_count_);
+}
+
 void BbrSender::CalculatePacingRate() {
   if (BandwidthEstimate().IsZero()) {
     return;
@@ -500,18 +542,23 @@
   if (rtt_variance_weight_ > 0.f && !BandwidthEstimate().IsZero()) {
     target_window += rtt_variance_weight_ * rtt_stats_->mean_deviation() *
                      BandwidthEstimate();
+  } else if (FLAGS_quic_reloadable_flag_quic_bbr_ack_spacing2 &&
+             is_at_full_bandwidth_) {
+    QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_spacing2, 2, 2);
+    // Add CWND for inter-ack spacing once STARTUP has been exited.
+    target_window += max_ack_spacing_.GetBest() * BandwidthEstimate();
   }
 
   // Instead of immediately setting the target CWND as the new one, BBR grows
   // the CWND towards |target_window| by only increasing it |bytes_acked| at a
   // time.
   if (is_at_full_bandwidth_) {
-    // If the connection is not yet out of startup phase, do not decrease the
-    // window.
     congestion_window_ =
         std::min(target_window, congestion_window_ + bytes_acked);
   } else if (congestion_window_ < target_window ||
              sampler_.total_bytes_acked() < initial_congestion_window_) {
+    // If the connection is not yet out of startup phase, do not decrease the
+    // window.
     congestion_window_ = congestion_window_ + bytes_acked;
   }
 
diff --git a/net/quic/core/congestion_control/bbr_sender.h b/net/quic/core/congestion_control/bbr_sender.h
index 7a1089c..b248cd2 100644
--- a/net/quic/core/congestion_control/bbr_sender.h
+++ b/net/quic/core/congestion_control/bbr_sender.h
@@ -36,8 +36,6 @@
 // pacing is disabled.
 //
 // TODO(vasilvv): implement traffic policer (long-term sampling) mode.
-//
-// TODO(vasilvv): implement packet conservation.
 class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface {
  public:
   enum Mode {
@@ -143,6 +141,12 @@
                          QuicRoundTripCount>
       MaxBandwidthFilter;
 
+  typedef WindowedFilter<QuicTime::Delta,
+                         MaxFilter<QuicTime::Delta>,
+                         QuicRoundTripCount,
+                         QuicRoundTripCount>
+      MaxAckDelayFilter;
+
   // Returns the current estimate of the RTT of the connection.  Outside of the
   // edge cases, this is minimum RTT.
   QuicTime::Delta GetMinRtt() const;
@@ -186,6 +190,11 @@
                            bool has_losses,
                            bool is_round_start);
 
+  // Updates the ack spacing max filter if a larger value is observed.
+  void UpdateAckSpacing(QuicTime ack_time,
+                        QuicPacketNumber largest_newly_acked,
+                        const CongestionVector& acked_packets);
+
   // Determines the appropriate pacing rate for the connection.
   void CalculatePacingRate();
   // Determines the appropriate congestion window for the connection.
@@ -217,6 +226,13 @@
   // round-trips.
   MaxBandwidthFilter max_bandwidth_;
 
+  // Tracks the maximum spacing between two acks acknowledging in order packets.
+  MaxAckDelayFilter max_ack_spacing_;
+
+  // The time the largest acked packet was acked and when it was sent.
+  QuicTime largest_acked_time_;
+  QuicTime largest_acked_sent_time_;
+
   // Minimum RTT estimate.  Automatically expires within 10 seconds (and
   // triggers PROBE_RTT mode) if no new value is sampled during that period.
   QuicTime::Delta min_rtt_;
diff --git a/net/quic/core/congestion_control/bbr_sender_test.cc b/net/quic/core/congestion_control/bbr_sender_test.cc
index d987655..64c0a15a 100644
--- a/net/quic/core/congestion_control/bbr_sender_test.cc
+++ b/net/quic/core/congestion_control/bbr_sender_test.cc
@@ -136,6 +136,13 @@
         kTestPropagationDelay));
   }
 
+  void EnableAggregation(QuicByteCount aggregation_bytes,
+                         QuicTime::Delta aggregation_timeout) {
+    // Enable aggregation on the path from the receiver to the sender.
+    switch_->port_queue(1)->EnableAggregation(aggregation_bytes,
+                                              aggregation_timeout);
+  }
+
   void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) {
     bbr_sender_.AddBytesToTransfer(transfer_size);
     bool simulator_result = simulator_.RunUntilOrTimeout(
@@ -224,6 +231,29 @@
   EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
 }
 
+// Test a simple long data transfer with 2 rtts of aggregation.
+TEST_F(BbrSenderTest, DISABLED_SimpleTransfer2RTTAggregation) {
+  QuicFlagSaver flags;
+  FLAGS_quic_reloadable_flag_quic_bbr_ack_spacing2 = true;
+  CreateDefaultSetup();
+  // 2 RTTs of aggregation, with a max of 10kb.
+  EnableAggregation(10 * 1024, 2 * kTestRtt);
+
+  // Transfer 12MB.
+  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+  // It's possible to read a bandwidth as much as 25% too high with aggregation.
+  EXPECT_LE(kTestLinkBandwidth * 0.99f,
+            sender_->ExportDebugState().max_bandwidth);
+  // TODO(ianswett): Tighten this bound once we understand why BBR is
+  // overestimating bandwidth with aggregation. b/36022633
+  EXPECT_GE(kTestLinkBandwidth * 1.25f,
+            sender_->ExportDebugState().max_bandwidth);
+  // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
+  // bandwidth higher than the link rate.
+  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
 // Test the number of losses incurred by the startup phase in a situation when
 // the buffer is less than BDP.
 TEST_F(BbrSenderTest, PacketLossOnSmallBufferStartup) {
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index 25b97cec..36ec22e 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -82,10 +82,6 @@
           FLAGS_quic_reloadable_flag_quic_release_crypto_stream_buffer,
           true)
 
-// If true, buffer packets while parsing public headers instead of parsing down
-// if CHLO is already buffered.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_buffer_packets_after_chlo, true)
-
 // If true, do not override a connection in global map if exists. Only create
 // QUIC session if it is successfully inserted to the global map. Toss the
 // packet if insertion fails.
@@ -171,3 +167,13 @@
 
 // If true, multipath bit is not used in public flag.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_remove_multipath_bit, false)
+
+// Allow QUIC's flow control autotuning to increase the window as
+// quickly for the first adjustment as in subsequent ones.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune,
+          false)
+
+// Only consider using the ack spacing in QUIC BBR if 2 packets are acked at
+// once.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_ack_spacing2, false)
diff --git a/net/quic/core/quic_flow_controller.cc b/net/quic/core/quic_flow_controller.cc
index d519032a..1c27276 100644
--- a/net/quic/core/quic_flow_controller.cc
+++ b/net/quic/core/quic_flow_controller.cc
@@ -7,8 +7,10 @@
 #include <cstdint>
 
 #include "net/quic/core/quic_connection.h"
+#include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/platform/api/quic_bug_tracker.h"
+#include "net/quic/platform/api/quic_flag_utils.h"
 #include "net/quic/platform/api/quic_logging.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 
@@ -184,6 +186,14 @@
   QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_;
   QuicByteCount threshold = WindowUpdateThreshold();
 
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune &&
+      !prev_window_update_time_.IsInitialized()) {
+    QUIC_FLAG_COUNT(quic_reloadable_flag_quic_flow_control_faster_autotune);
+    // Treat the initial window as if it is a window update, so if 1/2 the
+    // window is used in less than 2 RTTs, the window is increased.
+    prev_window_update_time_ = connection_->clock()->ApproximateNow();
+  }
+
   if (available_window >= threshold) {
     QUIC_DVLOG(1) << ENDPOINT << "Not sending WindowUpdate for stream " << id_
                   << ", available window: " << available_window
diff --git a/net/quic/core/quic_flow_controller_test.cc b/net/quic/core/quic_flow_controller_test.cc
index 9e8eb45..ee80f75 100644
--- a/net/quic/core/quic_flow_controller_test.cc
+++ b/net/quic/core/quic_flow_controller_test.cc
@@ -38,7 +38,9 @@
       : stream_id_(1234),
         send_window_(kInitialSessionFlowControlWindowForTest),
         receive_window_(kInitialSessionFlowControlWindowForTest),
-        connection_(&helper_, &alarm_factory_, Perspective::IS_CLIENT) {}
+        connection_(&helper_, &alarm_factory_, Perspective::IS_CLIENT) {
+    FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune = true;
+  }
 
   void Initialize() {
     flow_controller_.reset(new QuicFlowController(
@@ -55,6 +57,7 @@
   MockAlarmFactory alarm_factory_;
   MockQuicConnection connection_;
   MockFlowController session_flow_controller_;
+  QuicFlagSaver flag_saver_;
 };
 
 TEST_F(QuicFlowControllerTest, SendingBytes) {
@@ -165,7 +168,13 @@
 
 TEST_F(QuicFlowControllerTest, ReceivingBytesFastIncreasesFlowWindow) {
   // This test will generate two WINDOW_UPDATE frames.
-  EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_)).Times(2);
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_))
+        .Times(1);
+  } else {
+    EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_))
+        .Times(2);
+  }
 
   Initialize();
   flow_controller_->set_auto_tune_receive_window(true);
@@ -195,20 +204,32 @@
   EXPECT_FALSE(flow_controller_->FlowControlViolation());
   EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset,
             QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_CALL(
+        session_flow_controller_,
+        EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5));
+  }
 
   // Consume enough bytes to send a WINDOW_UPDATE frame.
   flow_controller_->AddBytesConsumed(threshold + 1);
   // Result is that once again we have a fully open receive window.
   EXPECT_FALSE(flow_controller_->FlowControlViolation());
-  EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
-            QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_EQ(
+        2 * kInitialSessionFlowControlWindowForTest,
+        QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+  } else {
+    EXPECT_EQ(
+        kInitialSessionFlowControlWindowForTest,
+        QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
 
-  // Move time forward, but by less than two RTTs.  Then receive and consume
-  // some more, forcing a second WINDOW_UPDATE with an increased max window
-  // size.
-  EXPECT_CALL(
-      session_flow_controller_,
-      EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5));
+    // Move time forward, but by less than two RTTs.  Then receive and consume
+    // some more, forcing a second WINDOW_UPDATE with an increased max window
+    // size.
+    EXPECT_CALL(
+        session_flow_controller_,
+        EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5));
+  }
 
   connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1));
   receive_offset += threshold + 1;
@@ -275,7 +296,13 @@
 
 TEST_F(QuicFlowControllerTest, ReceivingBytesNormalStableFlowWindow) {
   // This test will generate two WINDOW_UPDATE frames.
-  EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_)).Times(2);
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_))
+        .Times(1);
+  } else {
+    EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_))
+        .Times(2);
+  }
 
   Initialize();
   flow_controller_->set_auto_tune_receive_window(true);
@@ -304,13 +331,24 @@
   EXPECT_FALSE(flow_controller_->FlowControlViolation());
   EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset,
             QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
-
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_CALL(
+        session_flow_controller_,
+        EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5));
+  }
   flow_controller_->AddBytesConsumed(threshold + 1);
 
   // Result is that once again we have a fully open receive window.
   EXPECT_FALSE(flow_controller_->FlowControlViolation());
-  EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
-            QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_EQ(
+        2 * kInitialSessionFlowControlWindowForTest,
+        QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+  } else {
+    EXPECT_EQ(
+        kInitialSessionFlowControlWindowForTest,
+        QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+  }
 
   // Move time forward, but by more than two RTTs.  Then receive and consume
   // some more, forcing a second WINDOW_UPDATE with unchanged max window size.
@@ -324,8 +362,11 @@
 
   QuicByteCount new_threshold =
       QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
-
-  EXPECT_EQ(new_threshold, threshold);
+  if (FLAGS_quic_reloadable_flag_quic_flow_control_faster_autotune) {
+    EXPECT_EQ(new_threshold, 2 * threshold);
+  } else {
+    EXPECT_EQ(new_threshold, threshold);
+  }
 }
 
 TEST_F(QuicFlowControllerTest, ReceivingBytesNormalNoAutoTune) {
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 785319e..3dfb5d84 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -437,7 +437,9 @@
     NetLogWithSource log;
     HttpNetworkSession* session = helper.session();
     base::WeakPtr<SpdySession> spdy_session =
-        session->spdy_session_pool()->FindAvailableSession(key, url, log);
+        session->spdy_session_pool()->FindAvailableSession(
+            key, url,
+            /* enable_ip_based_pooling = */ true, log);
     ASSERT_TRUE(spdy_session);
     EXPECT_EQ(0u, spdy_session->num_active_streams());
     EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams());
@@ -4057,7 +4059,9 @@
                      PRIVACY_MODE_DISABLED);
   NetLogWithSource log;
   base::WeakPtr<SpdySession> spdy_session =
-      spdy_session_pool->FindAvailableSession(key, GURL(), log);
+      spdy_session_pool->FindAvailableSession(
+          key, GURL(),
+          /* enable_ip_based_pooling = */ true, log);
   EXPECT_TRUE(spdy_session);
 
   // Start second transaction.
@@ -4087,7 +4091,9 @@
   EXPECT_EQ("hello!", response_data);
 
   // Graceful GOAWAY was received, SpdySession should be unavailable.
-  spdy_session = spdy_session_pool->FindAvailableSession(key, GURL(), log);
+  spdy_session = spdy_session_pool->FindAvailableSession(
+      key, GURL(),
+      /* enable_ip_based_pooling = */ true, log);
   EXPECT_FALSE(spdy_session);
 
   helper.VerifyDataConsumed();
@@ -5035,7 +5041,9 @@
   SpdySessionKey key(host_port_pair_, ProxyServer::Direct(),
                      PRIVACY_MODE_DISABLED);
   base::WeakPtr<SpdySession> spdy_session =
-      spdy_session_pool->FindAvailableSession(key, GURL(), log);
+      spdy_session_pool->FindAvailableSession(
+          key, GURL(),
+          /* enable_ip_based_pooling = */ true, log);
 
   EXPECT_FALSE(spdy_session->unclaimed_pushed_streams_.empty());
   EXPECT_EQ(1u, spdy_session->unclaimed_pushed_streams_.size());
@@ -5182,7 +5190,9 @@
   SpdySessionKey key0(host_port_pair0, ProxyServer::Direct(),
                       PRIVACY_MODE_DISABLED);
   base::WeakPtr<SpdySession> spdy_session0 =
-      spdy_session_pool->FindAvailableSession(key0, GURL(), log);
+      spdy_session_pool->FindAvailableSession(
+          key0, GURL(),
+          /* enable_ip_based_pooling = */ true, log);
 
   EXPECT_TRUE(spdy_session0->unclaimed_pushed_streams_.empty());
   EXPECT_EQ(0u, spdy_session0->unclaimed_pushed_streams_.size());
@@ -5191,7 +5201,9 @@
   SpdySessionKey key1(host_port_pair1, ProxyServer::Direct(),
                       PRIVACY_MODE_DISABLED);
   base::WeakPtr<SpdySession> spdy_session1 =
-      spdy_session_pool->FindAvailableSession(key1, GURL(), log);
+      spdy_session_pool->FindAvailableSession(
+          key1, GURL(),
+          /* enable_ip_based_pooling = */ true, log);
 
   EXPECT_FALSE(spdy_session1->unclaimed_pushed_streams_.empty());
   EXPECT_EQ(1u, spdy_session1->unclaimed_pushed_streams_.size());
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 2b47a81..e217fc2a 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -1332,6 +1332,10 @@
   pooled_aliases_.insert(alias_key);
 }
 
+void SpdySession::RemovePooledAlias(const SpdySessionKey& alias_key) {
+  pooled_aliases_.erase(alias_key);
+}
+
 bool SpdySession::HasAcceptableTransportSecurity() const {
   // If we're not even using TLS, we have no standards to meet.
   if (!is_secure_) {
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 9852e2d6..d6f03c3 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -532,6 +532,9 @@
   // Adds |alias| to set of aliases associated with this session.
   void AddPooledAlias(const SpdySessionKey& alias_key);
 
+  // Removes |alias| from set of aliases associated with this session.
+  void RemovePooledAlias(const SpdySessionKey& alias_key);
+
   // Returns the set of aliases associated with this session.
   const std::set<SpdySessionKey>& pooled_aliases() const {
     return pooled_aliases_;
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 308a80b1e..7850b4f 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -130,6 +130,7 @@
 base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession(
     const SpdySessionKey& key,
     const GURL& url,
+    bool enable_ip_based_pooling,
     const NetLogWithSource& net_log) {
   UnclaimedPushedStreamMap::iterator url_it =
       unclaimed_pushed_streams_.find(url);
@@ -169,6 +170,16 @@
           NetLogEventType::HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION,
           it->second->net_log().source().ToEventParametersCallback());
     } else {
+      if (!enable_ip_based_pooling) {
+        // Remove session from available sessions and from aliases, and remove
+        // key from the session's pooled alias set, so that a new session can be
+        // created with this |key|.
+        it->second->RemovePooledAlias(key);
+        UnmapKey(key);
+        RemoveAliases(key);
+        return base::WeakPtr<SpdySession>();
+      }
+
       UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
                                 FOUND_EXISTING_FROM_IP_POOL,
                                 SPDY_SESSION_GET_MAX);
@@ -180,6 +191,9 @@
     return it->second;
   }
 
+  if (!enable_ip_based_pooling)
+    return base::WeakPtr<SpdySession>();
+
   // Look up IP addresses from resolver cache.
   HostResolver::RequestInfo resolve_info(key.host_port_pair());
   AddressList addresses;
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h
index fc82c40..9da2fb7a 100644
--- a/net/spdy/spdy_session_pool.h
+++ b/net/spdy/spdy_session_pool.h
@@ -87,12 +87,19 @@
       const NetLogWithSource& net_log,
       bool is_secure);
 
-  // Return an available session for |key| that has an unclaimed push stream for
-  // |url| if such exists and |url| is not empty, or else an available session
-  // for |key| if such exists, or else nullptr.
+  // If |url| is not empty and there is a session for |key| that has an
+  // unclaimed push stream for |url|, return it.
+  // Otherwise if there is an available session for |key|, return it.
+  // Otherwise if there is a session to pool to based on IP address:
+  //   * if |enable_ip_based_pooling == true|,
+  //     then mark it as available for |key| and return it;
+  //   * if |enable_ip_based_pooling == false|,
+  //     then remove it from the available sessions, and return nullptr.
+  // Otherwise return nullptr.
   base::WeakPtr<SpdySession> FindAvailableSession(
       const SpdySessionKey& key,
       const GURL& url,
+      bool enable_ip_based_pooling,
       const NetLogWithSource& net_log);
 
   // Remove all mappings and aliases for the given session, which must
diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc
index 7c69a0b8..2d0d845 100644
--- a/net/spdy/spdy_session_pool_unittest.cc
+++ b/net/spdy/spdy_session_pool_unittest.cc
@@ -419,7 +419,8 @@
   // we got with host 0, and that is a different from host 2's session.
   base::WeakPtr<SpdySession> session1 =
       spdy_session_pool_->FindAvailableSession(
-          test_hosts[1].key, GURL(test_hosts[1].url), NetLogWithSource());
+          test_hosts[1].key, GURL(test_hosts[1].url),
+          /* enable_ip_based_pooling = */ true, NetLogWithSource());
   EXPECT_EQ(session.get(), session1.get());
   EXPECT_NE(session2.get(), session1.get());
 
@@ -560,16 +561,18 @@
   BoundTestNetLog net_log;
   base::HistogramTester histogram_tester;
   base::WeakPtr<SpdySession> session1 =
-      spdy_session_pool_->FindAvailableSession(test_hosts[1].key, GURL(),
-                                               net_log.bound());
+      spdy_session_pool_->FindAvailableSession(
+          test_hosts[1].key, GURL(),
+          /* enable_ip_based_pooling = */ true, net_log.bound());
   EXPECT_EQ(session0.get(), session1.get());
 
   ASSERT_EQ(1u, net_log.GetSize());
   histogram_tester.ExpectTotalCount("Net.SpdySessionGet", 1);
 
   // A request to the second host should still pool to the existing connection.
-  session1 = spdy_session_pool_->FindAvailableSession(test_hosts[1].key, GURL(),
-                                                      net_log.bound());
+  session1 = spdy_session_pool_->FindAvailableSession(
+      test_hosts[1].key, GURL(),
+      /* enable_ip_based_pooling = */ true, net_log.bound());
   EXPECT_EQ(session0.get(), session1.get());
 
   ASSERT_EQ(2u, net_log.GetSize());
@@ -591,6 +594,78 @@
   histogram_tester.ExpectUniqueSample("Net.SpdySessionGet", 2, 2);
 }
 
+TEST_F(SpdySessionPoolTest, IPPoolingDisabled) {
+  // Define two hosts with identical IP address.
+  const int kTestPort = 443;
+  struct TestHosts {
+    std::string name;
+    std::string iplist;
+    SpdySessionKey key;
+    AddressList addresses;
+    std::unique_ptr<HostResolver::Request> request;
+  } test_hosts[] = {
+      {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
+  };
+
+  // Populate the HostResolver cache.
+  session_deps_.host_resolver->set_synchronous_mode(true);
+  for (size_t i = 0; i < arraysize(test_hosts); i++) {
+    session_deps_.host_resolver->rules()->AddIPLiteralRule(
+        test_hosts[i].name, test_hosts[i].iplist, std::string());
+
+    HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
+    session_deps_.host_resolver->Resolve(
+        info, DEFAULT_PRIORITY, &test_hosts[i].addresses, CompletionCallback(),
+        &test_hosts[i].request, NetLogWithSource());
+
+    test_hosts[i].key =
+        SpdySessionKey(HostPortPair(test_hosts[i].name, kTestPort),
+                       ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+  }
+
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+  StaticSocketDataProvider data(reads, arraysize(reads), nullptr, 0);
+  MockConnect connect_data(SYNCHRONOUS, OK);
+  data.set_connect_data(connect_data);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  AddSSLSocketData();
+
+  MockRead reads1[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+  StaticSocketDataProvider data1(reads1, arraysize(reads1), nullptr, 0);
+  MockConnect connect_data1(SYNCHRONOUS, OK);
+  data1.set_connect_data(connect_data1);
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  AddSSLSocketData();
+
+  CreateNetworkSession();
+
+  // Open SpdySession to the first host.
+  base::WeakPtr<SpdySession> session0 = CreateSecureSpdySession(
+      http_session_.get(), test_hosts[0].key, NetLogWithSource());
+
+  // A request to the second host should pool to the existing connection.
+  base::WeakPtr<SpdySession> session1 =
+      spdy_session_pool_->FindAvailableSession(
+          test_hosts[1].key, GURL(),
+          /* enable_ip_based_pooling = */ true, NetLogWithSource());
+  EXPECT_EQ(session0.get(), session1.get());
+
+  // A request to the second host should not pool to the existing connection if
+  // IP based pooling is disabled.
+  session1 = spdy_session_pool_->FindAvailableSession(
+      test_hosts[1].key, GURL(),
+      /* enable_ip_based_pooling = */ false, NetLogWithSource());
+  EXPECT_FALSE(session1);
+
+  // It should be possible to open a new SpdySession, even if a previous call to
+  // FindAvailableSession() linked the second key to the first connection in the
+  // IP pooled bucket of SpdySessionPool::available_session_map_.
+  session1 = CreateSecureSpdySessionWithIpBasedPoolingDisabled(
+      http_session_.get(), test_hosts[1].key, NetLogWithSource());
+  EXPECT_TRUE(session1);
+  EXPECT_NE(session0.get(), session1.get());
+}
+
 // Construct a Pool with SpdySessions in various availability states. Simulate
 // an IP address change. Ensure sessions gracefully shut down. Regression test
 // for crbug.com/379469.
@@ -740,14 +815,17 @@
 
   // FindAvailableSession should return |session| if called with empty |url|.
   base::WeakPtr<SpdySession> session1 =
-      spdy_session_pool_->FindAvailableSession(key, GURL(), NetLogWithSource());
+      spdy_session_pool_->FindAvailableSession(
+          key, GURL(),
+          /* enable_ip_based_pooling = */ true, NetLogWithSource());
   EXPECT_EQ(session.get(), session1.get());
 
   // FindAvailableSession should return |session| if called with |url| for which
   // there is no pushed stream on any sessions owned by |spdy_session_pool_|.
   base::WeakPtr<SpdySession> session2 =
       spdy_session_pool_->FindAvailableSession(
-          key, GURL("http://news.example.org/foo.html"), NetLogWithSource());
+          key, GURL("http://news.example.org/foo.html"),
+          /* enable_ip_based_pooling = */ true, NetLogWithSource());
   EXPECT_EQ(session.get(), session2.get());
 
   spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED);
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index cc64f1b..7be6c367b 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -3428,8 +3428,9 @@
                                        NetLogWithSource());
   // Get a session for |key2|, which should return the session created earlier.
   base::WeakPtr<SpdySession> session2 =
-      spdy_session_pool_->FindAvailableSession(key2, GURL(),
-                                               NetLogWithSource());
+      spdy_session_pool_->FindAvailableSession(
+          key2, GURL(),
+          /* enable_ip_based_pooling = */ true, NetLogWithSource());
   ASSERT_EQ(session1.get(), session2.get());
   EXPECT_FALSE(pool->IsStalled());
 
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 1b2ef247..53a3bfe6 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -25,6 +25,7 @@
 #include "net/http/http_network_transaction.h"
 #include "net/http/http_server_properties_impl.h"
 #include "net/log/net_log_with_source.h"
+#include "net/socket/client_socket_handle.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/ssl_client_socket.h"
@@ -484,8 +485,9 @@
 }
 
 bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key) {
-  return static_cast<bool>(
-      pool->FindAvailableSession(key, GURL(), NetLogWithSource()));
+  return static_cast<bool>(pool->FindAvailableSession(
+      key, GURL(),
+      /* enable_ip_based_pooling = */ true, NetLogWithSource()));
 }
 
 namespace {
@@ -495,8 +497,10 @@
     const SpdySessionKey& key,
     const NetLogWithSource& net_log,
     Error expected_status,
-    bool is_secure) {
-  EXPECT_FALSE(HasSpdySession(http_session->spdy_session_pool(), key));
+    bool is_secure,
+    bool enable_ip_based_pooling) {
+  EXPECT_FALSE(http_session->spdy_session_pool()->FindAvailableSession(
+      key, GURL(), enable_ip_based_pooling, NetLogWithSource()));
 
   scoped_refptr<TransportSocketParams> transport_params(
       new TransportSocketParams(
@@ -552,8 +556,9 @@
     HttpNetworkSession* http_session,
     const SpdySessionKey& key,
     const NetLogWithSource& net_log) {
-  return CreateSpdySessionHelper(http_session, key, net_log,
-                                 OK, false /* is_secure */);
+  return CreateSpdySessionHelper(http_session, key, net_log, OK,
+                                 /* is_secure = */ false,
+                                 /* enable_ip_based_pooling = */ true);
 }
 
 base::WeakPtr<SpdySession> TryCreateSpdySessionExpectingFailure(
@@ -563,15 +568,26 @@
     const NetLogWithSource& net_log) {
   DCHECK_LT(expected_error, ERR_IO_PENDING);
   return CreateSpdySessionHelper(http_session, key, net_log, expected_error,
-                                 true /* is_secure */);
+                                 /* is_secure = */ true,
+                                 /* enable_ip_based_pooling = */ true);
 }
 
 base::WeakPtr<SpdySession> CreateSecureSpdySession(
     HttpNetworkSession* http_session,
     const SpdySessionKey& key,
     const NetLogWithSource& net_log) {
-  return CreateSpdySessionHelper(http_session, key, net_log,
-                                 OK, true /* is_secure */);
+  return CreateSpdySessionHelper(http_session, key, net_log, OK,
+                                 /* is_secure = */ true,
+                                 /* enable_ip_based_pooling = */ true);
+}
+
+base::WeakPtr<SpdySession> CreateSecureSpdySessionWithIpBasedPoolingDisabled(
+    HttpNetworkSession* http_session,
+    const SpdySessionKey& key,
+    const NetLogWithSource& net_log) {
+  return CreateSpdySessionHelper(http_session, key, net_log, OK,
+                                 /* is_secure = */ true,
+                                 /* enable_ip_based_pooling = */ false);
 }
 
 namespace {
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index 62a0564..7672ece5 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -258,6 +258,13 @@
     const SpdySessionKey& key,
     const NetLogWithSource& net_log);
 
+// Like CreateSecureSpdySession(), but does not fail if there is already an IP
+// pooled session for |key|.
+base::WeakPtr<SpdySession> CreateSecureSpdySessionWithIpBasedPoolingDisabled(
+    HttpNetworkSession* http_session,
+    const SpdySessionKey& key,
+    const NetLogWithSource& net_log);
+
 // Creates an insecure SPDY session for the given key and puts it in
 // |pool|. The returned session will neither receive nor send any
 // data. A SPDY session for |key| must not already exist.
diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc
index 9fba8c4..8768eaf 100644
--- a/net/tools/quic/quic_dispatcher.cc
+++ b/net/tools/quic/quic_dispatcher.cc
@@ -269,8 +269,7 @@
     return false;
   }
 
-  if (FLAGS_quic_reloadable_flag_quic_buffer_packets_after_chlo &&
-      buffered_packets_.HasChloForConnection(connection_id)) {
+  if (buffered_packets_.HasChloForConnection(connection_id)) {
     BufferEarlyPacket(connection_id);
     return false;
   }
@@ -718,23 +717,18 @@
       FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop &&
       new_sessions_allowed_per_event_loop_ <= 0) {
     // Can't create new session any more. Wait till next event loop.
-    if (!buffered_packets_.HasChloForConnection(current_connection_id_)) {
-      // Only buffer one CHLO per connection. Remove this condition check when
-      // --quic_reloadable_flag_quic_buffer_packets_after_chlo
-      // is deprecated because after that retransmitted CHLO should be buffered
-      // earlier in OnUnauthenticatedPublicHeader().
-      bool is_new_connection =
-          !buffered_packets_.HasBufferedPackets(current_connection_id_);
-      EnqueuePacketResult rs = buffered_packets_.EnqueuePacket(
-          current_connection_id_, *current_packet_, current_server_address_,
-          current_client_address_, /*is_chlo=*/true);
-      if (rs != EnqueuePacketResult::SUCCESS) {
-        OnBufferPacketFailure(rs, current_connection_id_);
-      } else if (
-          !FLAGS_quic_reloadable_flag_quic_create_session_after_insertion &&
-          is_new_connection) {
-        ShouldCreateOrBufferPacketForConnection(current_connection_id_);
-      }
+    QUIC_BUG_IF(buffered_packets_.HasChloForConnection(current_connection_id_));
+    bool is_new_connection =
+        !buffered_packets_.HasBufferedPackets(current_connection_id_);
+    EnqueuePacketResult rs = buffered_packets_.EnqueuePacket(
+        current_connection_id_, *current_packet_, current_server_address_,
+        current_client_address_, /*is_chlo=*/true);
+    if (rs != EnqueuePacketResult::SUCCESS) {
+      OnBufferPacketFailure(rs, current_connection_id_);
+    } else if (
+        !FLAGS_quic_reloadable_flag_quic_create_session_after_insertion &&
+        is_new_connection) {
+      ShouldCreateOrBufferPacketForConnection(current_connection_id_);
     }
     return;
   }
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index 621890ab..90b8e2a6 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -1471,10 +1471,6 @@
   ProcessPacket(client_addr_, last_connection, true, SerializeFullCHLO());
 
   size_t packets_buffered = 2;
-  if (!FLAGS_quic_reloadable_flag_quic_buffer_packets_after_chlo) {
-    // The packet sent above is dropped when flag is off.
-    packets_buffered = 1;
-  }
 
   // Reset counter and process buffered CHLO.
   EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, client_addr_))
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index 4a339f5..e586b50ac 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -732,7 +732,7 @@
   // Just ignore the message if it's received before Connect() is called. It's
   // likely to be a leftover from a previous session, so it's safe to ignore it.
   if (signal_strategy_)
-    signal_strategy_->OnIncomingMessage(iq);
+    signal_strategy_->GetIncomingMessageCallback().Run(iq);
 }
 
 void ChromotingInstance::HandleReleaseAllKeys(
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 41e8f75e..5636006 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -296,11 +296,14 @@
       return;
     }
 
-    delegating_signal_strategy_ = new DelegatingSignalStrategy(
-        local_jid, host_context_->network_task_runner(),
-        base::Bind(&It2MeNativeMessagingHost::SendOutgoingIq,
-                   weak_factory_.GetWeakPtr()));
-    signal_strategy.reset(delegating_signal_strategy_);
+    auto delegating_signal_strategy =
+        base::MakeUnique<DelegatingSignalStrategy>(
+            local_jid, host_context_->network_task_runner(),
+            base::Bind(&It2MeNativeMessagingHost::SendOutgoingIq,
+                       weak_factory_.GetWeakPtr()));
+    incoming_message_callback_ =
+        delegating_signal_strategy->GetIncomingMessageCallback();
+    signal_strategy = std::move(delegating_signal_strategy);
   }
 
   std::string directory_bot_jid = service_urls->directory_bot_jid();
@@ -357,8 +360,7 @@
     return;
   }
 
-  if (delegating_signal_strategy_)
-    delegating_signal_strategy_->OnIncomingMessage(iq);
+  incoming_message_callback_.Run(iq);
   SendMessageToClient(std::move(response));
 };
 
diff --git a/remoting/host/it2me/it2me_native_messaging_host.h b/remoting/host/it2me/it2me_native_messaging_host.h
index 8560084..b42cda0 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.h
+++ b/remoting/host/it2me/it2me_native_messaging_host.h
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "extensions/browser/api/messaging/native_message_host.h"
 #include "remoting/host/it2me/it2me_host.h"
+#include "remoting/signaling/delegating_signal_strategy.h"
 
 #if !defined(OS_CHROMEOS)
 #include "remoting/host/native_messaging/log_message_handler.h"
@@ -28,7 +29,6 @@
 namespace remoting {
 
 class ChromotingHostContext;
-class DelegatingSignalStrategy;
 class ElevatedNativeMessagingHost;
 class PolicyWatcher;
 
@@ -94,7 +94,7 @@
 #endif  // defined(OS_WIN)
 
   Client* client_ = nullptr;
-  DelegatingSignalStrategy* delegating_signal_strategy_ = nullptr;
+  DelegatingSignalStrategy::IqCallback incoming_message_callback_;
   std::unique_ptr<ChromotingHostContext> host_context_;
   std::unique_ptr<It2MeHostFactory> factory_;
   scoped_refptr<It2MeHost> it2me_host_;
diff --git a/remoting/signaling/delegating_signal_strategy.cc b/remoting/signaling/delegating_signal_strategy.cc
index 83f23907..0f6dc7d 100644
--- a/remoting/signaling/delegating_signal_strategy.cc
+++ b/remoting/signaling/delegating_signal_strategy.cc
@@ -16,23 +16,41 @@
 DelegatingSignalStrategy::DelegatingSignalStrategy(
     std::string local_jid,
     scoped_refptr<base::SingleThreadTaskRunner> client_task_runner,
-    const SendIqCallback& send_iq_callback)
+    const IqCallback& send_iq_callback)
     : local_jid_(local_jid),
       delegate_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       client_task_runner_(client_task_runner),
       send_iq_callback_(send_iq_callback),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  incoming_iq_callback_ =
+      base::BindRepeating(&OnIncomingMessageFromDelegate,
+                          weak_factory_.GetWeakPtr(), client_task_runner_);
+}
 
 DelegatingSignalStrategy::~DelegatingSignalStrategy() {}
 
-void DelegatingSignalStrategy::OnIncomingMessage(const std::string& message) {
-  if (!client_task_runner_->BelongsToCurrentThread()) {
-    client_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&DelegatingSignalStrategy::OnIncomingMessage,
-                              weak_factory_.GetWeakPtr(), message));
+DelegatingSignalStrategy::IqCallback
+DelegatingSignalStrategy::GetIncomingMessageCallback() {
+  return incoming_iq_callback_;
+}
+
+// static
+void DelegatingSignalStrategy::OnIncomingMessageFromDelegate(
+    base::WeakPtr<DelegatingSignalStrategy> weak_ptr,
+    scoped_refptr<base::SingleThreadTaskRunner> client_task_runner,
+    const std::string& message) {
+  if (client_task_runner->BelongsToCurrentThread()) {
+    weak_ptr->OnIncomingMessage(message);
     return;
   }
 
+  client_task_runner->PostTask(
+      FROM_HERE, base::Bind(&DelegatingSignalStrategy::OnIncomingMessage,
+                            weak_ptr, message));
+}
+
+void DelegatingSignalStrategy::OnIncomingMessage(const std::string& message) {
+  DCHECK(client_task_runner_->BelongsToCurrentThread());
   std::unique_ptr<buzz::XmlElement> stanza(buzz::XmlElement::ForStr(message));
   if (!stanza.get()) {
     LOG(WARNING) << "Malformed XMPP stanza received: " << message;
diff --git a/remoting/signaling/delegating_signal_strategy.h b/remoting/signaling/delegating_signal_strategy.h
index 0b6d927..d41de42 100644
--- a/remoting/signaling/delegating_signal_strategy.h
+++ b/remoting/signaling/delegating_signal_strategy.h
@@ -21,25 +21,26 @@
 //
 // Notes on thread safety:
 // 1. This object can be created on any thread.
-// 2. OnIncomingMessage() must be called on the same thread on which this object
-//    is created.
-// 3. |send_iq_callback| will always be called on the thread that it is created.
+// 2. |send_iq_callback| will always be called on the thread that it is created.
 //    Note that |send_iq_callback| may be called after this object is destroyed.
-// 4. The caller should invoke all methods on the SignalStrategy interface on
+// 3. The caller should invoke all methods on the SignalStrategy interface on
 //    the |client_task_runner|.
-// 5. All listeners will be called on |client_task_runner| as well.
-// 6. The destructor should always be called on the |client_task_runner|.
+// 4. All listeners will be called on |client_task_runner| as well.
+// 5. The destructor should always be called on the |client_task_runner|.
+// 6. As a result of (5), use MakeIncomingMessageCallback() to obtain a callback
+//    when passing incoming signaling messages from the delegate.  The callback
+//    can then be invoked at any thread.
 class DelegatingSignalStrategy : public SignalStrategy {
  public:
-  typedef base::Callback<void(const std::string&)> SendIqCallback;
+  typedef base::RepeatingCallback<void(const std::string&)> IqCallback;
 
   DelegatingSignalStrategy(
       std::string local_jid,
       scoped_refptr<base::SingleThreadTaskRunner> client_task_runner,
-      const SendIqCallback& send_iq_callback);
+      const IqCallback& send_iq_callback);
   ~DelegatingSignalStrategy() override;
 
-  void OnIncomingMessage(const std::string& message);
+  IqCallback GetIncomingMessageCallback();
 
   // SignalStrategy interface.
   void Connect() override;
@@ -53,11 +54,19 @@
   std::string GetNextId() override;
 
  private:
+  static void OnIncomingMessageFromDelegate(
+      base::WeakPtr<DelegatingSignalStrategy> weak_ptr,
+      scoped_refptr<base::SingleThreadTaskRunner> client_task_runner,
+      const std::string& message);
+
+  void OnIncomingMessage(const std::string& message);
+
   std::string local_jid_;
   scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> client_task_runner_;
 
-  SendIqCallback send_iq_callback_;
+  IqCallback incoming_iq_callback_;
+  IqCallback send_iq_callback_;
   base::ObserverList<Listener> listeners_;
 
   base::WeakPtrFactory<DelegatingSignalStrategy> weak_factory_;
diff --git a/services/image_decoder/image_decoder_impl.cc b/services/image_decoder/image_decoder_impl.cc
index 30c2fe9..53aa56a 100644
--- a/services/image_decoder/image_decoder_impl.cc
+++ b/services/image_decoder/image_decoder_impl.cc
@@ -15,7 +15,7 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 
 #if defined(OS_CHROMEOS)
-#include "ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h"
+#include "ui/gfx/codec/chromeos/jpeg_codec_robust_slow.h"
 #include "ui/gfx/codec/png_codec.h"
 #endif
 
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 572edd2..cdce1fd 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -220,10 +220,6 @@
 #   define SK_USE_LEGACY_DISTANCE_FIELDS
 #endif
 
-#ifndef    SK_SUPPORT_LEGACY_CLIPOP_EXOTIC_NAMES
-#   define SK_SUPPORT_LEGACY_CLIPOP_EXOTIC_NAMES
-#endif
-
 #ifndef SK_SUPPORT_LEGACY_PATHEFFECT_SUBCLASSES
 #define SK_SUPPORT_LEGACY_PATHEFFECT_SUBCLASSES
 #endif
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 824caef..ddf9de8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -17,7 +17,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -49,7 +49,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -80,7 +80,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -111,7 +111,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -143,7 +143,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -174,7 +174,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -205,7 +205,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -239,7 +239,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -271,7 +271,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -302,7 +302,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -336,7 +336,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -368,7 +368,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -399,7 +399,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -430,7 +430,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -461,7 +461,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -492,7 +492,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -523,7 +523,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -555,7 +555,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -586,7 +586,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -617,7 +617,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -648,7 +648,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -679,7 +679,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -711,7 +711,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -743,7 +743,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -775,7 +775,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -807,7 +807,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -842,7 +842,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -874,7 +874,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -905,7 +905,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -936,7 +936,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -968,7 +968,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -999,7 +999,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1030,7 +1030,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1061,7 +1061,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1092,7 +1092,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1123,7 +1123,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1154,7 +1154,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:3ff24775a900b675866fbcacf2a8f98a18b2a16a"
             }
           ],
           "dimension_sets": [
@@ -1333,7 +1333,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1366,7 +1366,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1402,7 +1402,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1435,7 +1435,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1468,7 +1468,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1501,7 +1501,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1534,7 +1534,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1567,7 +1567,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1600,7 +1600,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1633,7 +1633,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1666,7 +1666,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1699,7 +1699,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1732,7 +1732,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1765,7 +1765,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1798,7 +1798,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1831,7 +1831,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1864,7 +1864,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1897,7 +1897,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1930,7 +1930,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1966,7 +1966,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1999,7 +1999,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2032,7 +2032,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2065,7 +2065,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2098,7 +2098,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2131,7 +2131,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2164,7 +2164,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2197,7 +2197,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2233,7 +2233,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2269,7 +2269,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2305,7 +2305,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2341,7 +2341,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2483,7 +2483,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2517,7 +2517,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2548,7 +2548,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2579,7 +2579,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2613,7 +2613,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2644,7 +2644,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2675,7 +2675,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2710,7 +2710,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2744,7 +2744,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2775,7 +2775,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2810,7 +2810,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2841,7 +2841,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2876,7 +2876,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2907,7 +2907,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2942,7 +2942,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2976,7 +2976,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3007,7 +3007,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3042,7 +3042,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3073,7 +3073,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3104,7 +3104,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3135,7 +3135,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3166,7 +3166,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3197,7 +3197,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3228,7 +3228,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3259,7 +3259,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3290,7 +3290,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3321,7 +3321,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3352,7 +3352,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3386,7 +3386,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3417,7 +3417,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3448,7 +3448,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -3479,7 +3479,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index c0b5ad2..1c859c24 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -11473,6 +11473,22 @@
       }
     ]
   },
+  "Ozone Linux": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mus_demo_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mus_ws_unittests"
+      }
+    ]
+  },
   "Site Isolation Android": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index e05e1bf..ca3449d 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -33,7 +33,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -65,7 +65,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -96,7 +96,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -127,7 +127,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -159,7 +159,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -190,7 +190,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -221,7 +221,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -255,7 +255,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -290,7 +290,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -322,7 +322,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -353,7 +353,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -384,7 +384,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -419,7 +419,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -451,7 +451,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -482,7 +482,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -513,7 +513,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -544,7 +544,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -575,7 +575,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -607,7 +607,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -638,7 +638,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -669,7 +669,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -700,7 +700,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -731,7 +731,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -763,7 +763,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -795,7 +795,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -827,7 +827,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -859,7 +859,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -894,7 +894,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -926,7 +926,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -957,7 +957,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -988,7 +988,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1019,7 +1019,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1050,7 +1050,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1081,7 +1081,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1112,7 +1112,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1143,7 +1143,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1248,7 +1248,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1280,7 +1280,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1311,7 +1311,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1342,7 +1342,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1374,7 +1374,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1405,7 +1405,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1436,7 +1436,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1470,7 +1470,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1505,7 +1505,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1537,7 +1537,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1568,7 +1568,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1599,7 +1599,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1634,7 +1634,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1666,7 +1666,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1697,7 +1697,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1728,7 +1728,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1759,7 +1759,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1790,7 +1790,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1822,7 +1822,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1853,7 +1853,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1884,7 +1884,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1915,7 +1915,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1946,7 +1946,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -1978,7 +1978,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2010,7 +2010,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2042,7 +2042,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2074,7 +2074,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2109,7 +2109,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2141,7 +2141,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2172,7 +2172,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2203,7 +2203,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2235,7 +2235,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2266,7 +2266,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2297,7 +2297,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2328,7 +2328,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2359,7 +2359,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
@@ -2390,7 +2390,7 @@
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
             }
           ],
           "dimension_sets": [
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 5b775b2..1500fd9 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -65,6 +65,9 @@
 crbug.com/662477 external/wpt/html/webappapis/idle-callbacks/cancel-invoked.html [ Leak ]
 crbug.com/662477 external/wpt/html/webappapis/scripting/events/onerroreventhandler.html [ Leak ]
 
+# Likely caused by http://crbug.com/501866.
+crbug.com/701695 external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html [ Leak ]
+
 # -----------------------------------------------------------------
 # Untriaged but known leaks of ActiveDOMObject (fast).
 # -----------------------------------------------------------------
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index a9a0d99..61a76cc4 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -7,9 +7,6 @@
 # failure. Expected outputs will be adjusted for the better once Oilpan
 # has been well and truly enabled always.
 
-crbug.com/420008 inspector/tracing/worker-js-frames.html [ Pass Timeout Failure ]
-crbug.com/420008 virtual/threaded/inspector/tracing/worker-js-frames.html [ Pass Timeout Failure ]
-
 crbug.com/569901 crbug.com/24182 [ Debug ] jquery/manipulation.html [ Timeout Pass ]
 
 # ====== Oilpan-only failures until here ======
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/password-delete-contents-expected.txt b/third_party/WebKit/LayoutTests/editing/deleting/password-delete-contents-expected.txt
new file mode 100644
index 0000000..76926963
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/deleting/password-delete-contents-expected.txt
@@ -0,0 +1,7 @@
+This makes sure we are able to delete the contents of a password field: deleting a selection and evaluating the field contents.
+
+PASS passwordField.value='helllo'; passwordField.setSelectionRange(3, 4); testRunner.execCommand('Delete', false, null); passwordField.value is 'hello'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/password-delete-contents.html b/third_party/WebKit/LayoutTests/editing/deleting/password-delete-contents.html
new file mode 100644
index 0000000..3e4461b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/deleting/password-delete-contents.html
@@ -0,0 +1,32 @@
+<!DOCTYPE>
+<html>
+<head>
+<script src="../../resources/js-test.js"></script>
+</head>
+<body>
+<p id="description">This makes sure we are able to delete the contents of a password field: deleting a selection and evaluating the field contents.</p>
+<div id="console"></div>
+<input type="password" id="passwordField">
+<script>
+
+if (!window.testRunner || !window.internals)
+    testFailed('This test requires access to window.internals');
+
+var textField;
+var desiredString = "hello";
+function runTest(element) {
+    textField = element;
+
+    textField.value = "helllo";
+    textField.focus();
+
+    shouldBe("passwordField.value='helllo'; passwordField.setSelectionRange(3, 4); testRunner.execCommand('Delete', false, null); passwordField.value", "'hello'");
+
+    textField.parentNode.removeChild(textField);
+}
+
+runTest(document.getElementById('passwordField'));
+
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/password-delete-performance-expected.txt b/third_party/WebKit/LayoutTests/editing/deleting/password-delete-performance-expected.txt
deleted file mode 100644
index b6aa4213..0000000
--- a/third_party/WebKit/LayoutTests/editing/deleting/password-delete-performance-expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-This test ensures that deleting characters from a password field that follows large content blocks is not slow. To run the test manually, delete the character from the password field. The user agent should not freeze.
-
-
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/password-delete-performance.html b/third_party/WebKit/LayoutTests/editing/deleting/password-delete-performance.html
deleted file mode 100644
index ce2520f..0000000
--- a/third_party/WebKit/LayoutTests/editing/deleting/password-delete-performance.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <body>
-        <p id="description">This test ensures that deleting characters from a password field that follows large content blocks is not slow.
-        To run the test manually, delete the character from the password field. The user agent should not freeze.</p>
-
-        <div id="content" style="height:0px; overflow:hidden;"> </div>
-        <input id="field" type="password" value="A">
-
-        <script src="../editing.js"></script>
-        <script>
-            if (window.testRunner)
-                testRunner.dumpAsText();
-
-            var newContent = '<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p>';
-            for (var i = 0; i < 13; i++) {
-                newContent += newContent;
-            }
-
-            var contentDiv = document.getElementById('content');
-            contentDiv.innerHTML = newContent;
-
-            document.getElementById("field").focus();
-            document.execCommand("Delete");
-
-            // We clear the content div to avoid having its content appear in the test harness output.
-            if (window.testRunner)
-                contentDiv.innerHTML = "";
-        </script>
-    </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 258ed15..bafc7dc0 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -11550,6 +11550,11 @@
      {}
     ]
    ],
+   "html/browsers/windows/browsing-context-names/browsing-context-_blank-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "html/browsers/windows/browsing-context-names/existing.html": [
     [
      {}
@@ -41454,6 +41459,18 @@
      {}
     ]
    ],
+   "html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html": [
+    [
+     "/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html",
+     {}
+    ]
+   ],
+   "html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js": [
+    [
+     "/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.html",
+     {}
+    ]
+   ],
    "html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html": [
     [
      "/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html",
@@ -65786,7 +65803,7 @@
    "support"
   ],
   "html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html": [
-   "3164df75ba277feef3b5c9a1ff6021f3e51d48de",
+   "4d0e0901dd42a40d86c7263ac7ac432059b6e84e",
    "testharness"
   ],
   "html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html": [
@@ -67133,6 +67150,10 @@
    "ed586ee147452ba6cb8d90782b266807f8b7975e",
    "testharness"
   ],
+  "html/browsers/windows/browsing-context-names/browsing-context-_blank-expected.txt": [
+   "c07a576dcc76bb8d6271442116909f0be5098a77",
+   "support"
+  ],
   "html/browsers/windows/browsing-context-names/browsing-context-_blank.html": [
    "4a1a9ff6c913291edce2339faa443b6da2e5fe74",
    "testharness"
@@ -76913,6 +76934,14 @@
    "ef22b322a16f835c562a0aa4575361f0fb53754a",
    "testharness"
   ],
+  "html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html": [
+   "378f086b3f8e2ea3443be95ee352d9d96604ccd0",
+   "testharness"
+  ],
+  "html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js": [
+   "3e8715be8b85623793e9bc18b1202616a5353e10",
+   "testharness"
+  ],
   "html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html": [
    "e69a2ad6b52db467bab6baee7485809fec35893c",
    "testharness"
@@ -77898,7 +77927,7 @@
    "testharness"
   ],
   "mediacapture-streams/MediaStreamTrack-init.https-expected.txt": [
-   "e06d937ccc5c05632bc1ce90cb8302d416be5480",
+   "4e890a8c710bbc3100a3c3fc68a2292cff4afa58",
    "support"
   ],
   "mediacapture-streams/MediaStreamTrack-init.https.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt
index df8492b..dab3404 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt
@@ -1,6 +1,8 @@
 This is a testharness.js-based test.
 PASS Returning a string must not cancel the event: CustomEvent, non-cancelable 
 PASS Returning a string must not cancel the event: CustomEvent, cancelable 
+FAIL Returning false must cancel the event, because it's coerced to the DOMString "false" which does not cancel CustomEvents: CustomEvent, cancelable assert_false: The event must not have been canceled expected false got true
+PASS Returning a string must not cancel the event: BeforeUnloadEvent with type "click", cancelable 
 PASS Returning null with a real iframe unloading 
 PASS Returning undefined with a real iframe unloading 
 FAIL Returning  with a real iframe unloading assert_equals: canceled expected true but got false
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
index b415ac2..1f8bb059 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
@@ -11,43 +11,82 @@
 <script>
 "use strict";
 
-async_test(t => {
+promise_test(t => {
   let onbeforeunloadHappened = false;
   window.onbeforeunload = t.step_func(() => {
     onbeforeunloadHappened = true;
     return "cancel me";
   });
 
-  const listener = t.step_func(e => {
+  const eventWatcher = new EventWatcher(t, window, "beforeunload");
+  const promise = eventWatcher.wait_for("beforeunload").then(e => {
     assert_true(onbeforeunloadHappened, "CustomEvent must be able to trigger the event handler");
     assert_false(e.defaultPrevented, "The event must not have been canceled");
     window.onbeforeunload = null;
-    t.done();
   });
 
-  window.addEventListener("beforeunload", listener);
-
   window.dispatchEvent(new CustomEvent("beforeunload"));
+
+  return promise;
 }, "Returning a string must not cancel the event: CustomEvent, non-cancelable");
 
-async_test(t => {
+promise_test(t => {
   let onbeforeunloadHappened = false;
   window.onbeforeunload = t.step_func(() => {
     onbeforeunloadHappened = true;
     return "cancel me";
   });
 
-  const listener = t.step_func(e => {
+  const eventWatcher = new EventWatcher(t, window, "beforeunload");
+  const promise = eventWatcher.wait_for("beforeunload").then(e => {
     assert_true(onbeforeunloadHappened, "CustomEvent must be able to trigger the event handler");
     assert_false(e.defaultPrevented, "The event must not have been canceled");
     window.onbeforeunload = null;
     t.done();
   });
 
-  window.addEventListener("beforeunload", listener);
+  window.dispatchEvent(new CustomEvent("beforeunload", { cancelable: true }));
+
+  return promise;
+}, "Returning a string must not cancel the event: CustomEvent, cancelable");
+
+promise_test(t => {
+  let onbeforeunloadHappened = false;
+  window.onbeforeunload = t.step_func(() => {
+    onbeforeunloadHappened = true;
+    return false;
+  });
+
+  const eventWatcher = new EventWatcher(t, window, "beforeunload");
+  const promise = eventWatcher.wait_for("beforeunload").then(e => {
+    assert_true(onbeforeunloadHappened, "CustomEvent must be able to trigger the event handler");
+    assert_false(e.defaultPrevented, "The event must not have been canceled");
+    window.onbeforeunload = null;
+    t.done();
+  });
 
   window.dispatchEvent(new CustomEvent("beforeunload", { cancelable: true }));
-}, "Returning a string must not cancel the event: CustomEvent, cancelable");
+
+  return promise;
+}, "Returning false must cancel the event, because it's coerced to the DOMString \"false\" which does not cancel " +
+   "CustomEvents: CustomEvent, cancelable");
+
+// This test can be removed if we update the DOM Standard to disallow createEvent("BeforeUnloadEvent"). Browser support
+// is inconsistent. https://github.com/whatwg/dom/issues/362
+promise_test(t => {
+  const eventWatcher = new EventWatcher(t, window, "click");
+  const promise = eventWatcher.wait_for("click").then(e => {
+    assert_false(e.defaultPrevented, "The event must not have been canceled");
+    window.onbeforeunload = null;
+    t.done();
+  });
+
+  const ev = document.createEvent("BeforeUnloadEvent");
+  ev.initEvent("click", false, true);
+  window.dispatchEvent(ev);
+
+  return promise;
+}, "Returning a string must not cancel the event: BeforeUnloadEvent with type \"click\", cancelable");
 
 const testCases = [
   {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html
new file mode 100644
index 0000000..75a1772
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: click events using ErrorEvent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+promise_test(t => {
+  document.onclick = t.step_func((...args) => {
+    assert_equals(args.length, 1);
+    return true;
+  });
+
+  const eventWatcher = new EventWatcher(t, document, "click");
+  const promise = eventWatcher.wait_for("click").then(e => {
+    assert_equals(e.defaultPrevented, false);
+  });
+
+  document.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+  return promise;
+}, "click event is normal (return true does not cancel; one arg) on Document, with a synthetic ErrorEvent");
+
+promise_test(t => {
+  window.onclick = t.step_func((...args) => {
+    assert_equals(args.length, 1);
+    return true;
+  });
+
+  const eventWatcher = new EventWatcher(t, window, "click");
+  const promise = eventWatcher.wait_for("click").then(e => {
+    assert_equals(e.defaultPrevented, false);
+  });
+
+  window.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+  return promise;
+}, "click event is normal (return true does not cancel; one arg) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+  const el = document.createElement("script");
+  el.onclick = t.step_func((...args) => {
+    assert_equals(args.length, 1);
+    return true;
+  });
+
+  const eventWatcher = new EventWatcher(t, el, "click");
+  const promise = eventWatcher.wait_for("click").then(e => {
+    assert_equals(e.defaultPrevented, false);
+  });
+
+  el.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+  return promise;
+}, "click event is normal (return true does not cancel; one arg) on a script element, with a synthetic ErrorEvent");
+
+promise_test(t => {
+  const worker = new Worker("resources/no-op-worker.js");
+  worker.onerror = t.step_func((...args) => {
+    assert_equals(args.length, 1);
+    return true;
+  });
+
+  const eventWatcher = new EventWatcher(t, worker, "click");
+  const promise = eventWatcher.wait_for("click").then(e => {
+    assert_equals(e.defaultPrevented, false);
+  });
+
+  worker.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+  return promise;
+}, "click event is normal (return true does not cancel; one arg) on Worker, with a synthetic ErrorEvent");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js
new file mode 100644
index 0000000..177a99e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js
@@ -0,0 +1,22 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+  self.onerror = t.step_func((...args) => {
+    assert_equals(args.length, 1);
+    return true;
+  });
+
+  const eventWatcher = new EventWatcher(t, self, "click");
+  const promise = eventWatcher.wait_for("click").then(e => {
+    assert_equals(e.defaultPrevented, false);
+  });
+
+  self.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+  return promise;
+}, "error event is normal (return true does not cancel; one arg) on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+done();
diff --git a/third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-transpose.html b/third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-transpose.html
new file mode 100644
index 0000000..0f617a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-transpose.html
@@ -0,0 +1,29 @@
+<title>InputEvent: macOS Transpose</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div id="editable" contenteditable></div>
+<script>
+test(() => {
+    assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
+
+    let eventRecorder = '';
+    document.addEventListener('beforeinput', event => {
+        eventRecorder += `beforeinput-${event.inputType}-${event.data}-`;
+    });
+    document.addEventListener('input', event => {
+        eventRecorder += `input-${event.inputType}`;
+    });
+
+    const editable = document.getElementById('editable');
+    editable.innerHTML = 'abc';
+    editable.focus();
+    const selection = window.getSelection();
+    selection.collapse(editable, 1); // End of first line.
+
+    // Test Transpose.
+    eventRecorder = '';
+    testRunner.execCommand('transpose');
+    assert_equals(editable.innerHTML, 'acb');
+    assert_equals(eventRecorder, 'beforeinput-insertTranspose-cb-input-insertTranspose');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-navigation.html b/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-navigation.html
index 35ba19e8..4052d5b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-navigation.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-navigation.html
@@ -24,7 +24,7 @@
 
 function test()
 {
-    for (var message of InspectorTest.mainTarget.consoleModel.messages()) {
+    for (var message of SDK.consoleModel.messages()) {
         var args = (message.parameters || []).map((arg) => arg.type);
         InspectorTest.addResult("Message: \"" + message.messageText + "\", arguments: [" + args.join(", ") + "]");
     }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-remove.html b/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-remove.html
index b17cf21e..fb74a8a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-remove.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/console-clear-arguments-on-frame-remove.html
@@ -31,7 +31,7 @@
 
 function test()
 {
-    for (var message of InspectorTest.mainTarget.consoleModel.messages()) {
+    for (var message of SDK.consoleModel.messages()) {
         var args = (message.parameters || []).map((arg) => arg.type);
         InspectorTest.addResult("Message: \"" + message.messageText + "\", arguments: [" + args.join(", ") + "]");
     }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/console/console-links-on-messages-before-inspection.html b/third_party/WebKit/LayoutTests/http/tests/inspector/console/console-links-on-messages-before-inspection.html
index 5eb8f34..390ee4d0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/console/console-links-on-messages-before-inspection.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/console/console-links-on-messages-before-inspection.html
@@ -16,7 +16,7 @@
     var mainTarget = SDK.targetManager.mainTarget();
     var debuggerModel = SDK.DebuggerModel.fromTarget(mainTarget);
     var message = new SDK.ConsoleMessage(mainTarget, SDK.ConsoleMessage.MessageSource.JS, SDK.ConsoleMessage.MessageLevel.Info, "hello?", null, "http://127.0.0.1:8000/inspector/resources/source2.js");
-    mainTarget.consoleModel.addMessage(message);
+    SDK.consoleModel.addMessage(message);
     debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, onScriptAdded);
 
     InspectorTest.dumpConsoleMessages();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js
index 7fe4492..b7a4b9c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js
@@ -971,7 +971,6 @@
         InspectorTest.RuntimeAgent = target.runtimeAgent();
         InspectorTest.TargetAgent = target.targetAgent();
 
-        InspectorTest.consoleModel = target.consoleModel;
         InspectorTest.networkManager = SDK.NetworkManager.fromTarget(target);
         InspectorTest.securityOriginManager = SDK.SecurityOriginManager.fromTarget(target);
         InspectorTest.resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target);
@@ -988,6 +987,7 @@
         InspectorTest.serviceWorkerManager = target.model(SDK.ServiceWorkerManager);
         InspectorTest.tracingManager = target.model(SDK.TracingManager);
         InspectorTest.mainTarget = target;
+        InspectorTest.consoleModel = SDK.consoleModel;
     },
 
     targetRemoved: function(target) { }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/user-agent-override.html b/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/user-agent-override.html
index 355837e..61d0ab08 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/user-agent-override.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/user-agent-override.html
@@ -30,13 +30,13 @@
     function waitForConsoleMessage(regex)
     {
         return new Promise(function(resolve) {
-            SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, sniff);
+            SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, sniff);
 
             function sniff(e)
             {
                 if (e.data && regex.test(e.data.messageText)) {
                     resolve(e.data);
-                    SDK.multitargetConsoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, sniff);
+                    SDK.consoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, sniff);
                 }
             }
         });
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-preserve-log.html b/third_party/WebKit/LayoutTests/inspector/console/console-preserve-log.html
index e01384fa..5e9cb3d 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-preserve-log.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-preserve-log.html
@@ -5,7 +5,7 @@
 <script>
 function test()
 {
-    InspectorTest.consoleModel.addMessage(new SDK.ConsoleMessage(InspectorTest.consoleModel.target(), SDK.ConsoleMessage.MessageSource.Other, SDK.ConsoleMessage.MessageLevel.Info, "PASS"));
+    InspectorTest.consoleModel.addMessage(new SDK.ConsoleMessage(InspectorTest.mainTarget, SDK.ConsoleMessage.MessageSource.Other, SDK.ConsoleMessage.MessageLevel.Info, "PASS"));
     Common.settingForTest("preserveConsoleLog").set(true);
     InspectorTest.reloadPage(function() {
         InspectorTest.dumpConsoleMessages();
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker.html b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker.html
index 50fcf62..341c25c 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker.html
@@ -18,8 +18,8 @@
 
 function test()
 {
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, InspectorTest.wrapListener(messageAdded));
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageUpdated, InspectorTest.wrapListener(messageUpdated));
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, InspectorTest.wrapListener(messageAdded));
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageUpdated, InspectorTest.wrapListener(messageUpdated));
 
     InspectorTest.addResult("Creating worker with promise");
     InspectorTest.evaluateInPageWithTimeout("createPromise()");
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-timestamp.html b/third_party/WebKit/LayoutTests/inspector/console/console-timestamp.html
index 634b5d0..1bc866e5 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-timestamp.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-timestamp.html
@@ -14,7 +14,7 @@
     function addMessageWithFixedTimestamp(messageText, timestamp)
     {
         var message = new SDK.ConsoleMessage(
-            InspectorTest.consoleModel.target(),
+            InspectorTest.mainTarget,
             SDK.ConsoleMessage.MessageSource.Other, // source
             SDK.ConsoleMessage.MessageLevel.Info, // level
             messageText,
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
index f024d78..4945678 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
+++ b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
@@ -64,11 +64,11 @@
         InspectorTest.disableConsoleViewport();
         InspectorTest.evaluateInPage("logMessage()");
         var wrappedConsoleMessageAdded = InspectorTest.safeWrap(consoleMessageAdded);
-        SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, wrappedConsoleMessageAdded);
+        SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, wrappedConsoleMessageAdded);
 
         function consoleMessageAdded()
         {
-            SDK.multitargetConsoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, wrappedConsoleMessageAdded);
+            SDK.consoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, wrappedConsoleMessageAdded);
             Console.ConsoleView.instance()._viewportThrottler.flush();
             InspectorTest.deprecatedRunAfterPendingDispatches(clickOnMessage)
         }
diff --git a/third_party/WebKit/LayoutTests/inspector/input-event-warning.html b/third_party/WebKit/LayoutTests/inspector/input-event-warning.html
index 75df9f9..0822b324 100644
--- a/third_party/WebKit/LayoutTests/inspector/input-event-warning.html
+++ b/third_party/WebKit/LayoutTests/inspector/input-event-warning.html
@@ -62,7 +62,7 @@
 
 function test()
 {
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, InspectorTest.safeWrap(onConsoleMessage));
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, InspectorTest.safeWrap(onConsoleMessage));
     step1();
 
     function step1()
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/show-function-definition.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/show-function-definition.html
index df0c230c..59e642c 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/show-function-definition.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/show-function-definition.html
@@ -40,7 +40,7 @@
         function testDumpFunctionDefinition(next)
         {
             InspectorTest.addSniffer(ObjectUI.ObjectPropertiesSection, "formatObjectAsFunction", onConsoleMessagesReceived);
-            SDK.ConsoleModel.evaluateCommandInConsole(UI.context.flavor(SDK.ExecutionContext), "jumpToMe");
+            SDK.consoleModel.evaluateCommandInConsole(UI.context.flavor(SDK.ExecutionContext), "jumpToMe");
 
             function onConsoleMessagesReceived()
             {
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames-expected.txt
index b0f4608..add010f 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames-expected.txt
@@ -1,27 +1,6 @@
 Tests js cpu profile in timeline.
 
 Main Thread
-EvaluateScript Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "EvaluateScript"
-}
 DedicatedWorker Thread
 DedicatedWorker Thread
-EvaluateScript Properties:
-{
-    data : {
-        columnNumber : <number>
-        frame : <string>
-        lineNumber : <number>
-        url : .../inspector/tracing/resources/worker.js
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "EvaluateScript"
-}
 
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames.html b/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames.html
index 871225a7..caa7a8e4 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/worker-js-frames.html
@@ -67,32 +67,28 @@
     {
         var mainThread = { name: "Main Thread", events: InspectorTest.timelineModel()._mainThreadEvents };
         processThread(new Set(["startSecondWorker", "worker2.onmessage"]), mainThread);
-        InspectorTest.timelineModel()._virtualThreads.forEach(function(thread)
-        {
+        for (var thread of InspectorTest.timelineModel()._virtualThreads) {
             if (!thread.isWorker())
-                return;
-            processThread(new Set(["onmessage"]), thread);
-        });
+                continue;
+            processThread(new Set(["Function Call"]), thread);
+        }
         InspectorTest.completeTest();
     }
 
-    function processThread(expectedFunctions, thread)
+    function processThread(expectedEvents, thread)
     {
         InspectorTest.addResult(thread.name);
-        var missingFunctions = thread.events.reduce(processEvent, expectedFunctions);
-        if (missingFunctions.size) {
-            InspectorTest.addResult("FAIL: missing functions:");
-            missingFunctions.forEach(InspectorTest.addResult);
+        var missingEvents = thread.events.reduce(processEvent, expectedEvents);
+        if (missingEvents.size) {
+            InspectorTest.addResult("FAIL: missing events:");
+            missingEvents.forEach(InspectorTest.addResult);
         }
     }
 
-    function processEvent(expectedFunctions, event)
+    function processEvent(expectedEvents, event)
     {
-        if (event.name === TimelineModel.TimelineModel.RecordType.EvaluateScript)
-            InspectorTest.printTraceEventProperties(event);
-        if (event.name === TimelineModel.TimelineModel.RecordType.JSFrame)
-            expectedFunctions.delete(event.args.data.functionName);
-        return expectedFunctions;
+        expectedEvents.delete(Timeline.TimelineUIUtils.eventTitle(event));
+        return expectedEvents;
     }
 }
 
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index 640e9505..93b2dc5 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 284.00 scrollHeight 842
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 284.00 scrollHeight 842
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-242) size 559x842 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-242) size 559x842 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 558.97x842
     LayoutBlockFlow (anonymous) at (0,0) size 558.97x20
       LayoutText {#text} at (0,0) size 559x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index e4d13bce2..504b9d26 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 278.00 scrollHeight 836
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 278.00 scrollHeight 836
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-236) size 570x836 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-236) size 570x836 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 570.41x836
     LayoutBlockFlow (anonymous) at (0,0) size 570.41x18
       LayoutText {#text} at (0,0) size 571x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index 9faa65ef..f8ebb28 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 278.00 scrollHeight 836
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 278.00 scrollHeight 836
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-236) size 570x836 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-236) size 570x836 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 570.41x836
     LayoutBlockFlow (anonymous) at (0,0) size 570.41x18
       LayoutText {#text} at (0,0) size 571x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index cbf97bd..9e0ca43 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 278.00 scrollHeight 836
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 278.00 scrollHeight 836
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-236) size 570x836 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-236) size 570x836 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 570.41x836
     LayoutBlockFlow (anonymous) at (0,0) size 570.41x18
       LayoutText {#text} at (0,0) size 571x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index 9faa65ef..f8ebb28 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 278.00 scrollHeight 836
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 278.00 scrollHeight 836
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-236) size 570x836 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-236) size 570x836 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 570.41x836
     LayoutBlockFlow (anonymous) at (0,0) size 570.41x18
       LayoutText {#text} at (0,0) size 571x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index bb84c3c..2acc770 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 278.00 scrollHeight 836
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 278.00 scrollHeight 836
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-236) size 570x836 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-236) size 570x836 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 570.41x836
     LayoutBlockFlow (anonymous) at (0,0) size 570.41x18
       LayoutText {#text} at (0,0) size 571x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_position-table-cell-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_position-table-cell-expected.png
index f0fd7ff6..5750e958 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_position-table-cell-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_position-table-cell-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
index d652bc4..15e5c4c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/overflow/scroll-nested-positioned-layer-in-overflow-expected.txt
@@ -3,9 +3,9 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-layer at (0,42) size 800x558 scrollY 282.00 scrollHeight 840
+layer at (0,42) size 800x558 clip at (0,42) size 785x558 scrollY 282.00 scrollHeight 840
   LayoutBlockFlow (positioned) {DIV} at (0,42) size 800x558
-layer at (0,-240) size 571x840 backgroundClip at (0,42) size 800x558 clip at (0,42) size 800x558
+layer at (0,-240) size 571x840 backgroundClip at (0,42) size 785x558 clip at (0,42) size 785x558
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 571.28x840
     LayoutBlockFlow (anonymous) at (0,0) size 571.28x18
       LayoutText {#text} at (0,0) size 572x17
diff --git a/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-overlay-with-touch-expected.txt b/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-overlay-with-touch-expected.txt
index 326a19b..ff9be7c 100644
--- a/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-overlay-with-touch-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-overlay-with-touch-expected.txt
@@ -15,7 +15,7 @@
     },
     {
       "name": "Scrolling Layer",
-      "bounds": [300, 300],
+      "bounds": [285, 285],
       "shouldFlattenTransform": false
     },
     {
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/permissions-attribute-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/permissions-attribute-expected.txt
deleted file mode 100644
index ee94514..0000000
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/permissions-attribute-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Tests that the permissions attribute is not exposed for stable
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
index 639df5f..9f75dde 100644
--- a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
@@ -567,7 +567,6 @@
     property marginHeight
     property marginWidth
     property name
-    property permissions
     property referrerPolicy
     property sandbox
     property scrolling
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 5718059..597f6a0 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -2625,7 +2625,6 @@
     getter marginHeight
     getter marginWidth
     getter name
-    getter permissions
     getter referrerPolicy
     getter sandbox
     getter scrolling
@@ -2644,7 +2643,6 @@
     setter marginHeight
     setter marginWidth
     setter name
-    setter permissions
     setter referrerPolicy
     setter sandbox
     setter scrolling
diff --git a/third_party/WebKit/LayoutTests/webexposed/permissions-attribute-expected.txt b/third_party/WebKit/LayoutTests/webexposed/permissions-attribute-expected.txt
index e59c8077..ee94514 100644
--- a/third_party/WebKit/LayoutTests/webexposed/permissions-attribute-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/permissions-attribute-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE ERROR: line 7: Error while parsing the 'permissions' attribute: 'abcdefg' is an invalid permissions flag.
 Tests that the permissions attribute is not exposed for stable
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/Source/bindings/bindings.gni b/third_party/WebKit/Source/bindings/bindings.gni
index 057f211..8d42eea 100644
--- a/third_party/WebKit/Source/bindings/bindings.gni
+++ b/third_party/WebKit/Source/bindings/bindings.gni
@@ -232,6 +232,7 @@
 bindings_unittest_files =
     get_path_info([
                     "core/v8/DocumentWriteEvaluatorTest.cpp",
+                    "core/v8/DOMWrapperWorldTest.cpp",
                     "core/v8/IDLTypesTest.cpp",
                     "core/v8/NativeValueTraitsImplTest.cpp",
                     "core/v8/NativeValueTraitsTest.cpp",
diff --git a/third_party/WebKit/Source/bindings/core/v8/DOMDataStore.h b/third_party/WebKit/Source/bindings/core/v8/DOMDataStore.h
index f1825983..03d9030 100644
--- a/third_party/WebKit/Source/bindings/core/v8/DOMDataStore.h
+++ b/third_party/WebKit/Source/bindings/core/v8/DOMDataStore.h
@@ -156,7 +156,7 @@
   // be in the main world).
   static bool canUseMainWorldWrapper() {
     return !WTF::mayNotBeMainThread() &&
-           !DOMWrapperWorld::nonMainWorldsInMainThread();
+           !DOMWrapperWorld::nonMainWorldsExistInMainThread();
   }
 
   static bool holderContainsWrapper(v8::Local<v8::Object> holder,
diff --git a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp
index 24b1935d..39f9f840 100644
--- a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp
@@ -84,19 +84,17 @@
 unsigned DOMWrapperWorld::s_numberOfNonMainWorldsInMainThread = 0;
 
 using WorldMap = HashMap<int, DOMWrapperWorld*>;
-
-static WorldMap& isolatedWorldMap() {
-  DCHECK(isMainThread());
-  DEFINE_STATIC_LOCAL(WorldMap, map, ());
-  return map;
-}
-
 static WorldMap& worldMap() {
   DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<WorldMap>, map,
                                   new ThreadSpecific<WorldMap>);
   return *map;
 }
 
+static bool isIsolatedWorldId(int worldId) {
+  return DOMWrapperWorld::MainWorldId < worldId &&
+         worldId < DOMWrapperWorld::IsolatedWorldIdLimit;
+}
+
 PassRefPtr<DOMWrapperWorld> DOMWrapperWorld::create(v8::Isolate* isolate,
                                                     WorldType worldType) {
   DCHECK_NE(WorldType::Isolated, worldType);
@@ -111,30 +109,23 @@
       m_worldId(worldId),
       m_domDataStore(
           WTF::wrapUnique(new DOMDataStore(isolate, isMainWorld()))) {
-  switch (worldType) {
+  switch (m_worldType) {
     case WorldType::Main:
-      // MainWorld is managed separately from worldMap() and isolatedWorldMap().
-      // See mainWorld().
+      // MainWorld is managed separately from worldMap(). See mainWorld().
       break;
-    case WorldType::Isolated: {
-      DCHECK(isMainThread());
-      WorldMap& map = isolatedWorldMap();
-      DCHECK(!map.contains(worldId));
-      map.insert(worldId, this);
-      break;
-    }
+    case WorldType::Isolated:
     case WorldType::GarbageCollector:
     case WorldType::RegExp:
     case WorldType::Testing:
     case WorldType::Worker: {
       WorldMap& map = worldMap();
-      DCHECK(!map.contains(worldId));
-      map.insert(worldId, this);
+      DCHECK(!map.contains(m_worldId));
+      map.insert(m_worldId, this);
+      if (isMainThread())
+        s_numberOfNonMainWorldsInMainThread++;
       break;
     }
   }
-  if (worldId != WorldId::MainWorldId && isMainThread())
-    s_numberOfNonMainWorldsInMainThread++;
 }
 
 DOMWrapperWorld& DOMWrapperWorld::mainWorld() {
@@ -147,25 +138,33 @@
 
 PassRefPtr<DOMWrapperWorld> DOMWrapperWorld::fromWorldId(v8::Isolate* isolate,
                                                          int worldId) {
-  if (worldId == MainWorldId)
+  // TODO(nhiroki): The current impl creates a new main/isolated world for
+  // |worldId| if it doesn't exist. We should stop it and instead return nullptr
+  // in the case.
+  if (worldId == WorldId::MainWorldId)
     return &mainWorld();
-  return ensureIsolatedWorld(isolate, worldId);
+  if (isIsolatedWorldId(worldId))
+    return ensureIsolatedWorld(isolate, worldId);
+
+  WorldMap& map = worldMap();
+  auto it = map.find(worldId);
+  if (it != map.end())
+    return it->value;
+  return nullptr;
 }
 
-void DOMWrapperWorld::allWorldsInMainThread(
+void DOMWrapperWorld::allWorldsInCurrentThread(
     Vector<RefPtr<DOMWrapperWorld>>& worlds) {
-  ASSERT(isMainThread());
-  worlds.push_back(&mainWorld());
+  if (isMainThread())
+    worlds.push_back(&mainWorld());
   for (DOMWrapperWorld* world : worldMap().values())
     worlds.push_back(world);
-  for (DOMWrapperWorld* world : isolatedWorldMap().values())
-    worlds.push_back(world);
 }
 
 void DOMWrapperWorld::markWrappersInAllWorlds(
     ScriptWrappable* scriptWrappable,
     const ScriptWrappableVisitor* visitor) {
-  // Marking for worlds other than the main world and the isolated worlds.
+  // Marking for worlds other than the main world.
   DCHECK(ThreadState::current()->isolate());
   for (DOMWrapperWorld* world : worldMap().values()) {
     DOMDataStore& dataStore = world->domDataStore();
@@ -173,66 +172,39 @@
       dataStore.markWrapper(scriptWrappable);
   }
 
-  // The main world and isolated worlds should exist only on the main thread.
-  if (!isMainThread())
-    return;
-
   // Marking for the main world.
-  scriptWrappable->markWrapper(visitor);
-
-  // Marking for the isolated worlds.
-  WorldMap& isolatedWorlds = isolatedWorldMap();
-  for (auto& world : isolatedWorlds.values()) {
-    DOMDataStore& dataStore = world->domDataStore();
-    if (dataStore.containsWrapper(scriptWrappable))
-      dataStore.markWrapper(scriptWrappable);
-  }
+  if (isMainThread())
+    scriptWrappable->markWrapper(visitor);
 }
 
 DOMWrapperWorld::~DOMWrapperWorld() {
   ASSERT(!isMainWorld());
-
-  dispose();
-
   if (isMainThread())
     s_numberOfNonMainWorldsInMainThread--;
 
-  if (!isIsolatedWorld())
-    return;
-
-  WorldMap& map = isolatedWorldMap();
-  WorldMap::iterator it = map.find(m_worldId);
-  if (it == map.end()) {
-    ASSERT_NOT_REACHED();
-    return;
-  }
-  ASSERT(it->value == this);
-
-  map.remove(it);
+  // WorkerWorld should be disposed of before the dtor.
+  if (!isWorkerWorld())
+    dispose();
+  DCHECK(!worldMap().contains(m_worldId));
 }
 
 void DOMWrapperWorld::dispose() {
   m_domObjectHolders.clear();
   m_domDataStore.reset();
+  DCHECK(worldMap().contains(m_worldId));
   worldMap().remove(m_worldId);
 }
 
-#if DCHECK_IS_ON()
-static bool isIsolatedWorldId(int worldId) {
-  return DOMWrapperWorld::MainWorldId < worldId &&
-         worldId < DOMWrapperWorld::IsolatedWorldIdLimit;
-}
-#endif
-
 PassRefPtr<DOMWrapperWorld> DOMWrapperWorld::ensureIsolatedWorld(
     v8::Isolate* isolate,
     int worldId) {
   ASSERT(isIsolatedWorldId(worldId));
 
-  WorldMap& map = isolatedWorldMap();
+  WorldMap& map = worldMap();
   auto it = map.find(worldId);
   if (it != map.end()) {
     RefPtr<DOMWrapperWorld> world = it->value;
+    DCHECK(world->isIsolatedWorld());
     DCHECK_EQ(worldId, world->worldId());
     return world.release();
   }
diff --git a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h
index d4821bd4..7dba1dd 100644
--- a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h
+++ b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h
@@ -84,10 +84,13 @@
   ~DOMWrapperWorld();
   void dispose();
 
-  static bool nonMainWorldsInMainThread() {
+  // Called from performance-sensitive functions, so we should keep this simple
+  // and fast as much as possible.
+  static bool nonMainWorldsExistInMainThread() {
     return s_numberOfNonMainWorldsInMainThread;
   }
-  static void allWorldsInMainThread(Vector<RefPtr<DOMWrapperWorld>>& worlds);
+
+  static void allWorldsInCurrentThread(Vector<RefPtr<DOMWrapperWorld>>& worlds);
   static void markWrappersInAllWorlds(ScriptWrappable*,
                                       const ScriptWrappableVisitor*);
 
@@ -99,7 +102,6 @@
     return world(isolate->GetCurrentContext());
   }
 
-  static DOMWrapperWorld*& workerWorld();
   static DOMWrapperWorld& mainWorld();
   static PassRefPtr<DOMWrapperWorld> fromWorldId(v8::Isolate*, int worldId);
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorldTest.cpp b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorldTest.cpp
new file mode 100644
index 0000000..3206ef0
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorldTest.cpp
@@ -0,0 +1,161 @@
+// Copyright 2017 The Chromium 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 "bindings/core/v8/DOMWrapperWorld.h"
+
+#include "bindings/core/v8/V8BindingForTesting.h"
+#include "bindings/core/v8/V8Initializer.h"
+#include "bindings/core/v8/V8PerIsolateData.h"
+#include "core/workers/WorkerBackingThread.h"
+#include "platform/CrossThreadFunctional.h"
+#include "platform/WebTaskRunner.h"
+#include "platform/WebThreadSupportingGC.h"
+#include "platform/testing/UnitTestHelpers.h"
+#include "public/platform/Platform.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+Vector<RefPtr<DOMWrapperWorld>> createIsolatedWorlds(v8::Isolate* isolate) {
+  Vector<RefPtr<DOMWrapperWorld>> worlds;
+  worlds.push_back(DOMWrapperWorld::ensureIsolatedWorld(
+      isolate, DOMWrapperWorld::WorldId::MainWorldId + 1));
+  worlds.push_back(DOMWrapperWorld::ensureIsolatedWorld(
+      isolate, DOMWrapperWorld::WorldId::IsolatedWorldIdLimit - 1));
+  EXPECT_TRUE(worlds[0]->isIsolatedWorld());
+  EXPECT_TRUE(worlds[1]->isIsolatedWorld());
+  EXPECT_EQ(worlds[0],
+            DOMWrapperWorld::fromWorldId(isolate, worlds[0]->worldId()));
+  EXPECT_EQ(worlds[1],
+            DOMWrapperWorld::fromWorldId(isolate, worlds[1]->worldId()));
+  return worlds;
+}
+
+Vector<RefPtr<DOMWrapperWorld>> createWorlds(v8::Isolate* isolate) {
+  Vector<RefPtr<DOMWrapperWorld>> worlds;
+  worlds.push_back(
+      DOMWrapperWorld::create(isolate, DOMWrapperWorld::WorldType::Worker));
+  worlds.push_back(
+      DOMWrapperWorld::create(isolate, DOMWrapperWorld::WorldType::Worker));
+  worlds.push_back(DOMWrapperWorld::create(
+      isolate, DOMWrapperWorld::WorldType::GarbageCollector));
+  EXPECT_TRUE(worlds[0]->isWorkerWorld());
+  EXPECT_TRUE(worlds[1]->isWorkerWorld());
+  EXPECT_FALSE(worlds[2]->isWorkerWorld());
+
+  // World ids should be unique.
+  HashSet<int> worldIds;
+  EXPECT_TRUE(worldIds.insert(worlds[0]->worldId()).isNewEntry);
+  EXPECT_TRUE(worldIds.insert(worlds[1]->worldId()).isNewEntry);
+  EXPECT_TRUE(worldIds.insert(worlds[2]->worldId()).isNewEntry);
+  EXPECT_EQ(worlds[0],
+            DOMWrapperWorld::fromWorldId(isolate, worlds[0]->worldId()));
+  EXPECT_EQ(worlds[1],
+            DOMWrapperWorld::fromWorldId(isolate, worlds[1]->worldId()));
+  EXPECT_EQ(worlds[2],
+            DOMWrapperWorld::fromWorldId(isolate, worlds[2]->worldId()));
+
+  return worlds;
+}
+
+void workerThreadFunc(WorkerBackingThread* thread,
+                      RefPtr<WebTaskRunner> mainThreadTaskRunner) {
+  thread->initialize();
+
+  // Worlds on the main thread should not be visible from the worker thread.
+  Vector<RefPtr<DOMWrapperWorld>> retrievedWorlds;
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_TRUE(retrievedWorlds.isEmpty());
+
+  // Create worlds on the worker thread and verify them.
+  Vector<RefPtr<DOMWrapperWorld>> worlds = createWorlds(thread->isolate());
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(worlds.size(), retrievedWorlds.size());
+  retrievedWorlds.clear();
+
+  // Dispose of the last world.
+  worlds.pop_back();
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(worlds.size(), retrievedWorlds.size());
+
+  // Dispose of remaining worlds.
+  for (RefPtr<DOMWrapperWorld>& world : worlds) {
+    if (world->isWorkerWorld())
+      world->dispose();
+  }
+  worlds.clear();
+
+  thread->shutdown();
+  mainThreadTaskRunner->postTask(BLINK_FROM_HERE,
+                                 crossThreadBind(&testing::exitRunLoop));
+}
+
+TEST(DOMWrapperWorldTest, Basic) {
+  V8TestingScope scope;
+
+  // Create the main world and verify it.
+  DOMWrapperWorld& mainWorld = DOMWrapperWorld::mainWorld();
+  EXPECT_TRUE(mainWorld.isMainWorld());
+  EXPECT_FALSE(DOMWrapperWorld::nonMainWorldsExistInMainThread());
+  Vector<RefPtr<DOMWrapperWorld>> retrievedWorlds;
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(1u, retrievedWorlds.size());
+  EXPECT_TRUE(retrievedWorlds[0]->isMainWorld());
+  EXPECT_EQ(&mainWorld,
+            DOMWrapperWorld::fromWorldId(scope.isolate(), mainWorld.worldId()));
+  retrievedWorlds.clear();
+
+  // Create isolated worlds and verify them.
+  Vector<RefPtr<DOMWrapperWorld>> isolatedWorlds =
+      createIsolatedWorlds(scope.isolate());
+  EXPECT_TRUE(DOMWrapperWorld::nonMainWorldsExistInMainThread());
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(isolatedWorlds.size() + 1, retrievedWorlds.size());
+
+  // Create other worlds and verify them.
+  Vector<RefPtr<DOMWrapperWorld>> worlds = createWorlds(scope.isolate());
+  EXPECT_TRUE(DOMWrapperWorld::nonMainWorldsExistInMainThread());
+  retrievedWorlds.clear();
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(isolatedWorlds.size() + worlds.size() + 1, retrievedWorlds.size());
+  retrievedWorlds.clear();
+
+  // Start a worker thread and create worlds on that.
+  std::unique_ptr<WorkerBackingThread> thread =
+      WorkerBackingThread::create("DOMWrapperWorld test thread");
+  RefPtr<WebTaskRunner> mainThreadTaskRunner =
+      Platform::current()->currentThread()->getWebTaskRunner();
+  thread->backingThread().postTask(
+      BLINK_FROM_HERE,
+      crossThreadBind(&workerThreadFunc, crossThreadUnretained(thread.get()),
+                      std::move(mainThreadTaskRunner)));
+  testing::enterRunLoop();
+
+  // Worlds on the worker thread should not be visible from the main thread.
+  EXPECT_TRUE(DOMWrapperWorld::nonMainWorldsExistInMainThread());
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(isolatedWorlds.size() + worlds.size() + 1, retrievedWorlds.size());
+  retrievedWorlds.clear();
+
+  // Dispose of the isolated worlds.
+  isolatedWorlds.clear();
+  EXPECT_TRUE(DOMWrapperWorld::nonMainWorldsExistInMainThread());
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(worlds.size() + 1, retrievedWorlds.size());
+  retrievedWorlds.clear();
+
+  // Dispose of the other worlds.
+  for (RefPtr<DOMWrapperWorld>& world : worlds) {
+    if (world->isWorkerWorld())
+      world->dispose();
+  }
+  worlds.clear();
+  EXPECT_FALSE(DOMWrapperWorld::nonMainWorldsExistInMainThread());
+  DOMWrapperWorld::allWorldsInCurrentThread(retrievedWorlds);
+  EXPECT_EQ(1u, retrievedWorlds.size());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
index ee223db..bf7ba0c 100644
--- a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
@@ -175,18 +175,10 @@
     v8::Isolate* isolate,
     DOMArrayBuffer* object,
     Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) {
-  if (isMainThread()) {
-    Vector<RefPtr<DOMWrapperWorld>> worlds;
-    DOMWrapperWorld::allWorldsInMainThread(worlds);
-    for (size_t i = 0; i < worlds.size(); i++) {
-      v8::Local<v8::Object> wrapper =
-          worlds[i]->domDataStore().get(object, isolate);
-      if (!wrapper.IsEmpty())
-        buffers.push_back(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
-    }
-  } else {
-    v8::Local<v8::Object> wrapper =
-        DOMWrapperWorld::current(isolate).domDataStore().get(object, isolate);
+  Vector<RefPtr<DOMWrapperWorld>> worlds;
+  DOMWrapperWorld::allWorldsInCurrentThread(worlds);
+  for (const auto& world : worlds) {
+    v8::Local<v8::Object> wrapper = world->domDataStore().get(object, isolate);
     if (!wrapper.IsEmpty())
       buffers.push_back(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
   }
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 6d9dd48..c14d3f1 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1237,7 +1237,6 @@
     "html/HTMLEmbedElementTest.cpp",
     "html/HTMLFormControlElementTest.cpp",
     "html/HTMLIFrameElementAllowTest.cpp",
-    "html/HTMLIFrameElementPermissionsTest.cpp",
     "html/HTMLIFrameElementTest.cpp",
     "html/HTMLImageElementTest.cpp",
     "html/HTMLInputElementTest.cpp",
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
index 3a22738..a45a7e3 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
@@ -306,7 +306,7 @@
   for (bool& flag : cancelRunningAnimationFlags)
     flag = true;
 
-  if (animationData && style.display() != EDisplay::None) {
+  if (animationData && style.display() != EDisplay::kNone) {
     const Vector<AtomicString>& nameList = animationData->nameList();
     for (size_t i = 0; i < nameList.size(); ++i) {
       AtomicString name = nameList[i];
@@ -859,7 +859,7 @@
   HashSet<PropertyHandle> listedProperties;
   bool anyTransitionHadTransitionAll = false;
   const LayoutObject* layoutObject = animatingElement->layoutObject();
-  if (!animationStyleRecalc && style.display() != EDisplay::None &&
+  if (!animationStyleRecalc && style.display() != EDisplay::kNone &&
       layoutObject && layoutObject->style() && transitionData) {
     TransitionUpdateState state = {
         update,         animatingElement,  *layoutObject->style(),
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
index 842f8b6..f95be0b 100644
--- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
+++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -1066,73 +1066,73 @@
 inline CSSIdentifierValue::CSSIdentifierValue(EDisplay e)
     : CSSValue(IdentifierClass) {
   switch (e) {
-    case EDisplay::Inline:
+    case EDisplay::kInline:
       m_valueID = CSSValueInline;
       break;
-    case EDisplay::Block:
+    case EDisplay::kBlock:
       m_valueID = CSSValueBlock;
       break;
-    case EDisplay::ListItem:
+    case EDisplay::kListItem:
       m_valueID = CSSValueListItem;
       break;
-    case EDisplay::InlineBlock:
+    case EDisplay::kInlineBlock:
       m_valueID = CSSValueInlineBlock;
       break;
-    case EDisplay::Table:
+    case EDisplay::kTable:
       m_valueID = CSSValueTable;
       break;
-    case EDisplay::InlineTable:
+    case EDisplay::kInlineTable:
       m_valueID = CSSValueInlineTable;
       break;
-    case EDisplay::TableRowGroup:
+    case EDisplay::kTableRowGroup:
       m_valueID = CSSValueTableRowGroup;
       break;
-    case EDisplay::TableHeaderGroup:
+    case EDisplay::kTableHeaderGroup:
       m_valueID = CSSValueTableHeaderGroup;
       break;
-    case EDisplay::TableFooterGroup:
+    case EDisplay::kTableFooterGroup:
       m_valueID = CSSValueTableFooterGroup;
       break;
-    case EDisplay::TableRow:
+    case EDisplay::kTableRow:
       m_valueID = CSSValueTableRow;
       break;
-    case EDisplay::TableColumnGroup:
+    case EDisplay::kTableColumnGroup:
       m_valueID = CSSValueTableColumnGroup;
       break;
-    case EDisplay::TableColumn:
+    case EDisplay::kTableColumn:
       m_valueID = CSSValueTableColumn;
       break;
-    case EDisplay::TableCell:
+    case EDisplay::kTableCell:
       m_valueID = CSSValueTableCell;
       break;
-    case EDisplay::TableCaption:
+    case EDisplay::kTableCaption:
       m_valueID = CSSValueTableCaption;
       break;
-    case EDisplay::WebkitBox:
+    case EDisplay::kWebkitBox:
       m_valueID = CSSValueWebkitBox;
       break;
-    case EDisplay::WebkitInlineBox:
+    case EDisplay::kWebkitInlineBox:
       m_valueID = CSSValueWebkitInlineBox;
       break;
-    case EDisplay::Flex:
+    case EDisplay::kFlex:
       m_valueID = CSSValueFlex;
       break;
-    case EDisplay::InlineFlex:
+    case EDisplay::kInlineFlex:
       m_valueID = CSSValueInlineFlex;
       break;
-    case EDisplay::Grid:
+    case EDisplay::kGrid:
       m_valueID = CSSValueGrid;
       break;
-    case EDisplay::InlineGrid:
+    case EDisplay::kInlineGrid:
       m_valueID = CSSValueInlineGrid;
       break;
-    case EDisplay::Contents:
+    case EDisplay::kContents:
       m_valueID = CSSValueContents;
       break;
-    case EDisplay::FlowRoot:
+    case EDisplay::kFlowRoot:
       m_valueID = CSSValueFlowRoot;
       break;
-    case EDisplay::None:
+    case EDisplay::kNone:
       m_valueID = CSSValueNone;
       break;
   }
@@ -1142,57 +1142,57 @@
 inline EDisplay CSSIdentifierValue::convertTo() const {
   switch (m_valueID) {
     case CSSValueInline:
-      return EDisplay::Inline;
+      return EDisplay::kInline;
     case CSSValueBlock:
-      return EDisplay::Block;
+      return EDisplay::kBlock;
     case CSSValueListItem:
-      return EDisplay::ListItem;
+      return EDisplay::kListItem;
     case CSSValueInlineBlock:
-      return EDisplay::InlineBlock;
+      return EDisplay::kInlineBlock;
     case CSSValueTable:
-      return EDisplay::Table;
+      return EDisplay::kTable;
     case CSSValueInlineTable:
-      return EDisplay::InlineTable;
+      return EDisplay::kInlineTable;
     case CSSValueTableRowGroup:
-      return EDisplay::TableRowGroup;
+      return EDisplay::kTableRowGroup;
     case CSSValueTableHeaderGroup:
-      return EDisplay::TableHeaderGroup;
+      return EDisplay::kTableHeaderGroup;
     case CSSValueTableFooterGroup:
-      return EDisplay::TableFooterGroup;
+      return EDisplay::kTableFooterGroup;
     case CSSValueTableRow:
-      return EDisplay::TableRow;
+      return EDisplay::kTableRow;
     case CSSValueTableColumnGroup:
-      return EDisplay::TableColumnGroup;
+      return EDisplay::kTableColumnGroup;
     case CSSValueTableColumn:
-      return EDisplay::TableColumn;
+      return EDisplay::kTableColumn;
     case CSSValueTableCell:
-      return EDisplay::TableCell;
+      return EDisplay::kTableCell;
     case CSSValueTableCaption:
-      return EDisplay::TableCaption;
+      return EDisplay::kTableCaption;
     case CSSValueWebkitBox:
-      return EDisplay::WebkitBox;
+      return EDisplay::kWebkitBox;
     case CSSValueWebkitInlineBox:
-      return EDisplay::WebkitInlineBox;
+      return EDisplay::kWebkitInlineBox;
     case CSSValueFlex:
     case CSSValueWebkitFlex:
-      return EDisplay::Flex;
+      return EDisplay::kFlex;
     case CSSValueInlineFlex:
     case CSSValueWebkitInlineFlex:
-      return EDisplay::InlineFlex;
+      return EDisplay::kInlineFlex;
     case CSSValueGrid:
-      return EDisplay::Grid;
+      return EDisplay::kGrid;
     case CSSValueInlineGrid:
-      return EDisplay::InlineGrid;
+      return EDisplay::kInlineGrid;
     case CSSValueContents:
-      return EDisplay::Contents;
+      return EDisplay::kContents;
     case CSSValueFlowRoot:
-      return EDisplay::FlowRoot;
+      return EDisplay::kFlowRoot;
     case CSSValueNone:
-      return EDisplay::None;
+      return EDisplay::kNone;
       break;
     default:
       NOTREACHED();
-      return EDisplay::None;
+      return EDisplay::kNone;
   }
 }
 
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
index 6227c6d6..e21383e9 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -2368,7 +2368,7 @@
       return CSSPrimitiveValue::create(style.order(),
                                        CSSPrimitiveValue::UnitType::Number);
     case CSSPropertyFloat:
-      if (style.display() != EDisplay::None && style.hasOutOfFlowPosition())
+      if (style.display() != EDisplay::kNone && style.hasOutOfFlowPosition())
         return CSSIdentifierValue::create(CSSValueNone);
       return CSSIdentifierValue::create(style.floating());
     case CSSPropertyFont:
@@ -2582,7 +2582,7 @@
       if (marginRight.isPercentOrCalc()) {
         // LayoutBox gives a marginRight() that is the distance between the
         // right-edge of the child box and the right-edge of the containing box,
-        // when display == EDisplay::Block. Let's calculate the absolute value
+        // when display == EDisplay::kBlock. Let's calculate the absolute value
         // of the specified margin-right % instead of relying on LayoutBox's
         // marginRight() value.
         value = minimumValueForLength(
diff --git a/third_party/WebKit/Source/core/css/properties/README.md b/third_party/WebKit/Source/core/css/properties/README.md
new file mode 100644
index 0000000..35fad85
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/README.md
@@ -0,0 +1,52 @@
+# Property APIs
+
+This directory contains implementations for CSS property APIs, as well as Utils
+files containing functions commonly used by the property APIs.
+
+A CSS property API represents a single CSS property or a group of CSS
+properties, and defines the logic for that property or group of properties.
+
+Examples:
+
+*   A single property API: the `CSSPropertyAPILineHeight` class is used only by
+    the `line-height` property
+*   A group of properties that share logic: the `CSSPropertyAPIImage` class
+    is shared by the `border-image-source` and `list-style-image` properties.
+
+Status (March 16 2017): Eventually, all logic pertaining to a single property
+should be found only within its CSS property API. Currently, the code base is in
+a transitional state and property specific logic is still scattered around the
+code base. See Project Ribbon
+[tracking bug](https://bugs.chromium.org/p/chromium/issues/detail?id=545324) and
+[design doc](https://docs.google.com/document/d/1ywjUTmnxF5FXlpUTuLpint0w4TdSsjJzdWJqmhNzlss/edit#heading=h.1ckibme4i78b)
+for details of progress.
+
+## How to add a new property API
+
+1.  Add a .cpp file to this directory named
+    `CSSPropertyAPI<Property/GroupName>.cpp`
+2.  Implement the property API in the .cpp file
+    1.  Add `#include "core/css/properties/CSSPropertyAPI<Property/GroupName>.h"`
+        (this will be a generated file)
+    2.  Implement the required methods on the API, e.g. `parseSingleValue`
+3.  If logic is required by multiple property APIs you may need to create a new
+    Utils file.
+4.  Add the new property to `core/css/CSSProperties.json5`. Ensure that you
+    include the 'api_class' flag and the 'api_methods' flag so that the API
+    files are generated correctly (see
+    [CSSProperties.json5](https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/CSSProperties.json5)
+    for more details)
+5.  Add new files to BUILD files
+    1.  Add the new .cpp file to
+        [core/css/BUILD.gn](https://codesearch.chromium.org/chromium/src/third_party/WebKit/Source/core/css/BUILD.gn)
+        in the `blink_core_sources` target's `sources` parameter
+    2.  Add the generated .h file to
+        [core/BUILD.gn](https://codesearch.chromium.org/chromium/src/third_party/WebKit/Source/core/BUILD.gn)
+        in the `css_properties` target's `outputs` parameter
+
+See [this example CL](https://codereview.chromium.org/2735093005), which
+converts the existing line-height property to use the CSSPropertyAPI design.
+This new line-height property API only implements the parseSingleValue method,
+using
+[CSSPropertyFontUtils.cpp](https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/properties/CSSPropertyFontUtils.h)
+to access shared font logic.
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
index 1e9fa35..24af17b 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
@@ -58,41 +58,41 @@
 
 static EDisplay equivalentBlockDisplay(EDisplay display) {
   switch (display) {
-    case EDisplay::Block:
-    case EDisplay::Table:
-    case EDisplay::WebkitBox:
-    case EDisplay::Flex:
-    case EDisplay::Grid:
-    case EDisplay::ListItem:
-    case EDisplay::FlowRoot:
+    case EDisplay::kBlock:
+    case EDisplay::kTable:
+    case EDisplay::kWebkitBox:
+    case EDisplay::kFlex:
+    case EDisplay::kGrid:
+    case EDisplay::kListItem:
+    case EDisplay::kFlowRoot:
       return display;
-    case EDisplay::InlineTable:
-      return EDisplay::Table;
-    case EDisplay::WebkitInlineBox:
-      return EDisplay::WebkitBox;
-    case EDisplay::InlineFlex:
-      return EDisplay::Flex;
-    case EDisplay::InlineGrid:
-      return EDisplay::Grid;
+    case EDisplay::kInlineTable:
+      return EDisplay::kTable;
+    case EDisplay::kWebkitInlineBox:
+      return EDisplay::kWebkitBox;
+    case EDisplay::kInlineFlex:
+      return EDisplay::kFlex;
+    case EDisplay::kInlineGrid:
+      return EDisplay::kGrid;
 
-    case EDisplay::Contents:
-    case EDisplay::Inline:
-    case EDisplay::InlineBlock:
-    case EDisplay::TableRowGroup:
-    case EDisplay::TableHeaderGroup:
-    case EDisplay::TableFooterGroup:
-    case EDisplay::TableRow:
-    case EDisplay::TableColumnGroup:
-    case EDisplay::TableColumn:
-    case EDisplay::TableCell:
-    case EDisplay::TableCaption:
-      return EDisplay::Block;
-    case EDisplay::None:
+    case EDisplay::kContents:
+    case EDisplay::kInline:
+    case EDisplay::kInlineBlock:
+    case EDisplay::kTableRowGroup:
+    case EDisplay::kTableHeaderGroup:
+    case EDisplay::kTableFooterGroup:
+    case EDisplay::kTableRow:
+    case EDisplay::kTableColumnGroup:
+    case EDisplay::kTableColumn:
+    case EDisplay::kTableCell:
+    case EDisplay::kTableCaption:
+      return EDisplay::kBlock;
+    case EDisplay::kNone:
       ASSERT_NOT_REACHED();
       return display;
   }
   ASSERT_NOT_REACHED();
-  return EDisplay::Block;
+  return EDisplay::kBlock;
 }
 
 static bool isOutermostSVGElement(const Element* element) {
@@ -106,9 +106,9 @@
 // considered to be atomic inline-level.
 static bool doesNotInheritTextDecoration(const ComputedStyle& style,
                                          const Element* element) {
-  return style.display() == EDisplay::InlineTable ||
-         style.display() == EDisplay::InlineBlock ||
-         style.display() == EDisplay::WebkitInlineBox ||
+  return style.display() == EDisplay::kInlineTable ||
+         style.display() == EDisplay::kInlineBlock ||
+         style.display() == EDisplay::kWebkitInlineBox ||
          isAtShadowBoundary(element) || style.isFloating() ||
          style.hasOutOfFlowPosition() || isOutermostSVGElement(element) ||
          isHTMLRTElement(element);
@@ -153,7 +153,7 @@
     return;
 
   // Force inline display (except for floating first-letters).
-  style.setDisplay(style.isFloating() ? EDisplay::Block : EDisplay::Inline);
+  style.setDisplay(style.isFloating() ? EDisplay::kBlock : EDisplay::kInline);
 
   // CSS2 says first-letter can't be positioned.
   style.setPosition(EPosition::kStatic);
@@ -213,7 +213,7 @@
 
   if (isHTMLImageElement(element)) {
     if (toHTMLImageElement(element).isCollapsed())
-      style.setDisplay(EDisplay::None);
+      style.setDisplay(EDisplay::kNone);
     return;
   }
 
@@ -232,7 +232,7 @@
     // This is necessary to fix a crash where a site tries to position these
     // objects. They also never honor display.
     style.setPosition(EPosition::kStatic);
-    style.setDisplay(EDisplay::Block);
+    style.setDisplay(EDisplay::kBlock);
     return;
   }
 
@@ -253,7 +253,7 @@
   }
 
   if (isHTMLLegendElement(element)) {
-    style.setDisplay(EDisplay::Block);
+    style.setDisplay(EDisplay::kBlock);
     return;
   }
 
@@ -286,8 +286,8 @@
   DCHECK(style.overflowX() != EOverflow::kVisible ||
          style.overflowY() != EOverflow::kVisible);
 
-  if (style.display() == EDisplay::Table ||
-      style.display() == EDisplay::InlineTable) {
+  if (style.display() == EDisplay::kTable ||
+      style.display() == EDisplay::kInlineTable) {
     // Tables only support overflow:hidden and overflow:visible and ignore
     // anything else, see http://dev.w3.org/csswg/css2/visufx.html#overflow. As
     // a table is not a block container box the rules for resolving conflicting
@@ -327,36 +327,36 @@
 static void adjustStyleForDisplay(ComputedStyle& style,
                                   const ComputedStyle& layoutParentStyle,
                                   Document* document) {
-  if (style.display() == EDisplay::Block && !style.isFloating())
+  if (style.display() == EDisplay::kBlock && !style.isFloating())
     return;
 
-  if (style.display() == EDisplay::Contents)
+  if (style.display() == EDisplay::kContents)
     return;
 
   // FIXME: Don't support this mutation for pseudo styles like first-letter or
   // first-line, since it's not completely clear how that should work.
-  if (style.display() == EDisplay::Inline &&
+  if (style.display() == EDisplay::kInline &&
       style.styleType() == PseudoIdNone &&
       style.getWritingMode() != layoutParentStyle.getWritingMode())
-    style.setDisplay(EDisplay::InlineBlock);
+    style.setDisplay(EDisplay::kInlineBlock);
 
   // We do not honor position: relative or sticky for table rows, headers, and
   // footers. This is correct for position: relative in CSS2.1 (and caused a
   // crash in containingBlock() on some sites) and position: sticky is defined
   // as following position: relative behavior for table elements. It is
   // incorrect for CSS3.
-  if ((style.display() == EDisplay::TableHeaderGroup ||
-       style.display() == EDisplay::TableRowGroup ||
-       style.display() == EDisplay::TableFooterGroup ||
-       style.display() == EDisplay::TableRow) &&
+  if ((style.display() == EDisplay::kTableHeaderGroup ||
+       style.display() == EDisplay::kTableRowGroup ||
+       style.display() == EDisplay::kTableFooterGroup ||
+       style.display() == EDisplay::kTableRow) &&
       style.hasInFlowPosition())
     style.setPosition(EPosition::kStatic);
 
   // Cannot support position: sticky for table columns and column groups because
   // current code is only doing background painting through columns / column
   // groups.
-  if ((style.display() == EDisplay::TableColumnGroup ||
-       style.display() == EDisplay::TableColumn) &&
+  if ((style.display() == EDisplay::kTableColumnGroup ||
+       style.display() == EDisplay::kTableColumn) &&
       style.position() == EPosition::kSticky)
     style.setPosition(EPosition::kStatic);
 
@@ -364,21 +364,21 @@
   // rows, and table columns.
   // FIXME: Table cells should be allowed to be perpendicular or flipped with
   // respect to the table, though.
-  if (style.display() == EDisplay::TableColumn ||
-      style.display() == EDisplay::TableColumnGroup ||
-      style.display() == EDisplay::TableFooterGroup ||
-      style.display() == EDisplay::TableHeaderGroup ||
-      style.display() == EDisplay::TableRow ||
-      style.display() == EDisplay::TableRowGroup ||
-      style.display() == EDisplay::TableCell)
+  if (style.display() == EDisplay::kTableColumn ||
+      style.display() == EDisplay::kTableColumnGroup ||
+      style.display() == EDisplay::kTableFooterGroup ||
+      style.display() == EDisplay::kTableHeaderGroup ||
+      style.display() == EDisplay::kTableRow ||
+      style.display() == EDisplay::kTableRowGroup ||
+      style.display() == EDisplay::kTableCell)
     style.setWritingMode(layoutParentStyle.getWritingMode());
 
   // FIXME: Since we don't support block-flow on flexible boxes yet, disallow
   // setting of block-flow to anything other than TopToBottomWritingMode.
   // https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support.
   if (style.getWritingMode() != WritingMode::kHorizontalTb &&
-      (style.display() == EDisplay::WebkitBox ||
-       style.display() == EDisplay::WebkitInlineBox))
+      (style.display() == EDisplay::kWebkitBox ||
+       style.display() == EDisplay::kWebkitInlineBox))
     style.setWritingMode(WritingMode::kHorizontalTb);
 
   if (layoutParentStyle.isDisplayFlexibleOrGridBox()) {
@@ -401,7 +401,7 @@
                                         const ComputedStyle& parentStyle,
                                         const ComputedStyle& layoutParentStyle,
                                         Element* element) {
-  if (style.display() != EDisplay::None) {
+  if (style.display() != EDisplay::kNone) {
     if (element && element->isHTMLElement())
       adjustStyleForHTMLElement(style, toHTMLElement(*element));
 
@@ -414,7 +414,7 @@
 
     // Absolute/fixed positioned elements, floating elements and the document
     // element need block-like outside display.
-    if (style.display() != EDisplay::Contents &&
+    if (style.display() != EDisplay::kContents &&
         (style.hasOutOfFlowPosition() || style.isFloating()))
       style.setDisplay(equivalentBlockDisplay(style.display()));
 
@@ -430,8 +430,8 @@
 
     // Paint containment forces a block formatting context, so we must coerce
     // from inline.  https://drafts.csswg.org/css-containment/#containment-paint
-    if (style.containsPaint() && style.display() == EDisplay::Inline)
-      style.setDisplay(EDisplay::Block);
+    if (style.containsPaint() && style.display() == EDisplay::kInline)
+      style.setDisplay(EDisplay::kBlock);
   } else {
     adjustStyleForFirstLetter(style);
   }
@@ -491,8 +491,8 @@
     // SVGElement::layoutObjectIsNeeded.
     //
     // [1]: https://www.w3.org/TR/SVG/painting.html#DisplayProperty
-    if (style.display() == EDisplay::Contents)
-      style.setDisplay(EDisplay::Inline);
+    if (style.display() == EDisplay::kContents)
+      style.setDisplay(EDisplay::kInline);
 
     // Only the root <svg> element in an SVG document fragment tree honors css
     // position.
@@ -503,7 +503,7 @@
     // SVG text layout code expects us to be a block-level style element.
     if ((isSVGForeignObjectElement(*element) || isSVGTextElement(*element)) &&
         style.isDisplayInlineType())
-      style.setDisplay(EDisplay::Block);
+      style.setDisplay(EDisplay::kBlock);
 
     // Columns don't apply to svg text elements.
     if (isSVGTextElement(*element))
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
index 4353a4a..ac4174a 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
@@ -561,7 +561,7 @@
   // These are designed to match the user-agent stylesheet values for the
   // document element so that the common case doesn't need to create a new
   // ComputedStyle in Document::inheritHtmlAndBodyElementStyles.
-  documentStyle->setDisplay(EDisplay::Block);
+  documentStyle->setDisplay(EDisplay::kBlock);
   documentStyle->setPosition(EPosition::kAbsolute);
 
   // Document::inheritHtmlAndBodyElementStyles will set the final overflow
@@ -636,7 +636,7 @@
       !element->layoutObject()) {
     if (!s_styleNotYetAvailable) {
       s_styleNotYetAvailable = ComputedStyle::create().leakRef();
-      s_styleNotYetAvailable->setDisplay(EDisplay::None);
+      s_styleNotYetAvailable->setDisplay(EDisplay::kNone);
       s_styleNotYetAvailable->font().update(
           document().styleEngine().fontSelector());
     }
diff --git a/third_party/WebKit/Source/core/dom/DocumentStatisticsCollector.cpp b/third_party/WebKit/Source/core/dom/DocumentStatisticsCollector.cpp
index 69884a3..4381e3f 100644
--- a/third_party/WebKit/Source/core/dom/DocumentStatisticsCollector.cpp
+++ b/third_party/WebKit/Source/core/dom/DocumentStatisticsCollector.cpp
@@ -63,7 +63,7 @@
   const ComputedStyle* style = element.computedStyle();
   if (!style)
     return false;
-  return (style->display() != EDisplay::None &&
+  return (style->display() != EDisplay::kNone &&
           style->visibility() != EVisibility::kHidden && style->opacity() != 0);
 }
 
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index cde1c10..73df10b 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1574,8 +1574,8 @@
 }
 
 bool Element::layoutObjectIsNeeded(const ComputedStyle& style) {
-  return style.display() != EDisplay::None &&
-         style.display() != EDisplay::Contents;
+  return style.display() != EDisplay::kNone &&
+         style.display() != EDisplay::kContents;
 }
 
 LayoutObject* Element::createLayoutObject(const ComputedStyle& style) {
@@ -3196,18 +3196,18 @@
 
 bool Element::hasDisplayContentsStyle() const {
   if (const ComputedStyle* style = nonLayoutObjectComputedStyle())
-    return style->display() == EDisplay::Contents;
+    return style->display() == EDisplay::kContents;
   return false;
 }
 
 bool Element::shouldStoreNonLayoutObjectComputedStyle(
     const ComputedStyle& style) const {
 #if DCHECK_IS_ON()
-  if (style.display() == EDisplay::Contents)
+  if (style.display() == EDisplay::kContents)
     DCHECK(!layoutObject());
 #endif
 
-  return style.display() == EDisplay::Contents ||
+  return style.display() == EDisplay::kContents ||
          isHTMLOptGroupElement(*this) || isHTMLOptionElement(*this);
 }
 
diff --git a/third_party/WebKit/Source/core/dom/NodeRareData.cpp b/third_party/WebKit/Source/core/dom/NodeRareData.cpp
index 9343fd3..1bedf95 100644
--- a/third_party/WebKit/Source/core/dom/NodeRareData.cpp
+++ b/third_party/WebKit/Source/core/dom/NodeRareData.cpp
@@ -33,7 +33,7 @@
 #include "bindings/core/v8/ScriptWrappableVisitor.h"
 #include "core/dom/Element.h"
 #include "core/dom/ElementRareData.h"
-#include "core/frame/FrameHost.h"
+#include "core/page/Page.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
@@ -85,12 +85,12 @@
 }
 
 void NodeRareData::incrementConnectedSubframeCount() {
-  SECURITY_CHECK((m_connectedFrameCount + 1) <= FrameHost::maxNumberOfFrames);
+  SECURITY_CHECK((m_connectedFrameCount + 1) <= Page::maxNumberOfFrames);
   ++m_connectedFrameCount;
 }
 
 // Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow
-static_assert(FrameHost::maxNumberOfFrames <
+static_assert(Page::maxNumberOfFrames <
                   (1 << NodeRareData::ConnectedFrameCountBits),
               "Frame limit should fit in rare data count");
 
diff --git a/third_party/WebKit/Source/core/dom/PseudoElement.h b/third_party/WebKit/Source/core/dom/PseudoElement.h
index e05dacf..1e9e1aff 100644
--- a/third_party/WebKit/Source/core/dom/PseudoElement.h
+++ b/third_party/WebKit/Source/core/dom/PseudoElement.h
@@ -65,7 +65,7 @@
 inline bool pseudoElementLayoutObjectIsNeeded(const ComputedStyle* style) {
   if (!style)
     return false;
-  if (style->display() == EDisplay::None)
+  if (style->display() == EDisplay::kNone)
     return false;
   if (style->styleType() == PseudoIdFirstLetter ||
       style->styleType() == PseudoIdBackdrop)
diff --git a/third_party/WebKit/Source/core/dom/Range.cpp b/third_party/WebKit/Source/core/dom/Range.cpp
index 45618228..938dfec 100644
--- a/third_party/WebKit/Source/core/dom/Range.cpp
+++ b/third_party/WebKit/Source/core/dom/Range.cpp
@@ -1644,8 +1644,9 @@
 }
 
 void Range::expand(const String& unit, ExceptionState& exceptionState) {
+  if (!startPosition().isConnected() || !endPosition().isConnected())
+    return;
   m_ownerDocument->updateStyleAndLayoutIgnorePendingStylesheets();
-
   VisiblePosition start = createVisiblePosition(startPosition());
   VisiblePosition end = createVisiblePosition(endPosition());
   if (unit == "word") {
diff --git a/third_party/WebKit/Source/core/dom/RangeTest.cpp b/third_party/WebKit/Source/core/dom/RangeTest.cpp
index 61ac3b4..d321dcf 100644
--- a/third_party/WebKit/Source/core/dom/RangeTest.cpp
+++ b/third_party/WebKit/Source/core/dom/RangeTest.cpp
@@ -12,6 +12,7 @@
 #include "core/editing/EditingTestBase.h"
 #include "core/frame/Settings.h"
 #include "core/html/HTMLBodyElement.h"
+#include "core/html/HTMLDivElement.h"
 #include "core/html/HTMLDocument.h"
 #include "core/html/HTMLElement.h"
 #include "core/html/HTMLHtmlElement.h"
@@ -230,4 +231,12 @@
   EXPECT_EQ(2u, range->endOffset());
 }
 
+// Regression test for crbug.com/698123
+TEST_F(RangeTest, ExpandNotCrash) {
+  Range* range = Range::create(document());
+  Node* div = HTMLDivElement::create(document());
+  range->setStart(div, 0, ASSERT_NO_EXCEPTION);
+  range->expand("", ASSERT_NO_EXCEPTION);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/Text.cpp b/third_party/WebKit/Source/core/dom/Text.cpp
index 7644a6c..f7510db 100644
--- a/third_party/WebKit/Source/core/dom/Text.cpp
+++ b/third_party/WebKit/Source/core/dom/Text.cpp
@@ -265,7 +265,7 @@
   if (!length())
     return false;
 
-  if (style.display() == EDisplay::None)
+  if (style.display() == EDisplay::kNone)
     return false;
 
   if (!containsOnlyWhitespace())
diff --git a/third_party/WebKit/Source/core/editing/EditingUtilities.cpp b/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
index 76af4bce..53f009d 100644
--- a/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
@@ -918,7 +918,7 @@
     return false;
 
   const ComputedStyle* style = node->computedStyle();
-  return style && style->display() == EDisplay::Inline;
+  return style && style->display() == EDisplay::kInline;
 }
 
 // TODO(yosin) Deploy this in all of the places where |enclosingBlockFlow()| and
@@ -1066,8 +1066,8 @@
   if (!layoutObject)
     return false;
 
-  if (layoutObject->style()->display() == EDisplay::Table ||
-      layoutObject->style()->display() == EDisplay::InlineTable)
+  if (layoutObject->style()->display() == EDisplay::kTable ||
+      layoutObject->style()->display() == EDisplay::kInlineTable)
     return true;
 
   if (layoutObject->style()->isFloating())
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp
index 6bb6beb..f1e1a4f 100644
--- a/third_party/WebKit/Source/core/editing/Editor.cpp
+++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -1416,11 +1416,23 @@
       frame().selection().computeVisibleSelectionInDOMTreeDeprecated())
     frame().selection().setSelection(newSelection);
 
+  if (dispatchBeforeInputInsertText(
+          eventTargetNodeForDocument(frame().document()), transposed,
+          InputEvent::InputType::InsertTranspose) !=
+      DispatchEventResult::NotCanceled)
+    return;
+
+  // 'beforeinput' event handler may destroy document.
+  if (m_frame->document()->frame() != m_frame)
+    return;
+
+  // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets
+  // needs to be audited. see http://crbug.com/590369 for more details.
+  frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
+
   // Insert the transposed characters.
-  // TODO(chongz): Once we add |InsertTranspose| in |InputEvent::InputType|, we
-  // should use it instead of |InsertFromPaste|.
   replaceSelectionWithText(transposed, false, false,
-                           InputEvent::InputType::InsertFromPaste);
+                           InputEvent::InputType::InsertTranspose);
 }
 
 void Editor::addToKillRing(const EphemeralRange& range) {
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index 47629e3..d452857 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -657,6 +657,7 @@
   setSelection(SelectionInDOMTree::Builder()
                    .setBaseAndExtent(range)
                    .setAffinity(affinity)
+                   .setIsHandleVisible(isHandleVisible())
                    .setIsDirectional(directional ==
                                      SelectionDirectionalMode::Directional)
                    .build(),
diff --git a/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp b/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
index d124ac4..9047d80 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
@@ -293,4 +293,37 @@
          "after it.";
 }
 
+TEST_F(FrameSelectionTest, SetSelectedRangePreservesHandle) {
+  Text* text = appendTextNode("Hello, World!");
+  document().view()->updateAllLifecyclePhases();
+  selection().setSelection(
+      SelectionInDOMTree::Builder()
+          .setBaseAndExtent(Position(text, 0), Position(text, 5))
+          .setIsHandleVisible(false)
+          .build());
+
+  selection().setSelectedRange(
+      EphemeralRange(Position(text, 0), Position(text, 12)),
+      VP_DEFAULT_AFFINITY, SelectionDirectionalMode::NonDirectional, 0);
+
+  EXPECT_FALSE(selection().isHandleVisible())
+      << "If handles weren't present before"
+         "setSelectedRange they shouldn't be present"
+         "after it.";
+
+  selection().setSelection(
+      SelectionInDOMTree::Builder()
+          .setBaseAndExtent(Position(text, 0), Position(text, 5))
+          .setIsHandleVisible(true)
+          .build());
+
+  selection().setSelectedRange(
+      EphemeralRange(Position(text, 0), Position(text, 12)),
+      VP_DEFAULT_AFFINITY, SelectionDirectionalMode::NonDirectional, 0);
+
+  EXPECT_TRUE(selection().isHandleVisible())
+      << "If handles were present before"
+         "selectSetSelectedRange they should be present after it.";
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/events/InputEvent.cpp b/third_party/WebKit/Source/core/events/InputEvent.cpp
index ff3f83b..0b84074 100644
--- a/third_party/WebKit/Source/core/events/InputEvent.cpp
+++ b/third_party/WebKit/Source/core/events/InputEvent.cpp
@@ -26,6 +26,7 @@
     {InputEvent::InputType::InsertFromPaste, "insertFromPaste"},
     {InputEvent::InputType::InsertFromDrop, "insertFromDrop"},
     {InputEvent::InputType::InsertFromYank, "insertFromYank"},
+    {InputEvent::InputType::InsertTranspose, "insertTranspose"},
     {InputEvent::InputType::InsertReplacementText, "insertReplacementText"},
     {InputEvent::InputType::InsertCompositionText, "insertCompositionText"},
     {InputEvent::InputType::DeleteWordBackward, "deleteWordBackward"},
diff --git a/third_party/WebKit/Source/core/events/InputEvent.h b/third_party/WebKit/Source/core/events/InputEvent.h
index 51cf3892..03d6497 100644
--- a/third_party/WebKit/Source/core/events/InputEvent.h
+++ b/third_party/WebKit/Source/core/events/InputEvent.h
@@ -35,6 +35,7 @@
     InsertFromPaste,
     InsertFromDrop,
     InsertFromYank,
+    InsertTranspose,
     InsertReplacementText,
     InsertCompositionText,
     // Deletion.
diff --git a/third_party/WebKit/Source/core/frame/FrameConsole.cpp b/third_party/WebKit/Source/core/frame/FrameConsole.cpp
index 2c02a9a..113044b 100644
--- a/third_party/WebKit/Source/core/frame/FrameConsole.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameConsole.cpp
@@ -69,9 +69,9 @@
 }
 
 bool FrameConsole::addMessageToStorage(ConsoleMessage* consoleMessage) {
-  if (!m_frame->document() || !m_frame->host())
+  if (!m_frame->document() || !m_frame->page())
     return false;
-  m_frame->host()->consoleMessageStorage().addConsoleMessage(
+  m_frame->page()->consoleMessageStorage().addConsoleMessage(
       m_frame->document(), consoleMessage);
   return true;
 }
diff --git a/third_party/WebKit/Source/core/frame/FrameHost.cpp b/third_party/WebKit/Source/core/frame/FrameHost.cpp
index 226ca913..bd1177f 100644
--- a/third_party/WebKit/Source/core/frame/FrameHost.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameHost.cpp
@@ -42,9 +42,7 @@
   return new FrameHost(page);
 }
 
-FrameHost::FrameHost(Page& page)
-    : m_page(&page),
-      m_subframeCount(0) {}
+FrameHost::FrameHost(Page& page) : m_page(&page) {}
 
 // Explicitly in the .cpp to avoid default constructor in .h
 FrameHost::~FrameHost() {}
@@ -73,35 +71,19 @@
   return page().overscrollController();
 }
 
-ConsoleMessageStorage& FrameHost::consoleMessageStorage() {
-  return page().consoleMessageStorage();
-}
-
-const ConsoleMessageStorage& FrameHost::consoleMessageStorage() const {
-  return page().consoleMessageStorage();
-}
-
 DEFINE_TRACE(FrameHost) {
   visitor->trace(m_page);
 }
 
-#if DCHECK_IS_ON()
-void checkFrameCountConsistency(int expectedFrameCount, Frame* frame) {
-  ASSERT(expectedFrameCount >= 0);
-
-  int actualFrameCount = 0;
-  for (; frame; frame = frame->tree().traverseNext())
-    ++actualFrameCount;
-
-  ASSERT(expectedFrameCount == actualFrameCount);
+void FrameHost::incrementSubframeCount() {
+  page().incrementSubframeCount();
 }
-#endif
 
+void FrameHost::decrementSubframeCount() {
+  page().decrementSubframeCount();
+}
 int FrameHost::subframeCount() const {
-#if DCHECK_IS_ON()
-  checkFrameCountConsistency(m_subframeCount + 1, m_page->mainFrame());
-#endif
-  return m_subframeCount;
+  return page().subframeCount();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/FrameHost.h b/third_party/WebKit/Source/core/frame/FrameHost.h
index 593a01da..1a8ce9e 100644
--- a/third_party/WebKit/Source/core/frame/FrameHost.h
+++ b/third_party/WebKit/Source/core/frame/FrameHost.h
@@ -41,7 +41,6 @@
 namespace blink {
 
 class BrowserControls;
-class ConsoleMessageStorage;
 class OverscrollController;
 class Page;
 
@@ -72,29 +71,16 @@
   OverscrollController& overscrollController();
   const OverscrollController& overscrollController() const;
 
-  ConsoleMessageStorage& consoleMessageStorage();
-  const ConsoleMessageStorage& consoleMessageStorage() const;
-
   DECLARE_TRACE();
 
-  // Don't allow more than a certain number of frames in a page.
-  // This seems like a reasonable upper bound, and otherwise mutually
-  // recursive frameset pages can quickly bring the program to its knees
-  // with exponential growth in the number of frames.
-  static const int maxNumberOfFrames = 1000;
-  void incrementSubframeCount() { ++m_subframeCount; }
-  void decrementSubframeCount() {
-    ASSERT(m_subframeCount);
-    --m_subframeCount;
-  }
+  void incrementSubframeCount();
+  void decrementSubframeCount();
   int subframeCount() const;
 
  private:
   explicit FrameHost(Page&);
 
   const Member<Page> m_page;
-
-  int m_subframeCount;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/FrameOwner.h b/third_party/WebKit/Source/core/frame/FrameOwner.h
index cbc0741..440b85e 100644
--- a/third_party/WebKit/Source/core/frame/FrameOwner.h
+++ b/third_party/WebKit/Source/core/frame/FrameOwner.h
@@ -11,7 +11,6 @@
 #include "platform/scroll/ScrollTypes.h"
 #include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebVector.h"
-#include "public/platform/modules/permissions/permission.mojom-blink.h"
 
 namespace blink {
 
@@ -50,8 +49,6 @@
   virtual bool allowFullscreen() const = 0;
   virtual bool allowPaymentRequest() const = 0;
   virtual AtomicString csp() const = 0;
-  virtual const WebVector<mojom::blink::PermissionName>& delegatedPermissions()
-      const = 0;
   virtual const WebVector<WebFeaturePolicyFeature>& allowedFeatures() const = 0;
 };
 
@@ -85,12 +82,6 @@
   bool allowFullscreen() const override { return false; }
   bool allowPaymentRequest() const override { return false; }
   AtomicString csp() const override { return nullAtom; }
-  const WebVector<mojom::blink::PermissionName>& delegatedPermissions()
-      const override {
-    DEFINE_STATIC_LOCAL(WebVector<mojom::blink::PermissionName>, permissions,
-                        ());
-    return permissions;
-  }
   const WebVector<WebFeaturePolicyFeature>& allowedFeatures() const override {
     DEFINE_STATIC_LOCAL(WebVector<WebFeaturePolicyFeature>, features, ());
     return features;
diff --git a/third_party/WebKit/Source/core/frame/RootFrameViewport.h b/third_party/WebKit/Source/core/frame/RootFrameViewport.h
index 33ccd98..72c87ed6 100644
--- a/third_party/WebKit/Source/core/frame/RootFrameViewport.h
+++ b/third_party/WebKit/Source/core/frame/RootFrameViewport.h
@@ -86,9 +86,11 @@
   GraphicsLayer* layerForVerticalScrollbar() const override;
   GraphicsLayer* layerForScrollCorner() const override;
   int horizontalScrollbarHeight(
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
   int verticalScrollbarWidth(
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
   ScrollResult userScroll(ScrollGranularity, const FloatSize&) override;
   bool scrollAnimatorEnabled() const override;
   HostWindow* getHostWindow() const override;
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 4eda6c2..74ba6b1 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1487,6 +1487,7 @@
     VRPoseLinearAcceleration = 1864,
     VRPoseAngularVelocity = 1865,
     VRPoseAngularAcceleration = 1866,
+    CSSOverflowPaged = 1867,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/BUILD.gn b/third_party/WebKit/Source/core/html/BUILD.gn
index bcf57722..f605c3d 100644
--- a/third_party/WebKit/Source/core/html/BUILD.gn
+++ b/third_party/WebKit/Source/core/html/BUILD.gn
@@ -85,8 +85,6 @@
     "HTMLIFrameElement.cpp",
     "HTMLIFrameElementAllow.cpp",
     "HTMLIFrameElementAllow.h",
-    "HTMLIFrameElementPermissions.cpp",
-    "HTMLIFrameElementPermissions.h",
     "HTMLIFrameElementSandbox.cpp",
     "HTMLIFrameElementSandbox.h",
     "HTMLImageElement.cpp",
diff --git a/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5 b/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5
index 81c2d4d..c09bc0b4 100644
--- a/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5
+++ b/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5
@@ -293,7 +293,6 @@
     "open",
     "optimum",
     "pattern",
-    "permissions",
     "placeholder",
     "ping",
     "poster",
diff --git a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
index 43b5606b..2ab5f5c 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
@@ -123,13 +123,14 @@
 
   EDisplay display = style.display();
   bool formIsTablePart =
-      display == EDisplay::Table || display == EDisplay::InlineTable ||
-      display == EDisplay::TableRowGroup ||
-      display == EDisplay::TableHeaderGroup ||
-      display == EDisplay::TableFooterGroup || display == EDisplay::TableRow ||
-      display == EDisplay::TableColumnGroup ||
-      display == EDisplay::TableColumn || display == EDisplay::TableCell ||
-      display == EDisplay::TableCaption;
+      display == EDisplay::kTable || display == EDisplay::kInlineTable ||
+      display == EDisplay::kTableRowGroup ||
+      display == EDisplay::kTableHeaderGroup ||
+      display == EDisplay::kTableFooterGroup ||
+      display == EDisplay::kTableRow ||
+      display == EDisplay::kTableColumnGroup ||
+      display == EDisplay::kTableColumn || display == EDisplay::kTableCell ||
+      display == EDisplay::kTableCaption;
 
   return formIsTablePart;
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp b/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp
index 0efebb76..a2b4602 100644
--- a/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp
@@ -33,6 +33,7 @@
 #include "core/layout/api/LayoutPartItem.h"
 #include "core/loader/FrameLoadRequest.h"
 #include "core/loader/FrameLoader.h"
+#include "core/page/Page.h"
 #include "core/plugins/PluginView.h"
 #include "platform/weborigin/SecurityOrigin.h"
 
@@ -227,12 +228,6 @@
   dispatchScopedEvent(Event::create(EventTypeNames::load));
 }
 
-const WebVector<mojom::blink::PermissionName>&
-HTMLFrameOwnerElement::delegatedPermissions() const {
-  DEFINE_STATIC_LOCAL(WebVector<mojom::blink::PermissionName>, permissions, ());
-  return permissions;
-}
-
 const WebVector<WebFeaturePolicyFeature>&
 HTMLFrameOwnerElement::allowedFeatures() const {
   DEFINE_STATIC_LOCAL(WebVector<WebFeaturePolicyFeature>, features, ());
@@ -312,8 +307,7 @@
   if (!SubframeLoadingDisabler::canLoadFrame(*this))
     return false;
 
-  if (document().frame()->host()->subframeCount() >=
-      FrameHost::maxNumberOfFrames)
+  if (document().frame()->host()->subframeCount() >= Page::maxNumberOfFrames)
     return false;
 
   FrameLoadRequest frameLoadRequest(&document(), url, "_self",
diff --git a/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h b/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h
index 7f402265..a80c0d7 100644
--- a/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h
@@ -91,8 +91,6 @@
   bool allowFullscreen() const override { return false; }
   bool allowPaymentRequest() const override { return false; }
   AtomicString csp() const override { return nullAtom; }
-  const WebVector<mojom::blink::PermissionName>& delegatedPermissions()
-      const override;
   const WebVector<WebFeaturePolicyFeature>& allowedFeatures() const override;
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
index 90a0daed..a487c30 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
@@ -47,7 +47,6 @@
 
 DEFINE_TRACE(HTMLIFrameElement) {
   visitor->trace(m_sandbox);
-  visitor->trace(m_permissions);
   visitor->trace(m_allow);
   HTMLFrameElementBase::trace(visitor);
   Supplementable<HTMLIFrameElement>::trace(visitor);
@@ -59,12 +58,6 @@
   return m_sandbox.get();
 }
 
-DOMTokenList* HTMLIFrameElement::permissions() const {
-  if (!const_cast<HTMLIFrameElement*>(this)->initializePermissionsAttribute())
-    return nullptr;
-  return m_permissions.get();
-}
-
 DOMTokenList* HTMLIFrameElement::allow() const {
   return m_allow.get();
 }
@@ -147,9 +140,6 @@
     m_allowPaymentRequest = !value.isNull();
     if (m_allowPaymentRequest != oldAllowPaymentRequest)
       frameOwnerPropertiesChanged();
-  } else if (name == permissionsAttr) {
-    if (initializePermissionsAttribute())
-      m_permissions->setValue(value);
   } else if (RuntimeEnabledFeatures::embedderCSPEnforcementEnabled() &&
              name == cspAttr) {
     // TODO(amalika): add more robust validation of the value
@@ -202,21 +192,6 @@
   return true;
 }
 
-void HTMLIFrameElement::permissionsValueWasSet() {
-  if (!initializePermissionsAttribute())
-    return;
-
-  String invalidTokens;
-  m_delegatedPermissions =
-      m_permissions->parseDelegatedPermissions(invalidTokens);
-  if (!invalidTokens.isNull())
-    document().addConsoleMessage(ConsoleMessage::create(
-        OtherMessageSource, ErrorMessageLevel,
-        "Error while parsing the 'permissions' attribute: " + invalidTokens));
-  setSynchronizedLazyAttribute(permissionsAttr, m_permissions->value());
-  frameOwnerPropertiesChanged();
-}
-
 void HTMLIFrameElement::sandboxValueWasSet() {
   String invalidTokens;
   setSandboxFlags(m_sandbox->value().isNull()
@@ -245,13 +220,4 @@
   return m_referrerPolicy;
 }
 
-bool HTMLIFrameElement::initializePermissionsAttribute() {
-  if (!RuntimeEnabledFeatures::permissionDelegationEnabled())
-    return false;
-
-  if (!m_permissions)
-    m_permissions = HTMLIFrameElementPermissions::create(this);
-  return true;
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.h b/third_party/WebKit/Source/core/html/HTMLIFrameElement.h
index b137443..15b659d 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.h
@@ -27,12 +27,10 @@
 #include "core/CoreExport.h"
 #include "core/html/HTMLFrameElementBase.h"
 #include "core/html/HTMLIFrameElementAllow.h"
-#include "core/html/HTMLIFrameElementPermissions.h"
 #include "core/html/HTMLIFrameElementSandbox.h"
 #include "platform/Supplementable.h"
 #include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebVector.h"
-#include "public/platform/modules/permissions/permission.mojom-blink.h"
 
 namespace blink {
 
@@ -47,11 +45,9 @@
   DECLARE_VIRTUAL_TRACE();
   ~HTMLIFrameElement() override;
   DOMTokenList* sandbox() const;
-  DOMTokenList* permissions() const;
   DOMTokenList* allow() const;
 
   void sandboxValueWasSet();
-  void permissionsValueWasSet();
   void allowValueWasSet();
 
  private:
@@ -81,26 +77,18 @@
   bool allowFullscreen() const override { return m_allowFullscreen; }
   bool allowPaymentRequest() const override { return m_allowPaymentRequest; }
   AtomicString csp() const override { return m_csp; }
-  const WebVector<mojom::blink::PermissionName>& delegatedPermissions()
-      const override {
-    return m_delegatedPermissions;
-  }
   const WebVector<WebFeaturePolicyFeature>& allowedFeatures() const override {
     return m_allowedFeatures;
   }
 
-  bool initializePermissionsAttribute();
-
   AtomicString m_name;
   AtomicString m_csp;
   bool m_didLoadNonEmptyDocument;
   bool m_allowFullscreen;
   bool m_allowPaymentRequest;
   Member<HTMLIFrameElementSandbox> m_sandbox;
-  Member<HTMLIFrameElementPermissions> m_permissions;
   Member<HTMLIFrameElementAllow> m_allow;
 
-  WebVector<mojom::blink::PermissionName> m_delegatedPermissions;
   WebVector<WebFeaturePolicyFeature> m_allowedFeatures;
 
   ReferrerPolicy m_referrerPolicy;
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.idl b/third_party/WebKit/Source/core/html/HTMLIFrameElement.idl
index 862f9aa..c6c13d1d 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.idl
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.idl
@@ -34,8 +34,6 @@
     readonly attribute Window? contentWindow;
     [CheckSecurity=ReturnValue, RaisesException] Document? getSVGDocument();
     [CEReactions, Reflect, ReflectOnly=("","no-referrer","origin","no-referrer-when-downgrade","origin-when-cross-origin","unsafe-url"), ReflectMissing="", ReflectInvalid=""] attribute DOMString referrerPolicy;
-    // https://noncombatant.github.io/permission-delegation-api/#delegation-via-the-declarative-api
-    [RuntimeEnabled=PermissionDelegation, PutForwards=value] readonly attribute DOMTokenList permissions;
     // https://w3c.github.io/webappsec-csp/embedded/#dom-htmliframeelement-csp
     [RuntimeEnabled=EmbedderCSPEnforcement, CEReactions, Reflect] attribute DOMString csp;
 
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissions.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissions.cpp
deleted file mode 100644
index f5bff6e0..0000000
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissions.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSDstyle license that can be
-// found in the LICENSE file.
-
-#include "core/html/HTMLIFrameElementPermissions.h"
-
-#include "core/html/HTMLIFrameElement.h"
-#include "wtf/HashMap.h"
-#include "wtf/text/StringBuilder.h"
-
-namespace blink {
-
-namespace {
-
-struct SupportedPermission {
-  const char* name;
-  mojom::blink::PermissionName type;
-};
-
-const SupportedPermission kSupportedPermissions[] = {
-    {"geolocation", mojom::blink::PermissionName::GEOLOCATION},
-    {"notifications", mojom::blink::PermissionName::NOTIFICATIONS},
-    {"midi", mojom::blink::PermissionName::MIDI},
-};
-
-// Returns true if the name is valid and the type is stored in |result|.
-bool getPermissionType(const AtomicString& name,
-                       mojom::blink::PermissionName* result) {
-  for (const SupportedPermission& permission : kSupportedPermissions) {
-    if (name == permission.name) {
-      if (result)
-        *result = permission.type;
-      return true;
-    }
-  }
-  return false;
-}
-
-}  // namespace
-
-HTMLIFrameElementPermissions::HTMLIFrameElementPermissions(
-    HTMLIFrameElement* element)
-    : DOMTokenList(this), m_element(element) {}
-
-HTMLIFrameElementPermissions::~HTMLIFrameElementPermissions() {}
-
-DEFINE_TRACE(HTMLIFrameElementPermissions) {
-  visitor->trace(m_element);
-  DOMTokenList::trace(visitor);
-  DOMTokenListObserver::trace(visitor);
-}
-
-Vector<mojom::blink::PermissionName>
-HTMLIFrameElementPermissions::parseDelegatedPermissions(
-    String& invalidTokensErrorMessage) const {
-  Vector<blink::mojom::blink::PermissionName> permissions;
-  unsigned numTokenErrors = 0;
-  StringBuilder tokenErrors;
-  const SpaceSplitString& tokens = this->tokens();
-
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    blink::mojom::blink::PermissionName type;
-    if (getPermissionType(tokens[i], &type)) {
-      permissions.push_back(type);
-    } else {
-      tokenErrors.append(tokenErrors.isEmpty() ? "'" : ", '");
-      tokenErrors.append(tokens[i]);
-      tokenErrors.append("'");
-      ++numTokenErrors;
-    }
-  }
-
-  if (numTokenErrors) {
-    tokenErrors.append(numTokenErrors > 1 ? " are invalid permissions flags."
-                                          : " is an invalid permissions flag.");
-    invalidTokensErrorMessage = tokenErrors.toString();
-  }
-
-  return permissions;
-}
-
-bool HTMLIFrameElementPermissions::validateTokenValue(
-    const AtomicString& tokenValue,
-    ExceptionState&) const {
-  mojom::blink::PermissionName unused;
-  return getPermissionType(tokenValue, &unused);
-}
-
-void HTMLIFrameElementPermissions::valueWasSet() {
-  if (m_element)
-    m_element->permissionsValueWasSet();
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissions.h b/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissions.h
deleted file mode 100644
index 7d0409b..0000000
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissions.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSDstyle license that can be
-// found in the LICENSE file.
-
-#ifndef HTMLIFrameElementPermissions_h
-#define HTMLIFrameElementPermissions_h
-
-#include "core/CoreExport.h"
-#include "core/dom/DOMTokenList.h"
-#include "platform/heap/Handle.h"
-#include "public/platform/modules/permissions/permission.mojom-blink.h"
-#include "wtf/Vector.h"
-
-namespace blink {
-
-class HTMLIFrameElement;
-
-class CORE_EXPORT HTMLIFrameElementPermissions final
-    : public DOMTokenList,
-      public DOMTokenListObserver {
-  USING_GARBAGE_COLLECTED_MIXIN(HTMLIFrameElementPermissions);
-
- public:
-  static HTMLIFrameElementPermissions* create(HTMLIFrameElement* element) {
-    return new HTMLIFrameElementPermissions(element);
-  }
-
-  ~HTMLIFrameElementPermissions() override;
-
-  Vector<mojom::blink::PermissionName> parseDelegatedPermissions(
-      String& invalidTokensErrorMessage) const;
-
-  DECLARE_VIRTUAL_TRACE();
-
- private:
-  explicit HTMLIFrameElementPermissions(HTMLIFrameElement*);
-  bool validateTokenValue(const AtomicString& tokenValue,
-                          ExceptionState&) const override;
-
-  // DOMTokenListObserver.
-  void valueWasSet() override;
-
-  Member<HTMLIFrameElement> m_element;
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissionsTest.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissionsTest.cpp
deleted file mode 100644
index 920b741..0000000
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElementPermissionsTest.cpp
+++ /dev/null
@@ -1,58 +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.
-
-#include "core/html/HTMLIFrameElementPermissions.h"
-
-#include "public/platform/modules/permissions/permission.mojom-blink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-TEST(HTMLIFrameElementPermissionsTest, ParseDelegatedPermissionsValid) {
-  HTMLIFrameElementPermissions* permissions =
-      HTMLIFrameElementPermissions::create(nullptr);
-  Vector<mojom::blink::PermissionName> result;
-  String errorMessage;
-
-  permissions->setValue("midi");
-  result = permissions->parseDelegatedPermissions(errorMessage);
-  EXPECT_EQ(1u, result.size());
-  EXPECT_EQ(mojom::blink::PermissionName::MIDI, result[0]);
-
-  permissions->setValue("");
-  result = permissions->parseDelegatedPermissions(errorMessage);
-  EXPECT_EQ(0u, result.size());
-
-  permissions->setValue("geolocation midi");
-  result = permissions->parseDelegatedPermissions(errorMessage);
-  EXPECT_EQ(2u, result.size());
-  EXPECT_EQ(mojom::blink::PermissionName::GEOLOCATION, result[0]);
-  EXPECT_EQ(mojom::blink::PermissionName::MIDI, result[1]);
-}
-
-TEST(HTMLIFrameElementPermissionsTest, ParseDelegatedPermissionsInvalid) {
-  HTMLIFrameElementPermissions* permissions =
-      HTMLIFrameElementPermissions::create(nullptr);
-  Vector<mojom::blink::PermissionName> result;
-  String errorMessage;
-
-  permissions->setValue("midis");
-  result = permissions->parseDelegatedPermissions(errorMessage);
-  EXPECT_EQ(0u, result.size());
-  EXPECT_EQ("'midis' is an invalid permissions flag.", errorMessage);
-
-  permissions->setValue("geolocations midis");
-  result = permissions->parseDelegatedPermissions(errorMessage);
-  EXPECT_EQ(0u, result.size());
-  EXPECT_EQ("'geolocations', 'midis' are invalid permissions flags.",
-            errorMessage);
-
-  permissions->setValue("geolocation midis");
-  result = permissions->parseDelegatedPermissions(errorMessage);
-  EXPECT_EQ(1u, result.size());
-  EXPECT_EQ(mojom::blink::PermissionName::GEOLOCATION, result[0]);
-  EXPECT_EQ("'midis' is an invalid permissions flag.", errorMessage);
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElementTest.cpp
index 765ef853..a84b500 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElementTest.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElementTest.cpp
@@ -9,26 +9,6 @@
 
 namespace blink {
 
-// Test setting permission via the Element attribute (HTML codepath).
-TEST(HTMLIFrameElementTest, SetPermissionsAttribute) {
-  Document* document = Document::create();
-  HTMLIFrameElement* iframe = HTMLIFrameElement::create(*document);
-
-  iframe->setAttribute(HTMLNames::permissionsAttr, "geolocation");
-  EXPECT_EQ("geolocation", iframe->permissions()->value());
-  iframe->setAttribute(HTMLNames::permissionsAttr, "geolocation notifications");
-  EXPECT_EQ("geolocation notifications", iframe->permissions()->value());
-}
-
-// Test setting permission via the DOMTokenList (JS codepath).
-TEST(HTMLIFrameElementTest, SetPermissionsAttributeJS) {
-  Document* document = Document::create();
-  HTMLIFrameElement* iframe = HTMLIFrameElement::create(*document);
-
-  iframe->permissions()->setValue("midi");
-  EXPECT_EQ("midi", iframe->getAttribute(HTMLNames::permissionsAttr));
-}
-
 // Test setting feature policy via the Element attribute (HTML codepath).
 TEST(HTMLIFrameElementTest, SetAllowAttribute) {
   Document* document = Document::create();
diff --git a/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp b/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp
index cbf51fa..bc2f771 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp
@@ -124,7 +124,7 @@
       !newStyle->width().isSpecifiedOrIntrinsic() &&
       !newStyle->height().isSpecifiedOrIntrinsic() &&
       toHTMLElement(element).altText().isEmpty())
-    newStyle->setDisplay(EDisplay::None);
+    newStyle->setDisplay(EDisplay::kNone);
 
   // This preserves legacy behaviour originally defined when alt-text was
   // managed by LayoutImage.
diff --git a/third_party/WebKit/Source/core/html/HTMLOptionElement.cpp b/third_party/WebKit/Source/core/html/HTMLOptionElement.cpp
index a169c3f..10cb65b 100644
--- a/third_party/WebKit/Source/core/html/HTMLOptionElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLOptionElement.cpp
@@ -417,7 +417,7 @@
   if (!style)
     return false;
 
-  if (style->display() != EDisplay::None) {
+  if (style->display() != EDisplay::kNone) {
     // We need to check the parent's display property.  Parent's
     // display:none doesn't override children's display properties in
     // ComputedStyle.
@@ -427,10 +427,10 @@
       const ComputedStyle* parentStyle = parent->computedStyle()
                                              ? parent->computedStyle()
                                              : parent->ensureComputedStyle();
-      return !parentStyle || parentStyle->display() == EDisplay::None;
+      return !parentStyle || parentStyle->display() == EDisplay::kNone;
     }
   }
-  return style->display() == EDisplay::None;
+  return style->display() == EDisplay::kNone;
 }
 
 String HTMLOptionElement::innerText() {
diff --git a/third_party/WebKit/Source/core/html/HTMLRTElement.cpp b/third_party/WebKit/Source/core/html/HTMLRTElement.cpp
index 65bb354..fba8132 100644
--- a/third_party/WebKit/Source/core/html/HTMLRTElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLRTElement.cpp
@@ -17,7 +17,7 @@
 DEFINE_NODE_FACTORY(HTMLRTElement)
 
 LayoutObject* HTMLRTElement::createLayoutObject(const ComputedStyle& style) {
-  if (style.display() == EDisplay::Block)
+  if (style.display() == EDisplay::kBlock)
     return new LayoutRubyText(this);
   return LayoutObject::createObject(this, style);
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLRubyElement.cpp b/third_party/WebKit/Source/core/html/HTMLRubyElement.cpp
index 966eb222..cb7b429e 100644
--- a/third_party/WebKit/Source/core/html/HTMLRubyElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLRubyElement.cpp
@@ -17,9 +17,9 @@
 DEFINE_NODE_FACTORY(HTMLRubyElement)
 
 LayoutObject* HTMLRubyElement::createLayoutObject(const ComputedStyle& style) {
-  if (style.display() == EDisplay::Inline)
+  if (style.display() == EDisplay::kInline)
     return new LayoutRubyAsInline(this);
-  if (style.display() == EDisplay::Block)
+  if (style.display() == EDisplay::kBlock)
     return new LayoutRubyAsBlock(this);
   return LayoutObject::createObject(this, style);
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
index 8ee20dd..a37093e 100644
--- a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
@@ -1834,7 +1834,7 @@
   if (isHTMLOptionElement(element))
     return toHTMLOptionElement(element).isDisplayNone();
   if (const ComputedStyle* style = itemComputedStyle(element))
-    return style->display() == EDisplay::None;
+    return style->display() == EDisplay::kNone;
   return false;
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp b/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp
index f789441..a5e0740 100644
--- a/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp
@@ -47,8 +47,8 @@
 LayoutObject* HTMLSummaryElement::createLayoutObject(
     const ComputedStyle& style) {
   EDisplay display = style.display();
-  if (display == EDisplay::Flex || display == EDisplay::InlineFlex ||
-      display == EDisplay::Grid || display == EDisplay::InlineGrid)
+  if (display == EDisplay::kFlex || display == EDisplay::kInlineFlex ||
+      display == EDisplay::kGrid || display == EDisplay::kInlineGrid)
     return LayoutObject::createObject(this, style);
   return new LayoutBlockFlow(this);
 }
diff --git a/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp b/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp
index 2094e3a6..11bed40 100644
--- a/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp
+++ b/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp
@@ -333,11 +333,11 @@
     PassRefPtr<ComputedStyle> originalStyle) {
   EDisplay originalDisplay = originalStyle->display();
   EDisplay newDisplay = originalDisplay;
-  if (originalDisplay == EDisplay::Inline ||
-      originalDisplay == EDisplay::InlineBlock)
-    newDisplay = EDisplay::InlineFlex;
-  else if (originalDisplay == EDisplay::Block)
-    newDisplay = EDisplay::Flex;
+  if (originalDisplay == EDisplay::kInline ||
+      originalDisplay == EDisplay::kInlineBlock)
+    newDisplay = EDisplay::kInlineFlex;
+  else if (originalDisplay == EDisplay::kBlock)
+    newDisplay = EDisplay::kFlex;
   TextDirection contentDirection = computedTextDirection();
   if (originalStyle->direction() == contentDirection &&
       originalDisplay == newDisplay)
diff --git a/third_party/WebKit/Source/core/html/shadow/TextControlInnerElements.cpp b/third_party/WebKit/Source/core/html/shadow/TextControlInnerElements.cpp
index 7ba751e3..5c3e19fa 100644
--- a/third_party/WebKit/Source/core/html/shadow/TextControlInnerElements.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/TextControlInnerElements.cpp
@@ -81,7 +81,7 @@
 
   style->setFlexGrow(1);
   style->setMinWidth(Length(0, Fixed));
-  style->setDisplay(EDisplay::Block);
+  style->setDisplay(EDisplay::kBlock);
   style->setDirection(TextDirection::kLtr);
 
   // We don't want the shadow dom to be editable, so we set this block to
diff --git a/third_party/WebKit/Source/core/inspector/BUILD.gn b/third_party/WebKit/Source/core/inspector/BUILD.gn
index 42eec860..1a179aafe 100644
--- a/third_party/WebKit/Source/core/inspector/BUILD.gn
+++ b/third_party/WebKit/Source/core/inspector/BUILD.gn
@@ -92,24 +92,29 @@
   ]
 }
 
-action("instrumentation_sources") {
+# instrumentation probes -------------------------------------------------------
+
+action("instrumentation_probes") {
   visibility = [ ":*" ]
-  script = "CodeGeneratorInstrumentation.py"
+  script = "InstrumentingProbesCodeGenerator.py"
 
   inputs = [
     # Input file for the script.
-    "InspectorInstrumentation.idl",
+    "InstrumentingProbes.idl",
+    "InstrumentingProbesImpl_cpp.template",
+    "InstrumentingProbesImpl_h.template",
+    "InstrumentingAgents_h.template",
   ]
 
   outputs = [
     "$blink_core_output_dir/InspectorInstrumentationInl.h",
     "$blink_core_output_dir/InspectorOverridesInl.h",
+    "$blink_core_output_dir/InstrumentingProbesImpl.cpp",
     "$blink_core_output_dir/InstrumentingAgents.h",
-    "$blink_core_output_dir/InspectorInstrumentationImpl.cpp",
   ]
 
   args = [
-    rebase_path("InspectorInstrumentation.idl", root_build_dir),
+    rebase_path("InstrumentingProbes.idl", root_build_dir),
     "--output_dir",
     rebase_path(blink_core_output_dir, root_build_dir),
   ]
@@ -194,7 +199,7 @@
 # Compiles the sources generated above.
 source_set("generated") {
   sources = get_target_outputs(":protocol_sources") +
-            get_target_outputs(":instrumentation_sources")
+            get_target_outputs(":instrumentation_probes")
 
   configs -= core_config_remove
   configs += core_config_add + [
@@ -207,7 +212,7 @@
   }
 
   deps = [
-    ":instrumentation_sources",
+    ":instrumentation_probes",
     ":protocol_sources",
     "//skia",
     "//third_party/WebKit/Source/bindings/core/v8:bindings_core_v8_generated",
diff --git a/third_party/WebKit/Source/core/inspector/CodeGeneratorInstrumentation.py b/third_party/WebKit/Source/core/inspector/CodeGeneratorInstrumentation.py
deleted file mode 100755
index cc3c1b1..0000000
--- a/third_party/WebKit/Source/core/inspector/CodeGeneratorInstrumentation.py
+++ /dev/null
@@ -1,559 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2013 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import optparse
-import re
-import string
-import sys
-
-template_h = string.Template("""// Code generated from InspectorInstrumentation.idl
-
-#ifndef ${file_name}_h
-#define ${file_name}_h
-
-${includes}
-
-namespace blink {
-
-${forward_declarations}
-
-namespace probe {
-
-$methods
-} // namespace probe
-} // namespace blink
-
-#endif // !defined(${file_name}_h)
-""")
-
-template_cpp = string.Template("""// Code generated from InspectorInstrumentation.idl
-
-${includes}
-
-namespace blink {
-${extra_definitions}
-
-namespace probe {
-
-$methods
-
-} // namespace probe
-} // namespace blink
-""")
-
-template_scoped_decl = string.Template("""
-class CORE_EXPORT ${name} : public ProbeBase {
-    STACK_ALLOCATED()
-public:
-    explicit $name($param_list);
-    ~${name}();
-${member_list}
-};""")
-
-template_impl = string.Template("""
-${return_type} ${name}(${params})
-{
-    InstrumentingAgents* agents = instrumentingAgentsFor(${first_param_name});
-    if (!agents)
-        ${default_return}${impl_lines}${maybe_default_return}
-}""")
-
-template_agent_call = string.Template("""
-    if (agents->has${agent_class}s()) {
-        for (${agent_class}* agent : agents->${agent_getter}s())
-            ${maybe_return}agent->${name}(${params_agent});
-    }""")
-
-template_scoped_impl = string.Template("""
-${name}::${name}(${params})${init_list}
-{
-    InstrumentingAgents* agents = instrumentingAgentsFor(${first_param_name});
-    if (!agents)
-        return;${will_body_lines}
-}
-${name}::~${name}()
-{
-    InstrumentingAgents* agents = instrumentingAgentsFor(${first_param_name});
-    if (!agents)
-        return;${did_body_lines}
-}""")
-
-template_instrumenting_agents_h = string.Template("""// Code generated from InspectorInstrumentation.idl
-
-#ifndef InstrumentingAgents_h
-#define InstrumentingAgents_h
-
-#include "core/CoreExport.h"
-#include "platform/heap/Handle.h"
-#include "wtf/Allocator.h"
-#include "wtf/Noncopyable.h"
-#include "wtf/PassRefPtr.h"
-
-namespace blink {
-
-${forward_list}
-
-class CORE_EXPORT InstrumentingAgents : public GarbageCollected<InstrumentingAgents> {
-    WTF_MAKE_NONCOPYABLE(InstrumentingAgents);
-public:
-    InstrumentingAgents();
-    DECLARE_TRACE();
-${accessor_list}
-
-private:
-${member_list}
-};
-
-}
-
-#endif // !defined(InstrumentingAgents_h)
-""")
-
-template_instrumenting_agent_accessor = string.Template("""
-    bool has${class_name}s() const { return ${has_member_name}; }
-    const HeapHashSet<Member<${class_name}>>& ${getter_name}s() const { return ${member_name}; }
-    void add${class_name}(${class_name}* agent);
-    void remove${class_name}(${class_name}* agent);""")
-
-template_instrumenting_agent_impl = string.Template("""
-void InstrumentingAgents::add${class_name}(${class_name}* agent)
-{
-    ${member_name}.insert(agent);
-    ${has_member_name} = true;
-}
-
-void InstrumentingAgents::remove${class_name}(${class_name}* agent)
-{
-    ${member_name}.erase(agent);
-    ${has_member_name} = !${member_name}.isEmpty();
-}
-""")
-
-template_instrumenting_agents_cpp = string.Template("""
-InstrumentingAgents::InstrumentingAgents()
-    : $init_list
-{
-}
-${impl_list}
-DEFINE_TRACE(InstrumentingAgents)
-{
-    $trace_list
-}""")
-
-
-
-def match_and_consume(pattern, source):
-    match = re.match(pattern, source)
-    if match:
-        return match, source[len(match.group(0)):].strip()
-    return None, source
-
-
-def load_model_from_idl(source):
-    source = re.sub("//.*", "", source)  # Remove line comments
-    source = re.sub("/\*(.|\n)*?\*/", "", source, re.MULTILINE)  # Remove block comments
-    source = re.sub("\]\s*?\n\s*", "] ", source)  # Merge the method annotation with the next line
-    source = source.strip()
-
-    model = []
-
-    while len(source):
-        match, source = match_and_consume("interface\s(\w*)\s?\{([^\{]*)\}", source)
-        if not match:
-            sys.stderr.write("Cannot parse %s\n" % source[:100])
-            sys.exit(1)
-        model.append(File(match.group(1), match.group(2)))
-
-    return model
-
-
-class File:
-    def __init__(self, name, source):
-        self.name = name
-        self.header_name = self.name + "Inl"
-        self.includes = [include_inspector_header("InspectorInstrumentation")]
-        self.forward_declarations = []
-        self.declarations = []
-        for line in map(str.strip, source.split("\n")):
-            line = re.sub("\s{2,}", " ", line).strip()  # Collapse whitespace
-            if len(line) == 0:
-                continue
-            if line[0] == "#":
-                self.includes.append(line)
-            elif line.startswith("class "):
-                self.forward_declarations.append(line)
-            else:
-                self.declarations.append(Method(line))
-        self.includes.sort()
-        self.forward_declarations.sort()
-
-    def generate(self, cpp_lines, used_agents):
-        header_lines = []
-        for declaration in self.declarations:
-            for agent in set(declaration.agents):
-                used_agents.add(agent)
-            declaration.generate_header(header_lines)
-            declaration.generate_cpp(cpp_lines)
-
-        return template_h.substitute(None,
-                                     file_name=self.header_name,
-                                     includes="\n".join(self.includes),
-                                     forward_declarations="\n".join(self.forward_declarations),
-                                     methods="\n".join(header_lines))
-
-
-class Method:
-    def __init__(self, source):
-        match = re.match("(\[[\w|,|=|\s]*\])?\s?(\w*\*?) (\w*)\((.*)\)\s?;", source)
-        if not match:
-            sys.stderr.write("Cannot parse %s\n" % source)
-            sys.exit(1)
-
-        self.options = []
-        if match.group(1):
-            options_str = re.sub("\s", "", match.group(1)[1:-1])
-            if len(options_str) != 0:
-                self.options = options_str.split(",")
-
-        self.return_type = match.group(2)
-
-        self.name = match.group(3)
-
-        # Splitting parameters by a comma, assuming that attribute lists contain no more than one attribute.
-        self.params = map(Parameter, map(str.strip, match.group(4).split(",")))
-
-        self.returns_value = self.return_type == "bool"
-        if self.return_type == "bool":
-            self.default_return_value = "false"
-        elif self.returns_value:
-            sys.stderr.write("Can only return bool: %s\n" % self.name)
-            sys.exit(1)
-
-        self.params_agent = self.params
-        if "Keep" not in self.params_agent[0].options:
-            self.params_agent = self.params_agent[1:]
-
-        self.agents = filter(lambda option: not "=" in option, self.options)
-
-        if self.returns_value and len(self.agents) > 1:
-            sys.stderr.write("Can only return value from a single agent: %s\n" % self.name)
-            sys.exit(1)
-
-    def is_scoped(self):
-        return self.return_type == ""
-
-    def generate_header(self, header_lines):
-        param_list = ", ".join(map(Parameter.to_str_class_and_value, self.params))
-        if self.is_scoped():
-            member_list = "\n".join(map(generate_member_decl, self.params))
-            header_lines.append(template_scoped_decl.substitute(
-                None,
-                name=self.name,
-                param_list=param_list,
-                member_list=member_list))
-        else:
-            header_lines.append("CORE_EXPORT %s %s(%s);" % (
-                self.return_type, self.name, param_list))
-
-    def generate_cpp(self, cpp_lines):
-        if self.is_scoped():
-            self.generate_scoped_cpp(cpp_lines)
-        else:
-            self.generate_unscoped_cpp(cpp_lines)
-
-    def generate_scoped_cpp(self, cpp_lines):
-        will_body_lines = map(self.generate_ref_ptr, self.params)
-        will_body_lines += [self.generate_scoped_agent_call("will", agent) for agent in self.agents]
-        did_body_lines = map(self.generate_ref_ptr, self.params)
-        did_body_lines += [self.generate_scoped_agent_call("did", agent) for agent in self.agents]
-        member_init = ",\n".join(map(generate_member_init, self.params))
-
-        cpp_lines.append(template_scoped_impl.substitute(
-            None,
-            name=self.name,
-            params=", ".join(map(Parameter.to_str_class_and_name, self.params)),
-            init_list=":\n" + member_init if len(member_init) > 0 else "",
-            will_body_lines="".join(will_body_lines),
-            did_body_lines="".join(did_body_lines),
-            first_param_name=self.params[0].name))
-
-    def generate_scoped_agent_call(self, name, agent):
-        agent_class, agent_getter = agent_getter_signature(agent)
-        return template_agent_call.substitute(
-            None,
-            name=name,
-            agent_class=agent_class,
-            agent_getter=agent_getter,
-            maybe_return="",
-            params_agent="*this")
-
-    def generate_unscoped_cpp(self, cpp_lines):
-        default_return = "return;"
-        maybe_default_return = ""
-        if self.returns_value:
-            default_return = "return %s;" % self.default_return_value
-            maybe_default_return = "\n    " + default_return
-
-        body_lines = map(self.generate_ref_ptr, self.params)
-        body_lines += map(self.generate_agent_call, self.agents)
-
-        cpp_lines.append(template_impl.substitute(
-            None,
-            name=self.name,
-            return_type=self.return_type,
-            params=", ".join(map(Parameter.to_str_class_and_name, self.params)),
-            default_return=default_return,
-            maybe_default_return=maybe_default_return,
-            impl_lines="".join(body_lines),
-            first_param_name=self.params[0].name))
-
-    def generate_agent_call(self, agent):
-        agent_class, agent_getter = agent_getter_signature(agent)
-
-        maybe_return = ""
-        if self.returns_value:
-            maybe_return = "return "
-
-        return template_agent_call.substitute(
-            None,
-            name=self.name,
-            agent_class=agent_class,
-            agent_getter=agent_getter,
-            maybe_return=maybe_return,
-            params_agent=", ".join(map(Parameter.to_str_value, self.params_agent)))
-
-    def generate_ref_ptr(self, param):
-        if param.is_prp:
-            return "\n    RefPtr<%s> %s = %s;" % (param.inner_type, param.value, param.name)
-        else:
-            return ""
-
-class Parameter:
-    def __init__(self, source):
-        self.options = []
-        match, source = match_and_consume("\[(\w*)\]", source)
-        if match:
-            self.options.append(match.group(1))
-
-        parts = map(str.strip, source.split("="))
-        if len(parts) == 1:
-            self.default_value = None
-        else:
-            self.default_value = parts[1]
-
-        param_decl = parts[0]
-
-        if re.match("(const|unsigned long) ", param_decl):
-            min_type_tokens = 2
-        else:
-            min_type_tokens = 1
-
-        if len(param_decl.split(" ")) > min_type_tokens:
-            parts = param_decl.split(" ")
-            self.type = " ".join(parts[:-1])
-            self.name = parts[-1]
-        else:
-            self.type = param_decl
-            self.name = generate_param_name(self.type)
-
-        if re.match("PassRefPtr<", param_decl):
-            self.is_prp = True
-            self.value = self.name
-            self.name = "prp" + self.name[0].upper() + self.name[1:]
-            self.inner_type = re.match("PassRefPtr<(.+)>", param_decl).group(1)
-        else:
-            self.is_prp = False
-            self.value = self.name
-
-        self.is_ptr = self.type[-1] == "*"
-
-    def to_str_full(self):
-        if self.default_value is None:
-            return self.to_str_class_and_name()
-        return "%s %s = %s" % (self.type, self.name, self.default_value)
-
-    def to_str_class_and_name(self):
-        return "%s %s" % (self.type, self.name)
-
-    def to_str_class_and_value(self):
-        if self.default_value:
-            return "%s = %s" % (self.to_str_class(), self.default_value)
-        return self.to_str_class()
-
-    def to_str_class(self):
-        return self.type
-
-    def to_str_name(self):
-        return self.name
-
-    def to_str_value(self):
-        return self.value
-
-
-def generate_param_name(param_type):
-    base_name = re.match("(const |PassRefPtr<)?(\w*)", param_type).group(2)
-    return "param" + base_name
-
-
-def generate_member_decl(param):
-    if param.is_ptr and "char" not in param.type:
-        return "    Member<%s> %s;" % (param.type[:-1], param.name)
-    else:
-        return "    %s %s;" % (param.type, param.name)
-
-
-def generate_member_init(param):
-    return "    %s(%s)" % (param.name, param.name)
-
-
-def agent_class_name(agent):
-    if agent == "Performance":
-        return "PerformanceMonitor"
-    if agent == "TraceEvents":
-        return "InspectorTraceEvents"
-    return "Inspector%sAgent" % agent
-
-
-def agent_getter_signature(agent):
-    agent_class = agent_class_name(agent)
-    return agent_class, agent_class[0].lower() + agent_class[1:]
-
-
-def include_header(name):
-    return "#include \"%s.h\"" % name
-
-
-def include_inspector_header(name):
-    if name == "PerformanceMonitor":
-        return include_header("core/frame/" + name)
-    return include_header("core/inspector/" + name)
-
-
-def generate_instrumenting_agents(used_agents):
-    agents = list(used_agents)
-    agents.sort()
-
-    forward_list = []
-    accessor_list = []
-    member_list = []
-    init_list = []
-    trace_list = []
-    impl_list = []
-
-    for agent in agents:
-        class_name, getter_name = agent_getter_signature(agent)
-        member_name = "m_" + getter_name + "s"
-        has_member_name = "m_has" + class_name + "s"
-
-        forward_list.append("class %s;" % class_name)
-        accessor_list.append(template_instrumenting_agent_accessor.substitute(
-            None,
-            class_name=class_name,
-            getter_name=getter_name,
-            member_name=member_name,
-            has_member_name=has_member_name))
-        member_list.append("    HeapHashSet<Member<%s>> %s;" % (class_name, member_name))
-        member_list.append("    bool %s;" % has_member_name)
-        init_list.append("%s(false)" % has_member_name)
-        trace_list.append("visitor->trace(%s);" % member_name)
-        impl_list.append(template_instrumenting_agent_impl.substitute(
-            None,
-            class_name=class_name,
-            member_name=member_name,
-            has_member_name=has_member_name))
-
-    header_lines = template_instrumenting_agents_h.substitute(
-        None,
-        forward_list="\n".join(forward_list),
-        accessor_list="\n".join(accessor_list),
-        member_list="\n".join(member_list))
-
-    cpp_lines = template_instrumenting_agents_cpp.substitute(
-        None,
-        init_list="\n    , ".join(init_list),
-        impl_list="".join(impl_list),
-        trace_list="\n    ".join(trace_list))
-
-    return header_lines, cpp_lines
-
-
-def generate(input_path, output_dir):
-    fin = open(input_path, "r")
-    files = load_model_from_idl(fin.read())
-    fin.close()
-
-    cpp_includes = []
-    cpp_lines = []
-    used_agents = set()
-    for f in files:
-        cpp_includes.append(include_header(f.header_name))
-
-        fout = open(output_dir + "/" + f.header_name + ".h", "w")
-        fout.write(f.generate(cpp_lines, used_agents))
-        fout.close()
-
-    for agent in used_agents:
-        cpp_includes.append(include_inspector_header(agent_class_name(agent)))
-    cpp_includes.append(include_header("InstrumentingAgents"))
-    cpp_includes.append(include_header("core/CoreExport"))
-    cpp_includes.sort()
-
-    instrumenting_agents_header, instrumenting_agents_cpp = generate_instrumenting_agents(used_agents)
-
-    fout = open(output_dir + "/" + "InstrumentingAgents.h", "w")
-    fout.write(instrumenting_agents_header)
-    fout.close()
-
-    fout = open(output_dir + "/InspectorInstrumentationImpl.cpp", "w")
-    fout.write(template_cpp.substitute(None,
-                                       includes="\n".join(cpp_includes),
-                                       extra_definitions=instrumenting_agents_cpp,
-                                       methods="\n".join(cpp_lines)))
-    fout.close()
-
-
-cmdline_parser = optparse.OptionParser()
-cmdline_parser.add_option("--output_dir")
-
-try:
-    arg_options, arg_values = cmdline_parser.parse_args()
-    if (len(arg_values) != 1):
-        raise Exception("Exactly one plain argument expected (found %s)" % len(arg_values))
-    input_path = arg_values[0]
-    output_dirpath = arg_options.output_dir
-    if not output_dirpath:
-        raise Exception("Output directory must be specified")
-except Exception:
-    # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
-    exc = sys.exc_info()[1]
-    sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
-    sys.stderr.write("Usage: <script> --output_dir <output_dir> InspectorInstrumentation.idl\n")
-    exit(1)
-
-generate(input_path, output_dirpath)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
index a16579e..607b3a7 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
@@ -79,10 +79,12 @@
       m_task(task),
       m_recurring(step) {
   if (m_recurring) {
-    TRACE_EVENT_FLOW_STEP0("devtools.timeline.async", "AsyncTask", task,
+    TRACE_EVENT_FLOW_STEP0("devtools.timeline.async", "AsyncTask",
+                           TRACE_ID_LOCAL(reinterpret_cast<uintptr_t>(task)),
                            step ? step : "");
   } else {
-    TRACE_EVENT_FLOW_END0("devtools.timeline.async", "AsyncTask", task);
+    TRACE_EVENT_FLOW_END0("devtools.timeline.async", "AsyncTask",
+                          TRACE_ID_LOCAL(reinterpret_cast<uintptr_t>(task)));
   }
   if (m_debugger)
     m_debugger->asyncTaskStarted(m_task);
@@ -99,8 +101,9 @@
 void asyncTaskScheduled(ExecutionContext* context,
                         const String& name,
                         void* task) {
-  TRACE_EVENT_FLOW_BEGIN1("devtools.timeline.async", "AsyncTask", task, "data",
-                          InspectorAsyncTask::data(name));
+  TRACE_EVENT_FLOW_BEGIN1("devtools.timeline.async", "AsyncTask",
+                          TRACE_ID_LOCAL(reinterpret_cast<uintptr_t>(task)),
+                          "data", InspectorAsyncTask::data(name));
   if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
     debugger->asyncTaskScheduled(name, task, true);
 }
@@ -115,7 +118,8 @@
 void asyncTaskCanceled(ExecutionContext* context, void* task) {
   if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
     debugger->asyncTaskCanceled(task);
-  TRACE_EVENT_FLOW_END0("devtools.timeline.async", "AsyncTask", task);
+  TRACE_EVENT_FLOW_END0("devtools.timeline.async", "AsyncTask",
+                        TRACE_ID_LOCAL(reinterpret_cast<uintptr_t>(task)));
 }
 
 void asyncTaskCanceledBreakable(ExecutionContext* context,
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
index 01d1470..978f3ba 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -45,6 +45,14 @@
 
 namespace blink {
 
+namespace {
+
+void* asyncId(unsigned long identifier) {
+  return reinterpret_cast<void*>((identifier << 1) | 1);
+}
+
+}  //  namespace
+
 String toHexString(const void* p) {
   return String::format("0x%" PRIx64,
                         static_cast<uint64_t>(reinterpret_cast<uintptr_t>(p)));
@@ -92,6 +100,8 @@
   TRACE_EVENT_INSTANT1(
       "devtools.timeline", "ResourceSendRequest", TRACE_EVENT_SCOPE_THREAD,
       "data", InspectorSendRequestEvent::data(identifier, frame, request));
+  probe::asyncTaskScheduled(frame->document(), "SendRequest",
+                            asyncId(identifier));
 }
 
 void InspectorTraceEvents::didReceiveResourceResponse(
@@ -103,6 +113,8 @@
   TRACE_EVENT_INSTANT1(
       "devtools.timeline", "ResourceReceiveResponse", TRACE_EVENT_SCOPE_THREAD,
       "data", InspectorReceiveResponseEvent::data(identifier, frame, response));
+  probe::AsyncTask asyncTask(frame->document(), asyncId(identifier),
+                             "response");
 }
 
 void InspectorTraceEvents::didReceiveData(LocalFrame* frame,
@@ -113,6 +125,7 @@
       "devtools.timeline", "ResourceReceivedData", TRACE_EVENT_SCOPE_THREAD,
       "data",
       InspectorReceiveDataEvent::data(identifier, frame, encodedDataLength));
+  probe::AsyncTask asyncTask(frame->document(), asyncId(identifier), "data");
 }
 
 void InspectorTraceEvents::didFinishLoading(LocalFrame* frame,
@@ -124,6 +137,7 @@
       "devtools.timeline", "ResourceFinish", TRACE_EVENT_SCOPE_THREAD, "data",
       InspectorResourceFinishEvent::data(identifier, finishTime, false,
                                          encodedDataLength, decodedBodyLength));
+  probe::AsyncTask asyncTask(frame->document(), asyncId(identifier));
 }
 
 void InspectorTraceEvents::didFailLoading(unsigned long identifier,
diff --git a/third_party/WebKit/Source/core/inspector/InstrumentingAgents_h.template b/third_party/WebKit/Source/core/inspector/InstrumentingAgents_h.template
new file mode 100644
index 0000000..1a90db1
--- /dev/null
+++ b/third_party/WebKit/Source/core/inspector/InstrumentingAgents_h.template
@@ -0,0 +1,46 @@
+// This file is generated from {{input_file}}
+
+// Copyright 2017 The Chromium 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 InstrumentingAgents_h
+#define InstrumentingAgents_h
+
+#include "core/CoreExport.h"
+#include "platform/heap/HeapAllocator.h"
+
+namespace blink {
+
+{% for agent in agents %}
+class {{ agent | agent_name_to_class }};
+{% endfor %}
+
+class CORE_EXPORT InstrumentingAgents : public GarbageCollected<InstrumentingAgents> {
+  WTF_MAKE_NONCOPYABLE(InstrumentingAgents);
+
+ public:
+  InstrumentingAgents();
+  DECLARE_TRACE();
+
+{% for agent in agents %}
+{% set class_name = agent | agent_name_to_class %}
+{% set getter_name = class_name | to_lower_case %}
+  bool has{{class_name}}s() const { return m_has{{class_name}}s; }
+  const HeapHashSet<Member<{{class_name}}>>& {{getter_name}}s() const { return m_{{getter_name}}s; }
+  void add{{class_name}}({{class_name}}* agent);
+  void remove{{class_name}}({{class_name}}* agent);
+
+{% endfor %}
+ private:
+{% for agent in agents %}
+{% set class_name = agent | agent_name_to_class %}
+{% set getter_name = class_name | to_lower_case %}
+  HeapHashSet<Member<{{class_name}}>> m_{{getter_name}}s;
+  bool m_has{{class_name}}s = false;
+{% endfor %}
+};
+
+} // namespace blink
+
+#endif // !defined(InstrumentingAgents_h)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl b/third_party/WebKit/Source/core/inspector/InstrumentingProbes.idl
similarity index 100%
rename from third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl
rename to third_party/WebKit/Source/core/inspector/InstrumentingProbes.idl
diff --git a/third_party/WebKit/Source/core/inspector/InstrumentingProbesCodeGenerator.py b/third_party/WebKit/Source/core/inspector/InstrumentingProbesCodeGenerator.py
new file mode 100644
index 0000000..d334bf6
--- /dev/null
+++ b/third_party/WebKit/Source/core/inspector/InstrumentingProbesCodeGenerator.py
@@ -0,0 +1,233 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import optparse
+import os.path
+import re
+import sys
+
+# Path handling for libraries and templates
+# Paths have to be normalized because Jinja uses the exact template path to
+# determine the hash used in the cache filename, and we need a pre-caching step
+# to be concurrency-safe. Use absolute path because __file__ is absolute if
+# module is imported, and relative if executed directly.
+# If paths differ between pre-caching and individual file compilation, the cache
+# is regenerated, which causes a race condition and breaks concurrent build,
+# since some compile processes will try to read the partially written cache.
+module_path, module_filename = os.path.split(os.path.realpath(__file__))
+templates_dir = module_path
+third_party_dir = os.path.normpath(os.path.join(
+    module_path, os.pardir, os.pardir, os.pardir, os.pardir))
+# jinja2 is in chromium's third_party directory.
+# Insert at 1 so at front to override system libraries, and
+# after path[0] == invoking script dir
+sys.path.insert(1, third_party_dir)
+import jinja2
+
+
+def to_lower_case(name):
+    return name[:1].lower() + name[1:]
+
+
+def agent_name_to_class(agent_name):
+    if agent_name == "Performance":
+        return "PerformanceMonitor"
+    elif agent_name == "TraceEvents":
+        return "InspectorTraceEvents"
+    else:
+        return "Inspector%sAgent" % agent_name
+
+
+def initialize_jinja_env(cache_dir):
+    jinja_env = jinja2.Environment(
+        loader=jinja2.FileSystemLoader(templates_dir),
+        # Bytecode cache is not concurrency-safe unless pre-cached:
+        # if pre-cached this is read-only, but writing creates a race condition.
+        bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir),
+        keep_trailing_newline=True,  # newline-terminate generated files
+        lstrip_blocks=True,  # so can indent control flow tags
+        trim_blocks=True)
+    jinja_env.filters.update({
+        "to_lower_case": to_lower_case,
+        "agent_name_to_class": agent_name_to_class})
+    jinja_env.add_extension('jinja2.ext.loopcontrols')
+    return jinja_env
+
+
+def match_and_consume(pattern, source):
+    match = re.match(pattern, source)
+    if match:
+        return match, source[len(match.group(0)):].strip()
+    return None, source
+
+
+def load_model_from_idl(source):
+    source = re.sub(r"//.*", "", source)  # Remove line comments
+    source = re.sub(r"/\*(.|\n)*?\*/", "", source, re.MULTILINE)  # Remove block comments
+    source = re.sub(r"\]\s*?\n\s*", "] ", source)  # Merge the method annotation with the next line
+    source = source.strip()
+    model = []
+    while len(source):
+        match, source = match_and_consume(r"interface\s(\w*)\s?\{([^\{]*)\}", source)
+        if not match:
+            sys.stderr.write("Cannot parse %s\n" % source[:100])
+            sys.exit(1)
+        model.append(File(match.group(1), match.group(2)))
+    return model
+
+
+class File(object):
+    def __init__(self, name, source):
+        self.name = name
+        self.header_name = self.name + "Inl"
+        self.includes = [include_inspector_header("InspectorInstrumentation")]
+        self.forward_declarations = []
+        self.declarations = []
+        for line in map(str.strip, source.split("\n")):
+            line = re.sub(r"\s{2,}", " ", line).strip()  # Collapse whitespace
+            if len(line) == 0:
+                continue
+            if line[0] == "#":
+                self.includes.append(line)
+            elif line.startswith("class "):
+                self.forward_declarations.append(line)
+            else:
+                self.declarations.append(Method(line))
+        self.includes.sort()
+        self.forward_declarations.sort()
+
+
+def include_header(name):
+    return "#include \"%s.h\"" % name
+
+
+def include_inspector_header(name):
+    if name == "PerformanceMonitor":
+        return include_header("core/frame/" + name)
+    return include_header("core/inspector/" + name)
+
+
+class Method(object):
+    def __init__(self, source):
+        match = re.match(r"(\[[\w|,|=|\s]*\])?\s?(\w*\*?) (\w*)\((.*)\)\s?;", source)
+        if not match:
+            sys.stderr.write("Cannot parse %s\n" % source)
+            sys.exit(1)
+
+        self.options = []
+        if match.group(1):
+            options_str = re.sub(r"\s", "", match.group(1)[1:-1])
+            if len(options_str) != 0:
+                self.options = options_str.split(",")
+
+        self.return_type = match.group(2)
+        self.name = match.group(3)
+        self.is_scoped = self.return_type == ""
+
+        # Splitting parameters by a comma, assuming that attribute lists contain no more than one attribute.
+        self.params = map(Parameter, map(str.strip, match.group(4).split(",")))
+
+        self.returns_value = self.return_type != "" and self.return_type != "void"
+        if self.return_type == "bool":
+            self.default_return_value = "false"
+        elif self.returns_value:
+            sys.stderr.write("Can only return bool: %s\n" % self.name)
+            sys.exit(1)
+
+        self.agents = [option for option in self.options if "=" not in option]
+
+        if self.returns_value and len(self.agents) > 1:
+            sys.stderr.write("Can only return value from a single agent: %s\n" % self.name)
+            sys.exit(1)
+
+
+class Parameter(object):
+    def __init__(self, source):
+        self.options = []
+        match, source = match_and_consume(r"\[(\w*)\]", source)
+        if match:
+            self.options.append(match.group(1))
+
+        parts = map(str.strip, source.split("="))
+        self.default_value = parts[1] if len(parts) != 1 else None
+
+        param_decl = parts[0]
+        min_type_tokens = 2 if re.match("(const|unsigned long) ", param_decl) else 1
+
+        if len(param_decl.split(" ")) > min_type_tokens:
+            parts = param_decl.split(" ")
+            self.type = " ".join(parts[:-1])
+            self.name = parts[-1]
+        else:
+            self.type = param_decl
+            self.name = build_param_name(self.type)
+
+        self.value = self.name
+        self.is_prp = re.match(r"PassRefPtr<", param_decl) is not None
+        if self.is_prp:
+            self.name = "prp" + self.name[0].upper() + self.name[1:]
+            self.inner_type = re.match(r"PassRefPtr<(.+)>", param_decl).group(1)
+
+        if self.type[-1] == "*" and "char" not in self.type:
+            self.member_type = "Member<%s>" % self.type[:-1]
+        else:
+            self.member_type = self.type
+
+
+def build_param_name(param_type):
+    base_name = re.match(r"(const |PassRefPtr<)?(\w*)", param_type).group(2)
+    return "param" + base_name
+
+
+cmdline_parser = optparse.OptionParser()
+cmdline_parser.add_option("--output_dir")
+cmdline_parser.add_option("--template_dir")
+
+try:
+    arg_options, arg_values = cmdline_parser.parse_args()
+    if len(arg_values) != 1:
+        raise Exception("Exactly one plain argument expected (found %s)" % len(arg_values))
+    input_path = arg_values[0]
+    output_dirpath = arg_options.output_dir
+    if not output_dirpath:
+        raise Exception("Output directory must be specified")
+except Exception:
+    # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
+    exc = sys.exc_info()[1]
+    sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
+    sys.stderr.write("Usage: <script> --output_dir <output_dir> InstrumentingProbes.idl\n")
+    exit(1)
+
+jinja_env = initialize_jinja_env(output_dirpath)
+fin = open(input_path, "r")
+files = load_model_from_idl(fin.read())
+fin.close()
+all_agents = set()
+
+for f in files:
+    for declaration in f.declarations:
+        for agent in declaration.agents:
+            all_agents.add(agent)
+
+template_context = {
+    "files": files,
+    "agents": all_agents,
+    "input_file": os.path.basename(input_path)
+}
+cpp_template = jinja_env.get_template("/InstrumentingProbesImpl_cpp.template")
+cpp_file = open(output_dirpath + "/InstrumentingProbesImpl.cpp", "w")
+cpp_file.write(cpp_template.render(template_context))
+cpp_file.close()
+
+agents_h_template = jinja_env.get_template("/InstrumentingAgents_h.template")
+agents_h_file = open(output_dirpath + "/InstrumentingAgents.h", "w")
+agents_h_file.write(agents_h_template.render(template_context))
+agents_h_file.close()
+
+for f in files:
+    template_context["file"] = f
+    h_template = jinja_env.get_template("/InstrumentingProbesImpl_h.template")
+    h_file = open(output_dirpath + "/" + f.name + "Inl.h", "w")
+    h_file.write(h_template.render(template_context))
+    h_file.close()
diff --git a/third_party/WebKit/Source/core/inspector/InstrumentingProbesImpl_cpp.template b/third_party/WebKit/Source/core/inspector/InstrumentingProbesImpl_cpp.template
new file mode 100644
index 0000000..047dad1
--- /dev/null
+++ b/third_party/WebKit/Source/core/inspector/InstrumentingProbesImpl_cpp.template
@@ -0,0 +1,114 @@
+// This file is generated from {{input_file}}
+
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+{% for file in files %}
+#include "{{file.header_name}}.h"
+{% endfor %}
+#include "InstrumentingAgents.h"
+#include "core/CoreExport.h"
+{% for agent in agents %}
+{% set class_name = agent | agent_name_to_class %}
+{% if class_name == "PerformanceMonitor" %}
+#include "core/frame/PerformanceMonitor.h"
+{% else %}
+#include "core/inspector/{{class_name}}.h"
+{% endif %}
+{% endfor %}
+
+namespace blink {
+
+InstrumentingAgents::InstrumentingAgents() {}
+
+{% for agent in agents %}
+{% set class_name = agent | agent_name_to_class %}
+{% set getter_name = class_name | to_lower_case %}
+void InstrumentingAgents::add{{class_name}}({{class_name}}* agent) {
+  m_{{getter_name}}s.insert(agent);
+  m_has{{class_name}}s = true;
+}
+
+void InstrumentingAgents::remove{{class_name}}({{class_name}}* agent) {
+  m_{{getter_name}}s.erase(agent);
+  m_has{{class_name}}s = !m_{{getter_name}}s.isEmpty();
+}
+
+{% endfor -%}
+
+DEFINE_TRACE(InstrumentingAgents)
+{
+{% for agent in agents %}
+{% set getter_name = agent | agent_name_to_class | to_lower_case %}
+    visitor->trace(m_{{getter_name}}s);
+{% endfor %}
+}
+
+namespace probe {
+{% macro params_list(probe) -%}
+{%- for param in probe.params %}
+{{param.type}} {{param.name}}
+{%- if not loop.last %}, {% endif -%}
+{%- endfor -%}
+{%- endmacro %}
+
+{% macro probe_body(probe, common_name) %}
+{% set agent_probe_name = common_name or probe.name %}
+  InstrumentingAgents* agents = instrumentingAgentsFor({{probe.params[0].name}});
+  if (!agents)
+    return {{probe.default_return_value}};
+{% for param in probe.params %}
+{% if param.is_prp %}
+  RefPtr<{{param.inner_type}}> {{param.value}} = {{param.name}};
+{% endif %}
+{% endfor %}
+{% set maybe_return = "return " if probe.returns_value else "" %}
+{% for agent in probe.agents %}
+{% set class_name = agent | agent_name_to_class %}
+  if (agents->has{{class_name}}s()) {
+    for ({{class_name}}* agent : agents->{{ class_name | to_lower_case }}s())
+      {{maybe_return}}agent->{{agent_probe_name}}({{caller()}});
+  }
+{% endfor %}
+{% if probe.default_return_value %}
+  return {{probe.default_return_value}};
+{% endif %}
+{% endmacro -%}
+
+{% for file in files %}
+{% for probe in file.declarations %}
+{% if probe.is_scoped %}
+{{probe.name}}::{{probe.name}}({{ params_list(probe) }}) :
+{% for param in probe.params %}
+    {{param.name}}({{param.name}})
+{%- if not loop.last %},
+{% endif %}
+{% endfor %} {
+{% call probe_body(probe, "will") %}*this{% endcall %}
+}
+
+{{probe.name}}::~{{probe.name}}() {
+{% call probe_body(probe, "did") %}*this{% endcall %}
+}
+
+{% else -%}
+
+CORE_EXPORT {{probe.return_type}} {{probe.name}}({{ params_list(probe) }}) {
+{% call probe_body(probe, "") %}
+{%- for param in probe.params %}
+{%- if not loop.first or "Keep" in param.options -%}
+{{param.value}}
+{%- if not loop.last %}, {% endif -%}
+{%- endif -%}
+{%- endfor %}
+{%- endcall %}
+}
+
+{% endif %}
+{% endfor %}
+{% endfor %}
+
+} // namespace probe
+} // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/InstrumentingProbesImpl_h.template b/third_party/WebKit/Source/core/inspector/InstrumentingProbesImpl_h.template
new file mode 100644
index 0000000..d7e2cd0
--- /dev/null
+++ b/third_party/WebKit/Source/core/inspector/InstrumentingProbesImpl_h.template
@@ -0,0 +1,55 @@
+// This file is generated from {{input_file}}
+
+// Copyright 2017 The Chromium 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 {{file.header_name}}_h
+#define {{file.header_name}}_h
+
+{% for include in file.includes %}
+{{include}}
+{% endfor %}
+
+namespace blink {
+
+{% for forward_decl in file.forward_declarations %}
+{{forward_decl}}
+{% endfor %}
+
+namespace probe {
+{% for probe in file.declarations %}
+
+{%- macro params_decl(probe) %}
+{%- for param in probe.params %}
+{{ param.type }}
+{%- if param.default_value %} = {{ param.default_value }}
+{%- endif %}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+{% if probe.is_scoped %}
+
+
+class CORE_EXPORT {{probe.name}} : public ProbeBase {
+  STACK_ALLOCATED()
+ public:
+  explicit {{probe.name}}({{ params_decl(probe) }});
+  ~{{probe.name}}();
+{% for param in probe.params %}
+  {{param.member_type}} {{param.name}};
+{% endfor %}
+};
+{%- else %}
+
+CORE_EXPORT {{probe.return_type}} {{probe.name}}({{ params_decl(probe) }});
+
+{%- endif %}
+{%- endfor %}
+
+
+} // namespace probe
+} // namespace blink
+
+#endif // !defined({{file.header_name}}_h)
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
index 184bb08..53fb54ed 100644
--- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
+++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
@@ -326,8 +326,8 @@
   LocalFrame* frame = WeakIdentifierMap<LocalFrame>::lookup(contextGroupId);
   if (!frame)
     return;
-  if (frame->host())
-    frame->host()->consoleMessageStorage().clear();
+  if (frame->page())
+    frame->page()->consoleMessageStorage().clear();
 }
 
 v8::MaybeLocal<v8::Value> MainThreadDebugger::memoryInfo(
diff --git a/third_party/WebKit/Source/core/inspector/WorkerInspectorController.cpp b/third_party/WebKit/Source/core/inspector/WorkerInspectorController.cpp
index b5147ea3..aa6fa92 100644
--- a/third_party/WebKit/Source/core/inspector/WorkerInspectorController.cpp
+++ b/third_party/WebKit/Source/core/inspector/WorkerInspectorController.cpp
@@ -33,6 +33,7 @@
 #include "core/InstrumentingAgents.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/inspector/InspectorLogAgent.h"
+#include "core/inspector/InspectorTraceEvents.h"
 #include "core/inspector/WorkerThreadDebugger.h"
 #include "core/inspector/protocol/Protocol.h"
 #include "core/workers/WorkerBackingThread.h"
@@ -54,7 +55,9 @@
     WorkerThreadDebugger* debugger)
     : m_debugger(debugger),
       m_thread(thread),
-      m_instrumentingAgents(new InstrumentingAgents()) {}
+      m_instrumentingAgents(new InstrumentingAgents()) {
+  m_instrumentingAgents->addInspectorTraceEvents(new InspectorTraceEvents());
+}
 
 WorkerInspectorController::~WorkerInspectorController() {
   DCHECK(!m_thread);
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
index dc258c45..c5cc0e4 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
@@ -553,7 +553,7 @@
          isTableCaption() || isFieldset() || isWritingModeRoot() ||
          isDocumentElement() || isColumnSpanAll() || isGridItem() ||
          style()->containsPaint() || style()->containsLayout() ||
-         isSVGForeignObject() || style()->display() == EDisplay::FlowRoot;
+         isSVGForeignObject() || style()->display() == EDisplay::kFlowRoot;
 }
 
 static inline bool changeInAvailableLogicalHeightAffectsChild(
@@ -2013,12 +2013,12 @@
   // anonymous logic ?
   EDisplay newDisplay;
   LayoutBlock* newBox = nullptr;
-  if (display == EDisplay::Flex || display == EDisplay::InlineFlex) {
+  if (display == EDisplay::kFlex || display == EDisplay::kInlineFlex) {
     newBox = LayoutFlexibleBox::createAnonymous(&parent->document());
-    newDisplay = EDisplay::Flex;
+    newDisplay = EDisplay::kFlex;
   } else {
     newBox = LayoutBlockFlow::createAnonymous(&parent->document());
-    newDisplay = EDisplay::Block;
+    newDisplay = EDisplay::kBlock;
   }
 
   RefPtr<ComputedStyle> newStyle =
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.h b/third_party/WebKit/Source/core/layout/LayoutBlock.h
index 7b4fdaf..49de48e4 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlock.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBlock.h
@@ -221,8 +221,8 @@
 
   static LayoutBlock* createAnonymousWithParentAndDisplay(
       const LayoutObject*,
-      EDisplay = EDisplay::Block);
-  LayoutBlock* createAnonymousBlock(EDisplay display = EDisplay::Block) const {
+      EDisplay = EDisplay::kBlock);
+  LayoutBlock* createAnonymousBlock(EDisplay display = EDisplay::kBlock) const {
     return createAnonymousWithParentAndDisplay(this, display);
   }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
index 06f862d..f5db6a1 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
@@ -30,9 +30,11 @@
 
 #include "core/layout/LayoutBlockFlow.h"
 
+#include <memory>
 #include "core/editing/Editor.h"
 #include "core/frame/FrameView.h"
 #include "core/frame/LocalFrame.h"
+#include "core/frame/UseCounter.h"
 #include "core/html/HTMLDialogElement.h"
 #include "core/layout/HitTestLocation.h"
 #include "core/layout/LayoutAnalyzer.h"
@@ -52,7 +54,6 @@
 #include "core/paint/PaintLayer.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "wtf/PtrUtil.h"
-#include <memory>
 
 namespace blink {
 
@@ -4218,6 +4219,7 @@
                                                           styleRef());
     case PagedFlowThread:
       // Paged overflow is currently done using the multicol implementation.
+      UseCounter::count(document(), UseCounter::CSSOverflowPaged);
       return LayoutPagedFlowThread::createAnonymous(document(), styleRef());
     default:
       ASSERT_NOT_REACHED();
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index 2d85a29..da3c689e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -3212,7 +3212,7 @@
   // non-anonymous.
   if (containingBlock->isAnonymous()) {
     EDisplay display = containingBlock->styleRef().display();
-    return display == EDisplay::Block || display == EDisplay::InlineBlock;
+    return display == EDisplay::kBlock || display == EDisplay::kInlineBlock;
   }
 
   // For quirks mode, we skip most auto-height containing blocks when computing
@@ -4757,7 +4757,7 @@
   return shouldBeConsideredAsReplaced() || hasOverflowClip() || isHR() ||
          isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated() ||
          style()->containsPaint() || style()->containsLayout() ||
-         style()->display() == EDisplay::FlowRoot;
+         style()->display() == EDisplay::kFlowRoot;
 }
 
 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 37084cfc..aca950df 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -1088,7 +1088,7 @@
   // Returns the intersection of all overflow clips which apply.
   virtual LayoutRect overflowClipRect(
       const LayoutPoint& location,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const;
+      OverlayScrollbarClipBehavior = IgnorePlatformOverlayScrollbarSize) const;
   LayoutRect clipRect(const LayoutPoint& location) const;
 
   // Returns the combination of overflow clip, contain: paint clip and CSS clip
@@ -1445,7 +1445,7 @@
 
   void excludeScrollbars(
       LayoutRect&,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const;
+      OverlayScrollbarClipBehavior = IgnorePlatformOverlayScrollbarSize) const;
 
   LayoutUnit containingBlockLogicalWidthForPositioned(
       const LayoutBoxModelObject* containingBlock,
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
index 9d85fac9..1076826b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
@@ -316,10 +316,10 @@
       (parent() != containingBlock()) &&
       (styleRef().position() == oldStyle->position()) &&
       (styleRef().originalDisplay() != oldStyle->originalDisplay()) &&
-      ((styleRef().originalDisplay() == EDisplay::Block) ||
-       (styleRef().originalDisplay() == EDisplay::InlineBlock)) &&
-      ((oldStyle->originalDisplay() == EDisplay::Block) ||
-       (oldStyle->originalDisplay() == EDisplay::InlineBlock)))
+      ((styleRef().originalDisplay() == EDisplay::kBlock) ||
+       (styleRef().originalDisplay() == EDisplay::kInlineBlock)) &&
+      ((oldStyle->originalDisplay() == EDisplay::kBlock) ||
+       (oldStyle->originalDisplay() == EDisplay::kInlineBlock)))
     parent()->setNeedsLayout(LayoutInvalidationReason::ChildChanged,
                              MarkContainerChain);
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp b/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp
index 754f64a..fc8de4fd 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp
@@ -103,7 +103,7 @@
   fullscreenStyle->setFontDescription(FontDescription());
   fullscreenStyle->font().update(nullptr);
 
-  fullscreenStyle->setDisplay(EDisplay::Flex);
+  fullscreenStyle->setDisplay(EDisplay::kFlex);
   fullscreenStyle->setJustifyContentPosition(ContentPositionCenter);
   // TODO (lajava): Since the FullScrenn layout object is anonymous, its Default
   // Alignment (align-items) value can't be used to resolve its children Self
diff --git a/third_party/WebKit/Source/core/layout/LayoutInline.cpp b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
index 4a8cf22..c6512dd 100644
--- a/third_party/WebKit/Source/core/layout/LayoutInline.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
@@ -347,7 +347,7 @@
     // them in a clone of this object.
     RefPtr<ComputedStyle> newStyle =
         ComputedStyle::createAnonymousStyleWithDisplay(
-            containingBlock()->styleRef(), EDisplay::Block);
+            containingBlock()->styleRef(), EDisplay::kBlock);
 
     // If inside an inline affected by in-flow positioning the block needs to be
     // affected by it too. Giving the block a layer like this allows it to
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
index 0bec7de..289011e3 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
@@ -56,7 +56,7 @@
   LayoutMultiColumnFlowThread* layoutObject = new LayoutMultiColumnFlowThread();
   layoutObject->setDocumentForAnonymous(&document);
   layoutObject->setStyle(ComputedStyle::createAnonymousStyleWithDisplay(
-      parentStyle, EDisplay::Block));
+      parentStyle, EDisplay::kBlock));
   return layoutObject;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnSet.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnSet.cpp
index 6194d7a..c467325 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnSet.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnSet.cpp
@@ -46,7 +46,7 @@
   LayoutMultiColumnSet* layoutObject = new LayoutMultiColumnSet(&flowThread);
   layoutObject->setDocumentForAnonymous(&document);
   layoutObject->setStyle(ComputedStyle::createAnonymousStyleWithDisplay(
-      parentStyle, EDisplay::Block));
+      parentStyle, EDisplay::kBlock));
   return layoutObject;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnSpannerPlaceholder.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnSpannerPlaceholder.cpp
index 4ea37764..805d8f8 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnSpannerPlaceholder.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnSpannerPlaceholder.cpp
@@ -29,7 +29,7 @@
   newSpanner->setDocumentForAnonymous(&document);
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(parentStyle,
-                                                     EDisplay::Block);
+                                                     EDisplay::kBlock);
   copyMarginProperties(*newStyle, layoutObjectInFlowThread.styleRef());
   newSpanner->setStyle(newStyle);
   return newSpanner;
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index fc01370..2f05826 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -174,43 +174,43 @@
   }
 
   switch (style.display()) {
-    case EDisplay::None:
-    case EDisplay::Contents:
+    case EDisplay::kNone:
+    case EDisplay::kContents:
       return nullptr;
-    case EDisplay::Inline:
+    case EDisplay::kInline:
       return new LayoutInline(element);
-    case EDisplay::Block:
-    case EDisplay::FlowRoot:
-    case EDisplay::InlineBlock:
+    case EDisplay::kBlock:
+    case EDisplay::kFlowRoot:
+    case EDisplay::kInlineBlock:
       if (RuntimeEnabledFeatures::layoutNGEnabled())
         return new LayoutNGBlockFlow(element);
       return new LayoutBlockFlow(element);
-    case EDisplay::ListItem:
+    case EDisplay::kListItem:
       return new LayoutListItem(element);
-    case EDisplay::Table:
-    case EDisplay::InlineTable:
+    case EDisplay::kTable:
+    case EDisplay::kInlineTable:
       return new LayoutTable(element);
-    case EDisplay::TableRowGroup:
-    case EDisplay::TableHeaderGroup:
-    case EDisplay::TableFooterGroup:
+    case EDisplay::kTableRowGroup:
+    case EDisplay::kTableHeaderGroup:
+    case EDisplay::kTableFooterGroup:
       return new LayoutTableSection(element);
-    case EDisplay::TableRow:
+    case EDisplay::kTableRow:
       return new LayoutTableRow(element);
-    case EDisplay::TableColumnGroup:
-    case EDisplay::TableColumn:
+    case EDisplay::kTableColumnGroup:
+    case EDisplay::kTableColumn:
       return new LayoutTableCol(element);
-    case EDisplay::TableCell:
+    case EDisplay::kTableCell:
       return new LayoutTableCell(element);
-    case EDisplay::TableCaption:
+    case EDisplay::kTableCaption:
       return new LayoutTableCaption(element);
-    case EDisplay::WebkitBox:
-    case EDisplay::WebkitInlineBox:
+    case EDisplay::kWebkitBox:
+    case EDisplay::kWebkitInlineBox:
       return new LayoutDeprecatedFlexibleBox(*element);
-    case EDisplay::Flex:
-    case EDisplay::InlineFlex:
+    case EDisplay::kFlex:
+    case EDisplay::kInlineFlex:
       return new LayoutFlexibleBox(element);
-    case EDisplay::Grid:
-    case EDisplay::InlineGrid:
+    case EDisplay::kGrid:
+    case EDisplay::kInlineGrid:
       return new LayoutGrid(element);
   }
 
@@ -1925,6 +1925,12 @@
 
   if (oldStyle && oldStyle->styleType() == PseudoIdNone)
     applyPseudoStyleChanges(*oldStyle);
+
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && oldStyle &&
+      oldStyle->usedTransformStyle3D() != styleRef().usedTransformStyle3D()) {
+    // Change of transform-style may affect descendant transform property nodes.
+    setSubtreeNeedsPaintPropertyUpdate();
+  }
 }
 
 void LayoutObject::applyPseudoStyleChanges(const ComputedStyle& oldStyle) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 40dee15..f9a49f8 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -713,8 +713,9 @@
     // LayoutBlock having a BLOCK or BOX display. Other classes such as
     // LayoutTextFragment are not LayoutBlocks and will return false.
     // See https://bugs.webkit.org/show_bug.cgi?id=56709.
-    return isAnonymous() && (style()->display() == EDisplay::Block ||
-                             style()->display() == EDisplay::WebkitBox) &&
+    return isAnonymous() &&
+           (style()->display() == EDisplay::kBlock ||
+            style()->display() == EDisplay::kWebkitBox) &&
            style()->styleType() == PseudoIdNone && isLayoutBlock() &&
            !isListMarker() && !isLayoutFlowThread() &&
            !isLayoutMultiColumnSet() && !isLayoutFullScreen() &&
diff --git a/third_party/WebKit/Source/core/layout/LayoutPagedFlowThread.cpp b/third_party/WebKit/Source/core/layout/LayoutPagedFlowThread.cpp
index d707417..41bc1b5 100644
--- a/third_party/WebKit/Source/core/layout/LayoutPagedFlowThread.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutPagedFlowThread.cpp
@@ -14,7 +14,7 @@
   LayoutPagedFlowThread* pagedFlowThread = new LayoutPagedFlowThread();
   pagedFlowThread->setDocumentForAnonymous(&document);
   pagedFlowThread->setStyle(ComputedStyle::createAnonymousStyleWithDisplay(
-      parentStyle, EDisplay::Block));
+      parentStyle, EDisplay::kBlock));
   return pagedFlowThread;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutRubyRun.cpp b/third_party/WebKit/Source/core/layout/LayoutRubyRun.cpp
index c38bb42..af598c4 100644
--- a/third_party/WebKit/Source/core/layout/LayoutRubyRun.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutRubyRun.cpp
@@ -182,7 +182,7 @@
   LayoutRubyBase* layoutObject = LayoutRubyBase::createAnonymous(&document());
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(styleRef(),
-                                                     EDisplay::Block);
+                                                     EDisplay::kBlock);
   newStyle->setTextAlign(ETextAlign::kCenter);  // FIXME: use WEBKIT_CENTER?
   layoutObject->setStyle(std::move(newStyle));
   return layoutObject;
@@ -195,7 +195,7 @@
   rr->setDocumentForAnonymous(&parentRuby->document());
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(parentRuby->styleRef(),
-                                                     EDisplay::InlineBlock);
+                                                     EDisplay::kInlineBlock);
   rr->setStyle(std::move(newStyle));
   return rr;
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp b/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp
index 246e8de7..6670d07 100644
--- a/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp
@@ -228,9 +228,9 @@
           : PassRefPtr<ComputedStyle>(nullptr);
 
   bool needLayoutObject =
-      !destroy && partStyle && partStyle->display() != EDisplay::None;
+      !destroy && partStyle && partStyle->display() != EDisplay::kNone;
 
-  if (needLayoutObject && partStyle->display() != EDisplay::Block) {
+  if (needLayoutObject && partStyle->display() != EDisplay::kBlock) {
     // See if we are a button that should not be visible according to OS
     // settings.
     WebScrollbarButtonsPlacement buttonsPlacement = theme().buttonsPlacement();
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.cpp b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
index ea658b4..a60abf0d 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
@@ -120,9 +120,9 @@
   // Return true if 'object' can't exist in an anonymous table without being
   // wrapped in a table section box.
   EDisplay display = object->style()->display();
-  return display != EDisplay::TableCaption &&
-         display != EDisplay::TableColumnGroup &&
-         display != EDisplay::TableColumn;
+  return display != EDisplay::kTableCaption &&
+         display != EDisplay::kTableColumnGroup &&
+         display != EDisplay::kTableColumn;
 }
 
 void LayoutTable::addChild(LayoutObject* child, LayoutObject* beforeChild) {
@@ -135,7 +135,7 @@
     wrapInAnonymousSection = false;
   } else if (child->isTableSection()) {
     switch (child->style()->display()) {
-      case EDisplay::TableHeaderGroup:
+      case EDisplay::kTableHeaderGroup:
         resetSectionPointerIfNotBefore(m_head, beforeChild);
         if (!m_head) {
           m_head = toLayoutTableSection(child);
@@ -146,7 +146,7 @@
         }
         wrapInAnonymousSection = false;
         break;
-      case EDisplay::TableFooterGroup:
+      case EDisplay::kTableFooterGroup:
         resetSectionPointerIfNotBefore(m_foot, beforeChild);
         if (!m_foot) {
           m_foot = toLayoutTableSection(child);
@@ -154,7 +154,7 @@
           break;
         }
       // Fall through.
-      case EDisplay::TableRowGroup:
+      case EDisplay::kTableRowGroup:
         resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
         if (!m_firstBody)
           m_firstBody = toLayoutTableSection(child);
@@ -1081,11 +1081,11 @@
   for (LayoutObject* child = firstChild(); child; child = nextSibling) {
     nextSibling = child->nextSibling();
     switch (child->style()->display()) {
-      case EDisplay::TableColumn:
-      case EDisplay::TableColumnGroup:
+      case EDisplay::kTableColumn:
+      case EDisplay::kTableColumnGroup:
         m_hasColElements = true;
         break;
-      case EDisplay::TableHeaderGroup:
+      case EDisplay::kTableHeaderGroup:
         if (child->isTableSection()) {
           LayoutTableSection* section = toLayoutTableSection(child);
           if (!m_head)
@@ -1095,7 +1095,7 @@
           section->recalcCellsIfNeeded();
         }
         break;
-      case EDisplay::TableFooterGroup:
+      case EDisplay::kTableFooterGroup:
         if (child->isTableSection()) {
           LayoutTableSection* section = toLayoutTableSection(child);
           if (!m_foot)
@@ -1105,7 +1105,7 @@
           section->recalcCellsIfNeeded();
         }
         break;
-      case EDisplay::TableRowGroup:
+      case EDisplay::kTableRowGroup:
         if (child->isTableSection()) {
           LayoutTableSection* section = toLayoutTableSection(child);
           if (!m_firstBody)
@@ -1645,7 +1645,7 @@
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(
           parent->styleRef(),
-          parent->isLayoutInline() ? EDisplay::InlineTable : EDisplay::Table);
+          parent->isLayoutInline() ? EDisplay::kInlineTable : EDisplay::kTable);
   LayoutTable* newTable = new LayoutTable(nullptr);
   newTable->setDocumentForAnonymous(&parent->document());
   newTable->setStyle(std::move(newStyle));
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.h b/third_party/WebKit/Source/core/layout/LayoutTable.h
index 26b7755..d5bc8c92 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.h
@@ -508,7 +508,8 @@
 
   LayoutRect overflowClipRect(
       const LayoutPoint& location,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
 
   void addOverflowFromChildren() override;
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
index 4146da6..f10f6ce 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
@@ -479,7 +479,7 @@
 
 void LayoutTableCell::styleDidChange(StyleDifference diff,
                                      const ComputedStyle* oldStyle) {
-  DCHECK_EQ(style()->display(), EDisplay::TableCell);
+  DCHECK_EQ(style()->display(), EDisplay::kTableCell);
 
   LayoutBlockFlow::styleDidChange(diff, oldStyle);
   setHasBoxDecorationBackground(true);
@@ -1438,7 +1438,7 @@
       LayoutTableCell::createAnonymous(&parent->document());
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(parent->styleRef(),
-                                                     EDisplay::TableCell);
+                                                     EDisplay::kTableCell);
   newCell->setStyle(std::move(newStyle));
   return newCell;
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp b/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp
index ba923455..eefe5b6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp
@@ -43,8 +43,8 @@
 
 void LayoutTableCol::styleDidChange(StyleDifference diff,
                                     const ComputedStyle* oldStyle) {
-  DCHECK(style()->display() == EDisplay::TableColumn ||
-         style()->display() == EDisplay::TableColumnGroup);
+  DCHECK(style()->display() == EDisplay::kTableColumn ||
+         style()->display() == EDisplay::kTableColumnGroup);
 
   LayoutTableBoxComponent::styleDidChange(diff, oldStyle);
 
@@ -102,7 +102,7 @@
 bool LayoutTableCol::isChildAllowed(LayoutObject* child,
                                     const ComputedStyle& style) const {
   // We cannot use isTableColumn here as style() may return 0.
-  return child->isLayoutTableCol() && style.display() == EDisplay::TableColumn;
+  return child->isLayoutTableCol() && style.display() == EDisplay::kTableColumn;
 }
 
 bool LayoutTableCol::canHaveChildren() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCol.h b/third_party/WebKit/Source/core/layout/LayoutTableCol.h
index c8d0bd8..a6244de 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCol.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCol.h
@@ -69,10 +69,10 @@
 
   bool isTableColumnGroupWithColumnChildren() { return firstChild(); }
   bool isTableColumn() const {
-    return style()->display() == EDisplay::TableColumn;
+    return style()->display() == EDisplay::kTableColumn;
   }
   bool isTableColumnGroup() const {
-    return style()->display() == EDisplay::TableColumnGroup;
+    return style()->display() == EDisplay::kTableColumnGroup;
   }
 
   LayoutTableCol* enclosingColumnGroup() const;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
index ec17e6c..3184ad5 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
@@ -54,7 +54,7 @@
 
 void LayoutTableRow::styleDidChange(StyleDifference diff,
                                     const ComputedStyle* oldStyle) {
-  DCHECK_EQ(style()->display(), EDisplay::TableRow);
+  DCHECK_EQ(style()->display(), EDisplay::kTableRow);
 
   LayoutTableBoxComponent::styleDidChange(diff, oldStyle);
   propagateStyleToAnonymousChildren();
@@ -278,7 +278,7 @@
   LayoutTableRow* newRow = LayoutTableRow::createAnonymous(&parent->document());
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(parent->styleRef(),
-                                                     EDisplay::TableRow);
+                                                     EDisplay::kTableRow);
   newRow->setStyle(std::move(newStyle));
   return newRow;
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
index c272289..732fa1a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -115,9 +115,9 @@
 
 void LayoutTableSection::styleDidChange(StyleDifference diff,
                                         const ComputedStyle* oldStyle) {
-  DCHECK(style()->display() == EDisplay::TableFooterGroup ||
-         style()->display() == EDisplay::TableRowGroup ||
-         style()->display() == EDisplay::TableHeaderGroup);
+  DCHECK(style()->display() == EDisplay::kTableFooterGroup ||
+         style()->display() == EDisplay::kTableRowGroup ||
+         style()->display() == EDisplay::kTableHeaderGroup);
 
   LayoutTableBoxComponent::styleDidChange(diff, oldStyle);
   propagateStyleToAnonymousChildren();
@@ -1846,7 +1846,7 @@
     const LayoutObject* parent) {
   RefPtr<ComputedStyle> newStyle =
       ComputedStyle::createAnonymousStyleWithDisplay(parent->styleRef(),
-                                                     EDisplay::TableRowGroup);
+                                                     EDisplay::kTableRowGroup);
   LayoutTableSection* newSection = new LayoutTableSection(nullptr);
   newSection->setDocumentForAnonymous(&parent->document());
   newSection->setStyle(std::move(newStyle));
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextControlMultiLine.cpp b/third_party/WebKit/Source/core/layout/LayoutTextControlMultiLine.cpp
index 3a7bf40..ffd9b28 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextControlMultiLine.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextControlMultiLine.cpp
@@ -92,7 +92,7 @@
   RefPtr<ComputedStyle> textBlockStyle = ComputedStyle::create();
   textBlockStyle->inheritFrom(startStyle);
   adjustInnerEditorStyle(*textBlockStyle);
-  textBlockStyle->setDisplay(EDisplay::Block);
+  textBlockStyle->setDisplay(EDisplay::kBlock);
   textBlockStyle->setUnique();
 
   return textBlockStyle.release();
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextControlSingleLine.cpp b/third_party/WebKit/Source/core/layout/LayoutTextControlSingleLine.cpp
index f0ea860..4df23996 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextControlSingleLine.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextControlSingleLine.cpp
@@ -326,7 +326,7 @@
        logicalHeight.getFloatValue() > computedLineHeight))
     textBlockStyle->setLineHeight(ComputedStyle::initialLineHeight());
 
-  textBlockStyle->setDisplay(EDisplay::Block);
+  textBlockStyle->setDisplay(EDisplay::kBlock);
   textBlockStyle->setUnique();
 
   if (inputElement()->shouldRevealPassword())
@@ -337,7 +337,7 @@
   textBlockStyle->setOverflowY(EOverflow::kScroll);
   RefPtr<ComputedStyle> noScrollbarStyle = ComputedStyle::create();
   noScrollbarStyle->setStyleType(PseudoIdScrollbar);
-  noScrollbarStyle->setDisplay(EDisplay::None);
+  noScrollbarStyle->setDisplay(EDisplay::kNone);
   textBlockStyle->addCachedPseudoStyle(noScrollbarStyle);
   textBlockStyle->setHasPseudoStyle(PseudoIdScrollbar);
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp b/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp
index 67532156..065f95a6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp
@@ -37,7 +37,7 @@
 
 void LayoutTextTrackContainer::layout() {
   LayoutBlockFlow::layout();
-  if (style()->display() == EDisplay::None)
+  if (style()->display() == EDisplay::kNone)
     return;
 
   DeprecatedScheduleStyleRecalcDuringLayout marker(
diff --git a/third_party/WebKit/Source/core/layout/LayoutTheme.cpp b/third_party/WebKit/Source/core/layout/LayoutTheme.cpp
index c42b215..cda01b9a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTheme.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTheme.cpp
@@ -82,20 +82,20 @@
   // Force inline and table display styles to be inline-block (except for table-
   // which is block)
   ControlPart part = style.appearance();
-  if (style.display() == EDisplay::Inline ||
-      style.display() == EDisplay::InlineTable ||
-      style.display() == EDisplay::TableRowGroup ||
-      style.display() == EDisplay::TableHeaderGroup ||
-      style.display() == EDisplay::TableFooterGroup ||
-      style.display() == EDisplay::TableRow ||
-      style.display() == EDisplay::TableColumnGroup ||
-      style.display() == EDisplay::TableColumn ||
-      style.display() == EDisplay::TableCell ||
-      style.display() == EDisplay::TableCaption)
-    style.setDisplay(EDisplay::InlineBlock);
-  else if (style.display() == EDisplay::ListItem ||
-           style.display() == EDisplay::Table)
-    style.setDisplay(EDisplay::Block);
+  if (style.display() == EDisplay::kInline ||
+      style.display() == EDisplay::kInlineTable ||
+      style.display() == EDisplay::kTableRowGroup ||
+      style.display() == EDisplay::kTableHeaderGroup ||
+      style.display() == EDisplay::kTableFooterGroup ||
+      style.display() == EDisplay::kTableRow ||
+      style.display() == EDisplay::kTableColumnGroup ||
+      style.display() == EDisplay::kTableColumn ||
+      style.display() == EDisplay::kTableCell ||
+      style.display() == EDisplay::kTableCaption)
+    style.setDisplay(EDisplay::kInlineBlock);
+  else if (style.display() == EDisplay::kListItem ||
+           style.display() == EDisplay::kTable)
+    style.setDisplay(EDisplay::kBlock);
 
   if (isControlStyled(style)) {
     if (part == MenulistPart) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.h b/third_party/WebKit/Source/core/layout/LayoutView.h
index 2674d80..fccdf4bd 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.h
+++ b/third_party/WebKit/Source/core/layout/LayoutView.h
@@ -173,7 +173,8 @@
   LayoutRect viewRect() const override;
   LayoutRect overflowClipRect(
       const LayoutPoint& location,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
 
   LayoutState* layoutState() const { return m_layoutState; }
 
diff --git a/third_party/WebKit/Source/core/layout/api/LayoutBoxItem.h b/third_party/WebKit/Source/core/layout/api/LayoutBoxItem.h
index b51a0b37..1df7eed 100644
--- a/third_party/WebKit/Source/core/layout/api/LayoutBoxItem.h
+++ b/third_party/WebKit/Source/core/layout/api/LayoutBoxItem.h
@@ -49,7 +49,7 @@
 
   LayoutRect overflowClipRect(const LayoutPoint& location,
                               OverlayScrollbarClipBehavior behavior =
-                                  IgnoreOverlayScrollbarSize) const {
+                                  IgnorePlatformOverlayScrollbarSize) const {
     return toBox()->overflowClipRect(location, behavior);
   }
 
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 183c283..c715a29 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -550,7 +550,7 @@
   // FIXME: this should use cached clip rects, but this sometimes give
   // inaccurate results (and trips the ASSERTS in PaintLayerClipper).
   ClipRectsContext clipRectsContext(compositingAncestor, UncachedClipRects,
-                                    IgnoreOverlayScrollbarSize);
+                                    IgnorePlatformOverlayScrollbarSize);
   clipRectsContext.setIgnoreOverflowClip();
 
   ClipRect clipRect;
@@ -1162,7 +1162,7 @@
 
   ClipRectsContext clipRectsContext(compositingContainer,
                                     PaintingClipRectsIgnoringOverflowClip,
-                                    IgnoreOverlayScrollbarSize);
+                                    IgnorePlatformOverlayScrollbarSize);
 
   ClipRect parentClipRect;
   m_owningLayer.clipper(PaintLayer::DoNotUseGeometryMapper)
diff --git a/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h b/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h
index aabab90..8831039 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h
+++ b/third_party/WebKit/Source/core/layout/line/InlineFlowBox.h
@@ -73,7 +73,8 @@
     // must not apply. This change also means that gaps will exist between image
     // bullet list items.  Even when the list bullet is an image, the line is
     // still considered to be immune from the quirk.
-    m_hasTextChildren = lineLayoutItem.style()->display() == EDisplay::ListItem;
+    m_hasTextChildren =
+        lineLayoutItem.style()->display() == EDisplay::kListItem;
     m_hasTextDescendants = m_hasTextChildren;
   }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index d752903..a7b476f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -36,7 +36,7 @@
   bool is_in_parallel_flow =
       IsParallelWritingMode(parent_space.WritingMode(), child_writing_mode);
 
-  return child_style.display() == EDisplay::InlineBlock ||
+  return child_style.display() == EDisplay::kInlineBlock ||
          child_style.isFloating() || !is_in_parallel_flow;
 }
 
@@ -163,6 +163,7 @@
     const LayoutUnit float_offset,
     const NGLogicalOffset& from_offset,
     NGFloatingObject* floating_object) {
+  DCHECK(floating_object);
   auto margins = floating_object->margins;
   // Adjust to child's margin.
   LayoutUnit inline_offset = margins.inline_start;
@@ -187,6 +188,7 @@
                               const NGLogicalOffset& from_offset,
                               NGFloatingObject* floating_object,
                               NGConstraintSpace* new_parent_space) {
+  DCHECK(floating_object);
   const auto* float_space = floating_object->space.get();
   DCHECK(floating_object->fragment) << "Fragment cannot be null here";
 
@@ -220,10 +222,10 @@
 
 // Updates the Floating Object's left offset from the provided parent_space
 // and {@code floating_object}'s space and margins.
-void UpdateFloatingObjectLeftOffset(
-    const NGConstraintSpace& new_parent_space,
-    const Persistent<NGFloatingObject>& floating_object,
-    const NGLogicalOffset& float_logical_offset) {
+void UpdateFloatingObjectLeftOffset(const NGConstraintSpace& new_parent_space,
+                                    const NGLogicalOffset& float_logical_offset,
+                                    NGFloatingObject* floating_object) {
+  DCHECK(floating_object);
   // TODO(glebl): We should use physical offset here.
   floating_object->left_offset =
       floating_object->original_parent_space->BfcOffset().inline_offset -
@@ -250,10 +252,10 @@
         original_parent_space->BfcOffset().inline_offset, bfc_block_offset};
 
     NGLogicalOffset float_fragment_offset = PositionFloat(
-        origin_point, from_offset, floating_object, new_parent_space);
+        origin_point, from_offset, floating_object.get(), new_parent_space);
     builder->AddFloatingObject(floating_object, float_fragment_offset);
-    UpdateFloatingObjectLeftOffset(*new_parent_space, floating_object,
-                                   float_fragment_offset);
+    UpdateFloatingObjectLeftOffset(*new_parent_space, float_fragment_offset,
+                                   floating_object.get());
   }
   builder->MutableUnpositionedFloats().clear();
 }
@@ -283,8 +285,8 @@
     return true;
 
   EDisplay display = style.display();
-  if (display == EDisplay::Grid || display == EDisplay::Flex ||
-      display == EDisplay::WebkitBox)
+  if (display == EDisplay::kGrid || display == EDisplay::kFlex ||
+      display == EDisplay::kWebkitBox)
     return true;
 
   if (space.WritingMode() != FromPlatformWritingMode(style.getWritingMode()))
@@ -534,7 +536,7 @@
 
   if (child->Type() == NGLayoutInputNode::kLegacyBlock &&
       toNGBlockNode(child)->Style().isFloating()) {
-    NGFloatingObject* floating_object = new NGFloatingObject(
+    RefPtr<NGFloatingObject> floating_object = NGFloatingObject::Create(
         child_space, constraint_space_, toNGBlockNode(child)->Style(),
         curr_child_margins_, layout_result->PhysicalFragment().get());
     builder_.AddUnpositionedFloat(floating_object);
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
index 773af5f..af0c901 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
@@ -239,8 +239,9 @@
 
   // We may still have unpositioned floats when we reach the root box.
   if (!layout_box_->parent()) {
-    for (const auto& floating_object : fragment->PositionedFloats()) {
-      FloatingObjectPositionedUpdated(floating_object, layout_box_);
+    for (const RefPtr<NGFloatingObject>& floating_object :
+         fragment->PositionedFloats()) {
+      FloatingObjectPositionedUpdated(floating_object.get(), layout_box_);
     }
   }
 
@@ -248,10 +249,11 @@
     if (child_fragment->IsPlaced())
       FragmentPositionUpdated(toNGPhysicalBoxFragment(*child_fragment));
 
-    for (const auto& floating_object :
+    for (const RefPtr<NGFloatingObject>& floating_object :
          toNGPhysicalBoxFragment(child_fragment.get())->PositionedFloats()) {
       FloatingObjectPositionedUpdated(
-          floating_object, toLayoutBox(child_fragment->GetLayoutObject()));
+          floating_object.get(),
+          toLayoutBox(child_fragment->GetLayoutObject()));
     }
   }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h b/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h
index f841775..8430c5a 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h
@@ -9,30 +9,23 @@
 #include "core/layout/ng/ng_block_node.h"
 #include "core/layout/ng/ng_constraint_space.h"
 #include "core/layout/ng/ng_exclusion.h"
+#include "core/layout/ng/ng_physical_fragment.h"
 #include "core/style/ComputedStyle.h"
 #include "core/style/ComputedStyleConstants.h"
-#include "platform/heap/Handle.h"
+#include "wtf/RefPtr.h"
 
 namespace blink {
 
-class NGPhysicalFragment;
-
 // Struct that keeps all information needed to position floats in LayoutNG.
-struct CORE_EXPORT NGFloatingObject
-    : public GarbageCollectedFinalized<NGFloatingObject> {
-  NGFloatingObject(const NGConstraintSpace* space,
-                   const NGConstraintSpace* parent_space,
-                   const ComputedStyle& style,
-                   const NGBoxStrut& margins,
-                   NGPhysicalFragment* fragment)
-      : space(space),
-        original_parent_space(parent_space),
-        margins(margins),
-        fragment(fragment) {
-    exclusion_type = NGExclusion::kFloatLeft;
-    if (style.floating() == EFloat::kRight)
-      exclusion_type = NGExclusion::kFloatRight;
-    clear_type = style.clear();
+struct CORE_EXPORT NGFloatingObject : public RefCounted<NGFloatingObject> {
+ public:
+  static RefPtr<NGFloatingObject> Create(const NGConstraintSpace* space,
+                                         const NGConstraintSpace* parent_space,
+                                         const ComputedStyle& style,
+                                         const NGBoxStrut& margins,
+                                         NGPhysicalFragment* fragment) {
+    return adoptRef(
+        new NGFloatingObject(space, parent_space, style, margins, fragment));
   }
 
   // Original constraint space of the float.
@@ -57,7 +50,20 @@
   // would be attached.
   LayoutUnit left_offset;
 
-  DEFINE_INLINE_TRACE() {
+ private:
+  NGFloatingObject(const NGConstraintSpace* space,
+                   const NGConstraintSpace* parent_space,
+                   const ComputedStyle& style,
+                   const NGBoxStrut& margins,
+                   NGPhysicalFragment* fragment)
+      : space(space),
+        original_parent_space(parent_space),
+        margins(margins),
+        fragment(fragment) {
+    exclusion_type = NGExclusion::kFloatLeft;
+    if (style.floating() == EFloat::kRight)
+      exclusion_type = NGExclusion::kFloatRight;
+    clear_type = style.clear();
   }
 };
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
index cabf376d..7348e7ea 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
@@ -91,7 +91,7 @@
 }
 
 NGFragmentBuilder& NGFragmentBuilder::AddFloatingObject(
-    NGFloatingObject* floating_object,
+    RefPtr<NGFloatingObject> floating_object,
     const NGLogicalOffset& floating_object_offset) {
   positioned_floats_.push_back(floating_object);
   floating_object_offsets_.push_back(floating_object_offset);
@@ -117,8 +117,8 @@
 }
 
 NGFragmentBuilder& NGFragmentBuilder::AddUnpositionedFloat(
-    NGFloatingObject* floating_object) {
-  unpositioned_floats_.push_back(floating_object);
+    RefPtr<NGFloatingObject> floating_object) {
+  unpositioned_floats_.push_back(std::move(floating_object));
   return *this;
 }
 
@@ -173,9 +173,6 @@
         writing_mode_, direction_, physical_size, child->Size()));
   }
 
-  Vector<Persistent<NGFloatingObject>> positioned_floats;
-  positioned_floats.reserveCapacity(positioned_floats_.size());
-
   RefPtr<NGBreakToken> break_token;
   if (did_break_) {
     break_token = NGBlockBreakToken::create(
@@ -185,11 +182,10 @@
   }
 
   for (size_t i = 0; i < positioned_floats_.size(); ++i) {
-    Persistent<NGFloatingObject>& floating_object = positioned_floats_[i];
+    RefPtr<NGFloatingObject>& floating_object = positioned_floats_[i];
     NGPhysicalFragment* floating_fragment = floating_object->fragment.get();
     floating_fragment->SetOffset(floating_object_offsets_[i].ConvertToPhysical(
         writing_mode_, direction_, physical_size, floating_fragment->Size()));
-    positioned_floats.push_back(floating_object);
   }
 
   RefPtr<NGPhysicalBoxFragment> fragment = adoptRef(new NGPhysicalBoxFragment(
@@ -210,9 +206,6 @@
   DCHECK(children_.isEmpty());
   DCHECK(offsets_.isEmpty());
 
-  Vector<Persistent<NGFloatingObject>> empty_unpositioned_floats;
-  Vector<Persistent<NGFloatingObject>> empty_positioned_floats;
-
   return adoptRef(new NGPhysicalTextFragment(
       node_->GetLayoutObject(), toNGInlineNode(node_), index, start_offset,
       end_offset, size_.ConvertToPhysical(writing_mode_),
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
index fd11878d..f3f18eb 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
@@ -38,12 +38,13 @@
   NGFragmentBuilder& AddChild(RefPtr<NGPhysicalFragment>,
                               const NGLogicalOffset&);
 
-  NGFragmentBuilder& AddFloatingObject(NGFloatingObject*,
+  NGFragmentBuilder& AddFloatingObject(RefPtr<NGFloatingObject>,
                                        const NGLogicalOffset&);
 
   NGFragmentBuilder& SetBfcOffset(const NGLogicalOffset& offset);
 
-  NGFragmentBuilder& AddUnpositionedFloat(NGFloatingObject* floating_object);
+  NGFragmentBuilder& AddUnpositionedFloat(
+      RefPtr<NGFloatingObject> floating_object);
 
   // Builder has non-trivial out-of-flow descendant methods.
   // These methods are building blocks for implementation of
@@ -103,12 +104,12 @@
                                                 unsigned end_offset);
 
   // Mutable list of floats that need to be positioned.
-  Vector<Persistent<NGFloatingObject>>& MutableUnpositionedFloats() {
+  Vector<RefPtr<NGFloatingObject>>& MutableUnpositionedFloats() {
     return unpositioned_floats_;
   }
 
   // List of floats that need to be positioned.
-  const Vector<Persistent<NGFloatingObject>>& UnpositionedFloats() const {
+  const Vector<RefPtr<NGFloatingObject>>& UnpositionedFloats() const {
     return unpositioned_floats_;
   }
 
@@ -162,10 +163,10 @@
 
   // Floats that need to be positioned by the next in-flow fragment that can
   // determine its block position in space.
-  Vector<Persistent<NGFloatingObject>> unpositioned_floats_;
+  Vector<RefPtr<NGFloatingObject>> unpositioned_floats_;
 
   Vector<NGLogicalOffset> floating_object_offsets_;
-  Vector<Persistent<NGFloatingObject>> positioned_floats_;
+  Vector<RefPtr<NGFloatingObject>> positioned_floats_;
 
   WTF::Optional<NGLogicalOffset> bfc_offset_;
   NGMarginStrut end_margin_strut_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc
index 1954a5b..d078576 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc
@@ -22,7 +22,7 @@
   // [1] https://drafts.csswg.org/css-text-3/#line-break-transform
   // [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2
   unsigned next_start_offset = text_.length();
-  RemoveTrailingCollapsibleSpace(&next_start_offset);
+  RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset);
 
   return text_.toString();
 }
@@ -55,8 +55,9 @@
                                     const ComputedStyle* after_style) {
   // Remove if either before/after the newline is zeroWidthSpaceCharacter.
   UChar32 last = 0;
-  if (!before.isEmpty()) {
-    last = before[before.length() - 1];
+  DCHECK(!before.isEmpty() && before[before.length() - 1] == ' ');
+  if (before.length() >= 2) {
+    last = before[before.length() - 2];
     if (last == zeroWidthSpaceCharacter)
       return true;
   }
@@ -111,15 +112,17 @@
   items->push_back(NGLayoutInlineItem(start, end, style, layout_object));
 }
 
+static inline bool IsCollapsibleSpace(UChar c, bool preserve_newline) {
+  return c == spaceCharacter || c == tabulationCharacter ||
+         (!preserve_newline && c == newlineCharacter);
+}
+
 void NGLayoutInlineItemsBuilder::Append(const String& string,
                                         const ComputedStyle* style,
                                         LayoutObject* layout_object) {
   if (string.isEmpty())
     return;
 
-  if (has_pending_newline_)
-    ProcessPendingNewline(string, style);
-
   EWhiteSpace whitespace = style->whiteSpace();
   bool preserve_newline =
       ComputedStyle::preserveNewline(whitespace) && !is_svgtext_;
@@ -128,38 +131,49 @@
 
   if (!collapse_whitespace) {
     text_.append(string);
-    is_last_collapsible_space_ = false;
+    last_collapsible_space_ = CollapsibleSpace::None;
   } else {
     text_.reserveCapacity(string.length());
-    for (unsigned i = 0; i < string.length(); i++) {
+    for (unsigned i = 0; i < string.length();) {
       UChar c = string[i];
-      bool is_collapsible_space;
       if (c == newlineCharacter) {
-        RemoveTrailingCollapsibleSpace(&start_offset);
         if (preserve_newline) {
+          RemoveTrailingCollapsibleSpaceIfExists(&start_offset);
           text_.append(c);
           // Remove collapsible spaces immediately following a newline.
-          is_last_collapsible_space_ = true;
+          last_collapsible_space_ = CollapsibleSpace::Space;
+          i++;
           continue;
         }
-        if (i + 1 == string.length()) {
-          // If at the end of string, process this newline on the next Append.
-          has_pending_newline_ = true;
+
+        if (last_collapsible_space_ == CollapsibleSpace::None)
+          text_.append(spaceCharacter);
+        last_collapsible_space_ = CollapsibleSpace::Newline;
+        i++;
+        continue;
+      }
+
+      if (c == spaceCharacter || c == tabulationCharacter) {
+        if (last_collapsible_space_ == CollapsibleSpace::None) {
+          text_.append(spaceCharacter);
+          last_collapsible_space_ = CollapsibleSpace::Space;
+        }
+        i++;
+        continue;
+      }
+
+      if (last_collapsible_space_ == CollapsibleSpace::Newline) {
+        RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i,
+                                                 style);
+      }
+
+      unsigned start_of_non_space = i;
+      for (i++; i < string.length(); i++) {
+        if (IsCollapsibleSpace(string[i], false))
           break;
-        }
-        if (ShouldRemoveNewline(text_, style, string, i + 1, style))
-          continue;
-        is_collapsible_space = true;
-      } else {
-        is_collapsible_space = c == spaceCharacter || c == tabulationCharacter;
       }
-      if (!is_collapsible_space) {
-        text_.append(c);
-        is_last_collapsible_space_ = false;
-      } else if (!is_last_collapsible_space_) {
-        text_.append(spaceCharacter);
-        is_last_collapsible_space_ = true;
-      }
+      text_.append(string, start_of_non_space, i - start_of_non_space);
+      last_collapsible_space_ = CollapsibleSpace::None;
     }
   }
 
@@ -172,49 +186,49 @@
                                         LayoutObject* layout_object) {
   DCHECK(character != spaceCharacter && character != tabulationCharacter &&
          character != newlineCharacter && character != zeroWidthSpaceCharacter);
-  if (has_pending_newline_)
-    ProcessPendingNewline(emptyString, nullptr);
 
   text_.append(character);
   unsigned end_offset = text_.length();
   AppendItem(items_, end_offset - 1, end_offset, style, layout_object);
-  is_last_collapsible_space_ = false;
+  last_collapsible_space_ = CollapsibleSpace::None;
 }
 
-void NGLayoutInlineItemsBuilder::AppendAsOpaqueToSpaceCollapsing(
-    UChar character) {
-  if (has_pending_newline_)
-    ProcessPendingNewline(emptyString, nullptr);
+void NGLayoutInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded(
+    unsigned* next_start_offset,
+    const String& after,
+    unsigned after_index,
+    const ComputedStyle* after_style) {
+  DCHECK_EQ(last_collapsible_space_, CollapsibleSpace::Newline);
 
-  text_.append(character);
-  unsigned end_offset = text_.length();
-  AppendItem(items_, end_offset - 1, end_offset, nullptr);
-}
+  if (text_.isEmpty() || text_[text_.length() - 1] != spaceCharacter)
+    return;
 
-void NGLayoutInlineItemsBuilder::ProcessPendingNewline(
-    const String& string,
-    const ComputedStyle* style) {
-  DCHECK(has_pending_newline_);
+  const ComputedStyle* before_style = after_style;
   if (!items_->isEmpty()) {
     NGLayoutInlineItem& item = items_->back();
-    if (!ShouldRemoveNewline(text_, item.Style(), string, 0, style)) {
-      text_.append(spaceCharacter);
-      item.SetEndOffset(text_.length());
-    }
+    if (text_.length() < item.EndOffset() + 2)
+      before_style = item.Style();
   }
-  // Remove spaces following a newline even when the newline was removed.
-  is_last_collapsible_space_ = true;
-  has_pending_newline_ = false;
+
+  if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style))
+    RemoveTrailingCollapsibleSpace(next_start_offset);
+}
+
+void NGLayoutInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists(
+    unsigned* next_start_offset) {
+  if (last_collapsible_space_ != CollapsibleSpace::None && !text_.isEmpty() &&
+      text_[text_.length() - 1] == spaceCharacter)
+    RemoveTrailingCollapsibleSpace(next_start_offset);
 }
 
 void NGLayoutInlineItemsBuilder::RemoveTrailingCollapsibleSpace(
     unsigned* next_start_offset) {
-  if (!is_last_collapsible_space_ || text_.isEmpty())
-    return;
-  DCHECK_EQ(spaceCharacter, text_[text_.length() - 1]);
+  DCHECK_NE(last_collapsible_space_, CollapsibleSpace::None);
+  DCHECK(!text_.isEmpty() && text_[text_.length() - 1] == spaceCharacter);
+
   unsigned new_size = text_.length() - 1;
   text_.resize(new_size);
-  is_last_collapsible_space_ = false;
+  last_collapsible_space_ = CollapsibleSpace::None;
 
   // Adjust the last item if the removed space is already appended.
   if (*next_start_offset > new_size) {
@@ -233,8 +247,7 @@
 void NGLayoutInlineItemsBuilder::AppendBidiControl(const ComputedStyle* style,
                                                    UChar ltr,
                                                    UChar rtl) {
-  AppendAsOpaqueToSpaceCollapsing(
-      style->direction() == TextDirection::kRtl ? rtl : ltr);
+  Append(style->direction() == TextDirection::kRtl ? rtl : ltr);
 }
 
 void NGLayoutInlineItemsBuilder::EnterBlock(const ComputedStyle* style) {
@@ -273,11 +286,11 @@
       Enter(node, popDirectionalIsolateCharacter);
       break;
     case UnicodeBidi::kPlaintext:
-      AppendAsOpaqueToSpaceCollapsing(firstStrongIsolateCharacter);
+      Append(firstStrongIsolateCharacter);
       Enter(node, popDirectionalIsolateCharacter);
       break;
     case UnicodeBidi::kIsolateOverride:
-      AppendAsOpaqueToSpaceCollapsing(firstStrongIsolateCharacter);
+      Append(firstStrongIsolateCharacter);
       AppendBidiControl(style, leftToRightOverrideCharacter,
                         rightToLeftOverrideCharacter);
       Enter(node, popDirectionalIsolateCharacter);
@@ -303,7 +316,7 @@
 
 void NGLayoutInlineItemsBuilder::Exit(LayoutObject* node) {
   while (!exits_.isEmpty() && exits_.back().node == node) {
-    AppendAsOpaqueToSpaceCollapsing(exits_.back().character);
+    Append(exits_.back().character);
     exits_.pop_back();
   }
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.h
index adeb6f9..4fe4e42 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.h
@@ -61,11 +61,7 @@
   // LayoutObject.
   void Append(UChar, const ComputedStyle* = nullptr, LayoutObject* = nullptr);
 
-  // Append a character.
-  // The character is opaque to space collapsing that spaces before this
-  // character and after this character can collapse as if this character does
-  // not exist.
-  void AppendAsOpaqueToSpaceCollapsing(UChar);
+  // Append a Bidi control character, for LTR or RTL depends on the style.
   void AppendBidiControl(const ComputedStyle*, UChar ltr, UChar rtl);
 
   void EnterBlock(const ComputedStyle*);
@@ -83,8 +79,9 @@
   } OnExitNode;
   Vector<OnExitNode> exits_;
 
-  bool is_last_collapsible_space_ = true;
-  bool has_pending_newline_ = false;
+  enum class CollapsibleSpace { None, Space, Newline };
+
+  CollapsibleSpace last_collapsible_space_ = CollapsibleSpace::Space;
   bool is_svgtext_ = false;
   bool has_bidi_controls_ = false;
 
@@ -95,8 +92,14 @@
   void ProcessPendingNewline(const String&, const ComputedStyle*);
 
   // Removes the collapsible space at the end of |text_| if exists.
+  void RemoveTrailingCollapsibleSpaceIfExists(unsigned*);
   void RemoveTrailingCollapsibleSpace(unsigned*);
 
+  void RemoveTrailingCollapsibleNewlineIfNeeded(unsigned*,
+                                                const String&,
+                                                unsigned,
+                                                const ComputedStyle*);
+
   void Enter(LayoutObject*, UChar);
   void Exit(LayoutObject*);
 };
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
index b878a5a..3331e7c 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
@@ -97,15 +97,22 @@
 }
 
 TEST_F(NGLayoutInlineItemsBuilderTest, CollapseNewLines) {
-  String input("text\ntext \n text");
-  String collapsed("text text text");
+  String input("text\ntext \n text\n\ntext");
+  String collapsed("text text text text");
   TestWhitespaceValue(collapsed, input, EWhiteSpace::kNormal);
   TestWhitespaceValue(collapsed, input, EWhiteSpace::kNowrap);
-  TestWhitespaceValue("text\ntext\ntext", input, EWhiteSpace::kPreLine);
+  TestWhitespaceValue("text\ntext\ntext\n\ntext", input, EWhiteSpace::kPreLine);
   TestWhitespaceValue(input, input, EWhiteSpace::kPre);
   TestWhitespaceValue(input, input, EWhiteSpace::kPreWrap);
 }
 
+TEST_F(NGLayoutInlineItemsBuilderTest, CollapseNewlinesAsSpaces) {
+  EXPECT_EQ("text text", TestAppend("text\ntext"));
+  EXPECT_EQ("text text", TestAppend("text\n\ntext"));
+  EXPECT_EQ("text text", TestAppend("text \n\n text"));
+  EXPECT_EQ("text text", TestAppend("text \n \n text"));
+}
+
 TEST_F(NGLayoutInlineItemsBuilderTest, CollapseAcrossElements) {
   EXPECT_EQ("text text", TestAppend("text ", " text"))
       << "Spaces are collapsed even when across elements.";
@@ -175,9 +182,6 @@
 }
 
 TEST_F(NGLayoutInlineItemsBuilderTest, CollapseZeroWidthSpaces) {
-  EXPECT_EQ("text text", TestAppend("text\ntext"))
-      << "Newline is converted to a space.";
-
   EXPECT_EQ(String(u"text\u200Btext"), TestAppend(u"text\u200B\ntext"))
       << "Newline is removed if the character before is ZWS.";
   EXPECT_EQ(String(u"text\u200Btext"), TestAppend(u"text\n\u200Btext"))
@@ -224,12 +228,16 @@
   EXPECT_EQ(String(u"Hello \uFFFC World"), builder.ToString());
 }
 
-TEST_F(NGLayoutInlineItemsBuilderTest, AppendAsOpaqueToSpaceCollapsing) {
+TEST_F(NGLayoutInlineItemsBuilderTest, CollapseNewlineAfterObject) {
   NGLayoutInlineItemsBuilder builder(&items_);
-  builder.Append("Hello ", style_.get());
-  builder.AppendAsOpaqueToSpaceCollapsing(firstStrongIsolateCharacter);
-  builder.Append(" World", style_.get());
-  EXPECT_EQ(String(u"Hello \u2068World"), builder.ToString());
+  builder.Append(objectReplacementCharacter);
+  builder.Append("\n", style_.get());
+  builder.Append(objectReplacementCharacter);
+  EXPECT_EQ(String(u"\uFFFC \uFFFC"), builder.ToString());
+  EXPECT_EQ(3u, items_.size());
+  EXPECT_EQ(nullptr, items_[0].Style());
+  EXPECT_EQ(style_.get(), items_[1].Style());
+  EXPECT_EQ(nullptr, items_[2].Style());
 }
 
 TEST_F(NGLayoutInlineItemsBuilderTest, AppendEmptyString) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_result.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_result.cc
index 5a45423..c70b970 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_result.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_result.cc
@@ -4,8 +4,6 @@
 
 #include "core/layout/ng/ng_layout_result.h"
 
-#include "core/layout/ng/ng_floating_object.h"
-
 namespace blink {
 
 NGLayoutResult::NGLayoutResult(
@@ -13,10 +11,12 @@
     PersistentHeapLinkedHashSet<WeakMember<NGBlockNode>>&
         out_of_flow_descendants,
     Vector<NGStaticPosition> out_of_flow_positions,
-    Vector<Persistent<NGFloatingObject>>& unpositioned_floats)
+    Vector<RefPtr<NGFloatingObject>>& unpositioned_floats)
     : physical_fragment_(physical_fragment),
+      layout_object_(nullptr),
       out_of_flow_descendants_(out_of_flow_descendants),
-      out_of_flow_positions_(out_of_flow_positions),
-      unpositioned_floats_(unpositioned_floats) {}
+      out_of_flow_positions_(out_of_flow_positions) {
+  unpositioned_floats_.swap(unpositioned_floats);
+}
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h
index 3ec77699..64689633 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h
@@ -7,6 +7,7 @@
 
 #include "core/CoreExport.h"
 #include "core/layout/ng/geometry/ng_static_position.h"
+#include "core/layout/ng/ng_floating_object.h"
 #include "core/layout/ng/ng_physical_fragment.h"
 #include "platform/LayoutUnit.h"
 #include "platform/heap/Handle.h"
@@ -15,7 +16,6 @@
 namespace blink {
 
 class LayoutObject;
-class NGPhysicalFragment;
 class NGBlockNode;
 struct NGFloatingObject;
 
@@ -48,7 +48,7 @@
   // The float cannot be positioned right away inside of the 1st div because
   // the vertical position is not known at that moment. It will be known only
   // after the 2nd div collapses its margin with its parent.
-  const Vector<Persistent<NGFloatingObject>>& UnpositionedFloats() const {
+  const Vector<RefPtr<NGFloatingObject>>& UnpositionedFloats() const {
     return unpositioned_floats_;
   }
 
@@ -59,13 +59,13 @@
                  PersistentHeapLinkedHashSet<WeakMember<NGBlockNode>>&
                      out_of_flow_descendants,
                  Vector<NGStaticPosition> out_of_flow_positions,
-                 Vector<Persistent<NGFloatingObject>>& unpositioned_floats);
+                 Vector<RefPtr<NGFloatingObject>>& unpositioned_floats);
 
   RefPtr<NGPhysicalFragment> physical_fragment_;
   LayoutObject* layout_object_;
   PersistentHeapLinkedHashSet<WeakMember<NGBlockNode>> out_of_flow_descendants_;
   Vector<NGStaticPosition> out_of_flow_positions_;
-  Vector<Persistent<NGFloatingObject>> unpositioned_floats_;
+  Vector<RefPtr<NGFloatingObject>> unpositioned_floats_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.cc b/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.cc
index e3f359a..420b79fb 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.cc
@@ -13,7 +13,7 @@
     NGPhysicalSize size,
     NGPhysicalSize overflow,
     Vector<RefPtr<NGPhysicalFragment>>& children,
-    Vector<Persistent<NGFloatingObject>>& positioned_floats,
+    Vector<RefPtr<NGFloatingObject>>& positioned_floats,
     const WTF::Optional<NGLogicalOffset>& bfc_offset,
     const NGMarginStrut& end_margin_strut,
     RefPtr<NGBreakToken> break_token)
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.h b/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.h
index f8031d47..c6c1542 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_box_fragment.h
@@ -8,8 +8,8 @@
 #include "core/CoreExport.h"
 #include "core/layout/ng/geometry/ng_logical_offset.h"
 #include "core/layout/ng/geometry/ng_margin_strut.h"
+#include "core/layout/ng/ng_floating_object.h"
 #include "core/layout/ng/ng_physical_fragment.h"
-#include "platform/heap/Handle.h"
 #include "wtf/Optional.h"
 
 namespace blink {
@@ -23,7 +23,7 @@
                         NGPhysicalSize size,
                         NGPhysicalSize overflow,
                         Vector<RefPtr<NGPhysicalFragment>>& children,
-                        Vector<Persistent<NGFloatingObject>>& positioned_floats,
+                        Vector<RefPtr<NGFloatingObject>>& positioned_floats,
                         const WTF::Optional<NGLogicalOffset>& bfc_offset,
                         const NGMarginStrut& end_margin_strut,
                         RefPtr<NGBreakToken> break_token = nullptr);
@@ -35,7 +35,7 @@
   // List of positioned floats that need to be copied to the old layout tree.
   // TODO(layout-ng): remove this once we change painting code to handle floats
   // differently.
-  const Vector<Persistent<NGFloatingObject>>& PositionedFloats() const {
+  const Vector<RefPtr<NGFloatingObject>>& PositionedFloats() const {
     return positioned_floats_;
   }
 
@@ -47,7 +47,7 @@
 
  private:
   Vector<RefPtr<NGPhysicalFragment>> children_;
-  Vector<Persistent<NGFloatingObject>> positioned_floats_;
+  Vector<RefPtr<NGFloatingObject>> positioned_floats_;
   const WTF::Optional<NGLogicalOffset> bfc_offset_;
   const NGMarginStrut end_margin_strut_;
 };
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp
index 9f1fb920..3985c762 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp
@@ -51,7 +51,7 @@
   if (!layoutObject)
     return ClipStrategy::None;
   const ComputedStyle& style = layoutObject->styleRef();
-  if (style.display() == EDisplay::None ||
+  if (style.display() == EDisplay::kNone ||
       style.visibility() != EVisibility::kVisible)
     return ClipStrategy::None;
   ClipStrategy strategy = ClipStrategy::None;
@@ -71,7 +71,7 @@
   if (isSVGUseElement(element)) {
     const LayoutObject* useLayoutObject = element.layoutObject();
     if (!useLayoutObject ||
-        useLayoutObject->styleRef().display() == EDisplay::None)
+        useLayoutObject->styleRef().display() == EDisplay::kNone)
       return ClipStrategy::None;
     const SVGGraphicsElement* shapeElement =
         toSVGUseElement(element).visibleTargetGraphicsElementForClipping();
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp
index e215ce3..7d1df37 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp
@@ -88,7 +88,7 @@
   for (const SVGElement& childElement :
        Traversal<SVGElement>::childrenOf(*element())) {
     const LayoutObject* layoutObject = childElement.layoutObject();
-    if (!layoutObject || layoutObject->styleRef().display() == EDisplay::None)
+    if (!layoutObject || layoutObject->styleRef().display() == EDisplay::kNone)
       continue;
     SVGPaintContext::paintResourceSubtree(builder.context(), layoutObject);
   }
@@ -101,7 +101,7 @@
   for (const SVGElement& childElement :
        Traversal<SVGElement>::childrenOf(*element())) {
     const LayoutObject* layoutObject = childElement.layoutObject();
-    if (!layoutObject || layoutObject->styleRef().display() == EDisplay::None)
+    if (!layoutObject || layoutObject->styleRef().display() == EDisplay::kNone)
       continue;
     m_maskContentBoundaries.unite(
         layoutObject->localToSVGParentTransform().mapRect(
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
index e3d0528a..01d958f 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
@@ -230,8 +230,8 @@
 
 LayoutRect LayoutSVGRoot::overflowClipRect(const LayoutPoint& location,
                                            OverlayScrollbarClipBehavior) const {
-  return LayoutRect(pixelSnappedIntRect(
-      LayoutReplaced::overflowClipRect(location, IgnoreOverlayScrollbarSize)));
+  return LayoutRect(pixelSnappedIntRect(LayoutReplaced::overflowClipRect(
+      location, IgnorePlatformOverlayScrollbarSize)));
 }
 
 void LayoutSVGRoot::paintReplaced(const PaintInfo& paintInfo,
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h
index d9bc63b..a410c11 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h
@@ -90,7 +90,8 @@
   LayoutRect visualOverflowRect() const override;
   LayoutRect overflowClipRect(
       const LayoutPoint& location,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
 
   bool hasNonIsolatedBlendingDescendants() const final;
 
diff --git a/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp b/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
index f178e994..51d4b2a 100644
--- a/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
+++ b/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
@@ -20,6 +20,7 @@
   callback->call(wrappable, result);
 }
 
+// static
 MojoWatcher* MojoWatcher::create(mojo::Handle handle,
                                  const MojoHandleSignals& signalsDict,
                                  MojoWatchCallback* callback,
@@ -42,42 +43,16 @@
   return watcher;
 }
 
-MojoWatcher::MojoWatcher(ExecutionContext* context, MojoWatchCallback* callback)
-    : ContextLifecycleObserver(context),
-      m_taskRunner(TaskRunnerHelper::get(TaskType::UnspecedTimer, context)),
-      m_callback(this, callback) {}
-
 MojoWatcher::~MojoWatcher() {
   DCHECK(!m_handle.is_valid());
 }
 
-MojoResult MojoWatcher::watch(mojo::Handle handle,
-                              const MojoHandleSignals& signalsDict) {
-  ::MojoHandleSignals signals = MOJO_HANDLE_SIGNAL_NONE;
-  if (signalsDict.readable())
-    signals |= MOJO_HANDLE_SIGNAL_READABLE;
-  if (signalsDict.writable())
-    signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
-  if (signalsDict.peerClosed())
-    signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
-
-  MojoResult result =
-      MojoWatch(handle.value(), signals, &MojoWatcher::onHandleReady,
-                reinterpret_cast<uintptr_t>(this));
-  if (result == MOJO_RESULT_OK) {
-    m_handle = handle;
-  }
-  return result;
-}
-
 MojoResult MojoWatcher::cancel() {
-  if (!m_handle.is_valid())
-    return MOJO_RESULT_OK;
+  if (!m_watcherHandle.is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
 
-  MojoResult result =
-      MojoCancelWatch(m_handle.value(), reinterpret_cast<uintptr_t>(this));
-  m_handle = mojo::Handle();
-  return result;
+  m_watcherHandle.reset();
+  return MOJO_RESULT_OK;
 }
 
 DEFINE_TRACE(MojoWatcher) {
@@ -97,13 +72,76 @@
   cancel();
 }
 
+MojoWatcher::MojoWatcher(ExecutionContext* context, MojoWatchCallback* callback)
+    : ContextLifecycleObserver(context),
+      m_taskRunner(TaskRunnerHelper::get(TaskType::UnspecedTimer, context)),
+      m_callback(this, callback) {}
+
+MojoResult MojoWatcher::watch(mojo::Handle handle,
+                              const MojoHandleSignals& signalsDict) {
+  ::MojoHandleSignals signals = MOJO_HANDLE_SIGNAL_NONE;
+  if (signalsDict.readable())
+    signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  if (signalsDict.writable())
+    signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+  if (signalsDict.peerClosed())
+    signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+  MojoResult result =
+      mojo::CreateWatcher(&MojoWatcher::onHandleReady, &m_watcherHandle);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+
+  result = MojoWatch(m_watcherHandle.get().value(), handle.value(), signals,
+                     reinterpret_cast<uintptr_t>(this));
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  m_handle = handle;
+
+  MojoResult readyResult;
+  result = arm(&readyResult);
+  if (result == MOJO_RESULT_OK)
+    return result;
+
+  // We couldn't arm the watcher because the handle is already ready to
+  // trigger a success notification. Post a notification manually.
+  DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  m_taskRunner->postTask(BLINK_FROM_HERE,
+                         WTF::bind(&MojoWatcher::runReadyCallback,
+                                   wrapPersistent(this), readyResult));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult MojoWatcher::arm(MojoResult* readyResult) {
+  // Nothing to do if the watcher is inactive.
+  if (!m_handle.is_valid())
+    return MOJO_RESULT_OK;
+
+  uint32_t numReadyContexts = 1;
+  uintptr_t readyContext;
+  MojoResult localReadyResult;
+  MojoHandleSignalsState readySignals;
+  MojoResult result =
+      MojoArmWatcher(m_watcherHandle.get().value(), &numReadyContexts,
+                     &readyContext, &localReadyResult, &readySignals);
+  if (result == MOJO_RESULT_OK)
+    return MOJO_RESULT_OK;
+
+  DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  DCHECK_EQ(1u, numReadyContexts);
+  DCHECK_EQ(reinterpret_cast<uintptr_t>(this), readyContext);
+  *readyResult = localReadyResult;
+  return result;
+}
+
 void MojoWatcher::onHandleReady(uintptr_t context,
                                 MojoResult result,
                                 MojoHandleSignalsState,
-                                MojoWatchNotificationFlags) {
-  // It is safe to assume the MojoWatcher still exists because this
-  // callback will never be run after MojoWatcher destructor,
-  // which cancels the watch.
+                                MojoWatcherNotificationFlags) {
+  // It is safe to assume the MojoWatcher still exists. It stays alive at least
+  // as long as |m_handle| is valid, and |m_handle| is only reset after we
+  // dispatch a |MOJO_RESULT_CANCELLED| notification. That is always the last
+  // notification received by this callback.
   MojoWatcher* watcher = reinterpret_cast<MojoWatcher*>(context);
   watcher->m_taskRunner->postTask(
       BLINK_FROM_HERE,
@@ -112,17 +150,41 @@
 }
 
 void MojoWatcher::runReadyCallback(MojoResult result) {
-  // Ignore callbacks if not watching.
-  if (!m_handle.is_valid())
-    return;
-
-  // MOJO_RESULT_CANCELLED indicates that the handle has been closed, in which
-  // case watch has been implicitly cancelled. There is no need to explicitly
-  // cancel the watch.
-  if (result == MOJO_RESULT_CANCELLED)
+  if (result == MOJO_RESULT_CANCELLED) {
+    // Last notification.
     m_handle = mojo::Handle();
 
+    // Only dispatch to the callback if this cancellation was implicit due to
+    // |m_handle| closure. If it was explicit, |m_watcherHandle| has already
+    // been reset.
+    if (m_watcherHandle.is_valid()) {
+      m_watcherHandle.reset();
+      runWatchCallback(m_callback, this, result);
+    }
+    return;
+  }
+
+  // Ignore callbacks if not watching.
+  if (!m_watcherHandle.is_valid())
+    return;
+
   runWatchCallback(m_callback, this, result);
+
+  // Rearm the watcher so another notification can fire.
+  //
+  // TODO(rockot): MojoWatcher should expose some better approximation of the
+  // new watcher API, including explicit add and removal of handles from the
+  // watcher, as well as explicit arming.
+  MojoResult readyResult;
+  MojoResult armResult = arm(&readyResult);
+  if (armResult == MOJO_RESULT_OK)
+    return;
+
+  DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, armResult);
+
+  m_taskRunner->postTask(BLINK_FROM_HERE,
+                         WTF::bind(&MojoWatcher::runReadyCallback,
+                                   wrapWeakPersistent(this), readyResult));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/mojo/MojoWatcher.h b/third_party/WebKit/Source/core/mojo/MojoWatcher.h
index fe6e193..99671de8 100644
--- a/third_party/WebKit/Source/core/mojo/MojoWatcher.h
+++ b/third_party/WebKit/Source/core/mojo/MojoWatcher.h
@@ -10,6 +10,7 @@
 #include "bindings/core/v8/TraceWrapperMember.h"
 #include "core/dom/ContextLifecycleObserver.h"
 #include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/watcher.h"
 
 namespace blink {
 
@@ -47,15 +48,17 @@
 
   MojoWatcher(ExecutionContext*, MojoWatchCallback*);
   MojoResult watch(mojo::Handle, const MojoHandleSignals&);
+  MojoResult arm(MojoResult* readyResult);
 
   static void onHandleReady(uintptr_t context,
                             MojoResult,
                             MojoHandleSignalsState,
-                            MojoWatchNotificationFlags);
+                            MojoWatcherNotificationFlags);
   void runReadyCallback(MojoResult);
 
   RefPtr<WebTaskRunner> m_taskRunner;
   TraceWrapperMember<MojoWatchCallback> m_callback;
+  mojo::ScopedWatcherHandle m_watcherHandle;
   mojo::Handle m_handle;
 };
 
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index c270afe..257a0c5 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -128,6 +128,7 @@
       m_deviceScaleFactor(1),
       m_visibilityState(PageVisibilityStateVisible),
       m_isCursorVisible(true),
+      m_subframeCount(0),
       m_frameHost(FrameHost::create(*this)) {
   ASSERT(m_editorClient);
 
@@ -424,6 +425,25 @@
   return m_isCursorVisible;
 }
 
+#if DCHECK_IS_ON()
+void checkFrameCountConsistency(int expectedFrameCount, Frame* frame) {
+  DCHECK_GE(expectedFrameCount, 0);
+
+  int actualFrameCount = 0;
+  for (; frame; frame = frame->tree().traverseNext())
+    ++actualFrameCount;
+
+  DCHECK_EQ(expectedFrameCount, actualFrameCount);
+}
+#endif
+
+int Page::subframeCount() const {
+#if DCHECK_IS_ON()
+  checkFrameCountConsistency(m_subframeCount + 1, mainFrame());
+#endif
+  return m_subframeCount;
+}
+
 void Page::settingsChanged(SettingsDelegate::ChangeType changeType) {
   switch (changeType) {
     case SettingsDelegate::StyleChange:
@@ -555,7 +575,7 @@
 
     // TODO(rbyers): Most of this doesn't appear to take into account that each
     // SVGImage gets it's own Page instance.
-    frameHost().consoleMessageStorage().clear();
+    consoleMessageStorage().clear();
     useCounter().didCommitLoad(url);
     deprecation().clearSuppression();
     visualViewport().sendUMAMetrics();
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index d427c88..25f9144 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -250,6 +250,18 @@
   bool isCursorVisible() const;
   void setIsCursorVisible(bool isVisible) { m_isCursorVisible = isVisible; }
 
+  // Don't allow more than a certain number of frames in a page.
+  // This seems like a reasonable upper bound, and otherwise mutually
+  // recursive frameset pages can quickly bring the program to its knees
+  // with exponential growth in the number of frames.
+  static const int maxNumberOfFrames = 1000;
+  void incrementSubframeCount() { ++m_subframeCount; }
+  void decrementSubframeCount() {
+    DCHECK_GT(m_subframeCount, 0);
+    --m_subframeCount;
+  }
+  int subframeCount() const;
+
   void setDefaultPageScaleLimits(float minScale, float maxScale);
   void setUserAgentPageScaleConstraints(
       const PageScaleConstraints& newConstraints);
@@ -346,6 +358,8 @@
   bool m_isPainting = false;
 #endif
 
+  int m_subframeCount;
+
   // A pointer to all the interfaces provided to in-process Frames for this
   // Page.
   // FIXME: Most of the members of Page should move onto FrameHost.
diff --git a/third_party/WebKit/Source/core/paint/ClipRectsCache.h b/third_party/WebKit/Source/core/paint/ClipRectsCache.h
index 0fb9aae..3b54ee9 100644
--- a/third_party/WebKit/Source/core/paint/ClipRectsCache.h
+++ b/third_party/WebKit/Source/core/paint/ClipRectsCache.h
@@ -41,7 +41,7 @@
         : root(nullptr)
 #if DCHECK_IS_ON()
           ,
-          overlayScrollbarClipBehavior(IgnoreOverlayScrollbarSize)
+          overlayScrollbarClipBehavior(IgnorePlatformOverlayScrollbarSize)
 #endif
     {
     }
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index ad43b9f..a79763d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -845,7 +845,7 @@
       const LayoutRect& dirtyRect,
       ClipRectsCacheSlot,
       GeometryMapperOption,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize,
+      OverlayScrollbarClipBehavior = IgnorePlatformOverlayScrollbarSize,
       ShouldRespectOverflowClipType = RespectOverflowClip,
       const LayoutPoint* offsetFromRoot = 0,
       const LayoutSize& subPixelAccumulation = LayoutSize());
@@ -855,7 +855,7 @@
       const LayoutRect& dirtyRect,
       ClipRectsCacheSlot,
       GeometryMapperOption,
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize,
+      OverlayScrollbarClipBehavior = IgnorePlatformOverlayScrollbarSize,
       ShouldRespectOverflowClipType = RespectOverflowClip,
       const LayoutPoint* offsetFromRoot = 0,
       const LayoutSize& subPixelAccumulation = LayoutSize(),
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
index ec07bc0..e7dbd747 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
@@ -588,7 +588,8 @@
     const LayoutSize& subpixelAccumulation) const {
   DCHECK(!m_geometryMapper);
   ClipRectsContext context(rootLayer, PaintingClipRects,
-                           IgnoreOverlayScrollbarSize, subpixelAccumulation);
+                           IgnorePlatformOverlayScrollbarSize,
+                           subpixelAccumulation);
   if (respectOverflowClip == IgnoreOverflowClip)
     context.setIgnoreOverflowClip();
   return getClipRects(context);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.h b/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
index 61d860f..a98c1fe 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
@@ -66,7 +66,7 @@
   ClipRectsContext(const PaintLayer* root,
                    ClipRectsCacheSlot slot,
                    OverlayScrollbarClipBehavior overlayScrollbarClipBehavior =
-                       IgnoreOverlayScrollbarSize,
+                       IgnorePlatformOverlayScrollbarSize,
                    const LayoutSize& accumulation = LayoutSize())
       : rootLayer(root),
         overlayScrollbarClipBehavior(overlayScrollbarClipBehavior),
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
index 8645e8bc..19ae3988e 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
@@ -48,7 +48,7 @@
   PaintLayer* targetPaintLayer =
       toLayoutBoxModelObject(target->layoutObject())->layer();
   ClipRectsContext context(document().layoutView()->layer(), UncachedClipRects,
-                           IgnoreOverlayScrollbarSize,
+                           IgnorePlatformOverlayScrollbarSize,
                            LayoutSize(FloatSize(0.25, 0.35)));
   // When RLS is enabled, the LayoutView will have a composited scrolling layer,
   // so don't apply an overflow clip.
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 9fc2b4d..aa63b15 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -450,23 +450,23 @@
       paintLayerForFragments->appendSingleFragmentIgnoringPagination(
           layerFragments, localPaintingInfo.rootLayer,
           localPaintingInfo.paintDirtyRect, cacheSlot, geometryMapperOption,
-          IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot,
-          localPaintingInfo.subPixelAccumulation);
+          IgnorePlatformOverlayScrollbarSize, respectOverflowClip,
+          &offsetFromRoot, localPaintingInfo.subPixelAccumulation);
     } else if (isFixedPositionObjectInPagedMedia()) {
       PaintLayerFragments singleFragment;
       paintLayerForFragments->appendSingleFragmentIgnoringPagination(
           singleFragment, localPaintingInfo.rootLayer,
           localPaintingInfo.paintDirtyRect, cacheSlot, geometryMapperOption,
-          IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot,
-          localPaintingInfo.subPixelAccumulation);
+          IgnorePlatformOverlayScrollbarSize, respectOverflowClip,
+          &offsetFromRoot, localPaintingInfo.subPixelAccumulation);
       repeatFixedPositionObjectInPages(singleFragment[0], paintingInfo,
                                        layerFragments);
     } else {
       paintLayerForFragments->collectFragments(
           layerFragments, localPaintingInfo.rootLayer,
           localPaintingInfo.paintDirtyRect, cacheSlot, geometryMapperOption,
-          IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot,
-          localPaintingInfo.subPixelAccumulation);
+          IgnorePlatformOverlayScrollbarSize, respectOverflowClip,
+          &offsetFromRoot, localPaintingInfo.subPixelAccumulation);
     }
 
     if (paintFlags & PaintLayerPaintingAncestorClippingMaskPhase) {
@@ -729,7 +729,7 @@
     // here.
     paginationLayer->collectFragments(
         layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect,
-        cacheSlot, geometryMapperOption, IgnoreOverlayScrollbarSize,
+        cacheSlot, geometryMapperOption, IgnorePlatformOverlayScrollbarSize,
         respectOverflowClip, nullptr, paintingInfo.subPixelAccumulation,
         &transformedExtent);
   }
@@ -746,7 +746,7 @@
           paintingInfo.rootLayer,
           (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects
                                                      : PaintingClipRects,
-          IgnoreOverlayScrollbarSize);
+          IgnorePlatformOverlayScrollbarSize);
       if (shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()) ==
           IgnoreOverflowClip)
         clipRectsContext.setIgnoreOverflowClip();
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 291ea69..0ffab47 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -506,7 +506,10 @@
 
   IntSize contentSize = contentsSize();
   IntSize visibleSize =
-      pixelSnappedIntRect(box().overflowClipRect(box().location())).size();
+      pixelSnappedIntRect(
+          box().overflowClipRect(box().location(),
+                                 IgnorePlatformAndCSSOverlayScrollbarSize))
+          .size();
 
   Page* page = layoutBox()->document().page();
   DCHECK(page);
@@ -1300,10 +1303,16 @@
     OverlayScrollbarClipBehavior overlayScrollbarClipBehavior) const {
   if (!hasVerticalScrollbar())
     return 0;
-  if ((verticalScrollbar()->isOverlayScrollbar() ||
-       box().style()->overflowY() == EOverflow::kOverlay) &&
-      (overlayScrollbarClipBehavior == IgnoreOverlayScrollbarSize ||
-       !verticalScrollbar()->shouldParticipateInHitTesting())) {
+  if (overlayScrollbarClipBehavior ==
+          IgnorePlatformAndCSSOverlayScrollbarSize &&
+      box().style()->overflowY() == EOverflow::kOverlay) {
+    return 0;
+  }
+  if ((overlayScrollbarClipBehavior == IgnorePlatformOverlayScrollbarSize ||
+       overlayScrollbarClipBehavior ==
+           IgnorePlatformAndCSSOverlayScrollbarSize ||
+       !verticalScrollbar()->shouldParticipateInHitTesting()) &&
+      verticalScrollbar()->isOverlayScrollbar()) {
     return 0;
   }
   return verticalScrollbar()->scrollbarThickness();
@@ -1313,10 +1322,16 @@
     OverlayScrollbarClipBehavior overlayScrollbarClipBehavior) const {
   if (!hasHorizontalScrollbar())
     return 0;
-  if ((horizontalScrollbar()->isOverlayScrollbar() ||
-       box().style()->overflowX() == EOverflow::kOverlay) &&
-      (overlayScrollbarClipBehavior == IgnoreOverlayScrollbarSize ||
-       !horizontalScrollbar()->shouldParticipateInHitTesting())) {
+  if (overlayScrollbarClipBehavior ==
+          IgnorePlatformAndCSSOverlayScrollbarSize &&
+      box().style()->overflowX() == EOverflow::kOverlay) {
+    return 0;
+  }
+  if ((overlayScrollbarClipBehavior == IgnorePlatformOverlayScrollbarSize ||
+       overlayScrollbarClipBehavior ==
+           IgnorePlatformAndCSSOverlayScrollbarSize ||
+       !horizontalScrollbar()->shouldParticipateInHitTesting()) &&
+      horizontalScrollbar()->isOverlayScrollbar()) {
     return 0;
   }
   return horizontalScrollbar()->scrollbarThickness();
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
index e444af6..af36380 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -356,9 +356,11 @@
   int pixelSnappedScrollHeight() const;
 
   int verticalScrollbarWidth(
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
   int horizontalScrollbarHeight(
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const override;
+      OverlayScrollbarClipBehavior =
+          IgnorePlatformOverlayScrollbarSize) const override;
 
   DoubleSize adjustedScrollOffset() const {
     return toDoubleSize(DoublePoint(scrollOrigin()) + m_scrollOffset);
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
index a7758783d..24a7855 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
@@ -722,4 +722,21 @@
   EXPECT_EQ(FloatSize(60, 60), overflowClip->clipRect().rect().size());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest, Preserve3DChange) {
+  setBodyInnerHTML(
+      "<div id='parent'>"
+      "  <div id='child' style='transform: translate3D(1px, 2px, 3px)'></div>"
+      "</div>");
+
+  auto* child = getLayoutObjectByElementId("child");
+  auto* transform = child->paintProperties()->transform();
+  EXPECT_TRUE(transform->flattensInheritedTransform());
+
+  document().getElementById("parent")->setAttribute(
+      HTMLNames::styleAttr, "transform-style: preserve-3d");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(transform, child->paintProperties()->transform());
+  EXPECT_FALSE(transform->flattensInheritedTransform());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index 204d91b..218cdd8 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -830,7 +830,7 @@
          (borderRightStyle() == BorderStyleNone &&
           other.borderRightStyle() == BorderStyleHidden)))
       return true;
-  } else if (display() == EDisplay::ListItem) {
+  } else if (display() == EDisplay::kListItem) {
     if (listStyleType() != other.listStyleType() ||
         listStylePosition() != other.listStylePosition())
       return true;
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index aedafd4..2dfd875 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -851,7 +851,7 @@
   void setContent(ContentData*);
 
   // display
-  static EDisplay initialDisplay() { return EDisplay::Inline; }
+  static EDisplay initialDisplay() { return EDisplay::kInline; }
   EDisplay display() const {
     return static_cast<EDisplay>(m_nonInheritedData.m_effectiveDisplay);
   }
@@ -3570,39 +3570,42 @@
   }
 
   static bool isDisplayBlockContainer(EDisplay display) {
-    return display == EDisplay::Block || display == EDisplay::ListItem ||
-           display == EDisplay::InlineBlock || display == EDisplay::FlowRoot ||
-           display == EDisplay::TableCell || display == EDisplay::TableCaption;
+    return display == EDisplay::kBlock || display == EDisplay::kListItem ||
+           display == EDisplay::kInlineBlock ||
+           display == EDisplay::kFlowRoot || display == EDisplay::kTableCell ||
+           display == EDisplay::kTableCaption;
   }
 
   static bool isDisplayFlexibleBox(EDisplay display) {
-    return display == EDisplay::Flex || display == EDisplay::InlineFlex;
+    return display == EDisplay::kFlex || display == EDisplay::kInlineFlex;
   }
 
   static bool isDisplayGridBox(EDisplay display) {
-    return display == EDisplay::Grid || display == EDisplay::InlineGrid;
+    return display == EDisplay::kGrid || display == EDisplay::kInlineGrid;
   }
 
   static bool isDisplayReplacedType(EDisplay display) {
-    return display == EDisplay::InlineBlock ||
-           display == EDisplay::WebkitInlineBox ||
-           display == EDisplay::InlineFlex ||
-           display == EDisplay::InlineTable || display == EDisplay::InlineGrid;
+    return display == EDisplay::kInlineBlock ||
+           display == EDisplay::kWebkitInlineBox ||
+           display == EDisplay::kInlineFlex ||
+           display == EDisplay::kInlineTable ||
+           display == EDisplay::kInlineGrid;
   }
 
   static bool isDisplayInlineType(EDisplay display) {
-    return display == EDisplay::Inline || isDisplayReplacedType(display);
+    return display == EDisplay::kInline || isDisplayReplacedType(display);
   }
 
   static bool isDisplayTableType(EDisplay display) {
-    return display == EDisplay::Table || display == EDisplay::InlineTable ||
-           display == EDisplay::TableRowGroup ||
-           display == EDisplay::TableHeaderGroup ||
-           display == EDisplay::TableFooterGroup ||
-           display == EDisplay::TableRow ||
-           display == EDisplay::TableColumnGroup ||
-           display == EDisplay::TableColumn || display == EDisplay::TableCell ||
-           display == EDisplay::TableCaption;
+    return display == EDisplay::kTable || display == EDisplay::kInlineTable ||
+           display == EDisplay::kTableRowGroup ||
+           display == EDisplay::kTableHeaderGroup ||
+           display == EDisplay::kTableFooterGroup ||
+           display == EDisplay::kTableRow ||
+           display == EDisplay::kTableColumnGroup ||
+           display == EDisplay::kTableColumn ||
+           display == EDisplay::kTableCell ||
+           display == EDisplay::kTableCaption;
   }
 
   // Color accessors are all private to make sure callers use
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
index 1175eed8..e16a15c 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -335,29 +335,29 @@
 };
 
 enum class EDisplay : unsigned {
-  Inline,
-  Block,
-  ListItem,
-  InlineBlock,
-  Table,
-  InlineTable,
-  TableRowGroup,
-  TableHeaderGroup,
-  TableFooterGroup,
-  TableRow,
-  TableColumnGroup,
-  TableColumn,
-  TableCell,
-  TableCaption,
-  WebkitBox,
-  WebkitInlineBox,
-  Flex,
-  InlineFlex,
-  Grid,
-  InlineGrid,
-  Contents,
-  FlowRoot,
-  None
+  kInline,
+  kBlock,
+  kListItem,
+  kInlineBlock,
+  kTable,
+  kInlineTable,
+  kTableRowGroup,
+  kTableHeaderGroup,
+  kTableFooterGroup,
+  kTableRow,
+  kTableColumnGroup,
+  kTableColumn,
+  kTableCell,
+  kTableCaption,
+  kWebkitBox,
+  kWebkitInlineBox,
+  kFlex,
+  kInlineFlex,
+  kGrid,
+  kInlineGrid,
+  kContents,
+  kFlowRoot,
+  kNone
 };
 
 enum class EInsideLink : unsigned {
diff --git a/third_party/WebKit/Source/core/svg/SVGGElement.cpp b/third_party/WebKit/Source/core/svg/SVGGElement.cpp
index 1d5541a..bf029b04c 100644
--- a/third_party/WebKit/Source/core/svg/SVGGElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGGElement.cpp
@@ -38,7 +38,7 @@
   // We still have to create layoutObjects for the <g> & <linearGradient>
   // element, though the subtree may be hidden - we only want the resource
   // layoutObjects to exist so they can be referenced from somewhere else.
-  if (style.display() == EDisplay::None)
+  if (style.display() == EDisplay::kNone)
     return new LayoutSVGHiddenContainer(this);
 
   return new LayoutSVGTransformableContainer(this);
diff --git a/third_party/WebKit/Source/devtools/front_end/Tests.js b/third_party/WebKit/Source/devtools/front_end/Tests.js
index 32107ca..e1c8f01 100644
--- a/third_party/WebKit/Source/devtools/front_end/Tests.js
+++ b/third_party/WebKit/Source/devtools/front_end/Tests.js
@@ -514,22 +514,19 @@
   TestSuite.prototype.testConsoleOnNavigateBack = function() {
 
     function filteredMessages() {
-      return SDK.multitargetConsoleModel.messages().filter(
-          a => a.source !== SDK.ConsoleMessage.MessageSource.Violation);
+      return SDK.consoleModel.messages().filter(a => a.source !== SDK.ConsoleMessage.MessageSource.Violation);
     }
 
-    if (filteredMessages().length === 1) {
+    if (filteredMessages().length === 1)
       firstConsoleMessageReceived.call(this, null);
-    } else {
-      SDK.multitargetConsoleModel.addEventListener(
-          SDK.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
-    }
+    else
+      SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
+
 
     function firstConsoleMessageReceived(event) {
       if (event && event.data.source === SDK.ConsoleMessage.MessageSource.Violation)
         return;
-      SDK.multitargetConsoleModel.removeEventListener(
-          SDK.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
+      SDK.consoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
       this.evaluateInConsole_('clickLink();', didClickLink.bind(this));
     }
 
@@ -685,12 +682,12 @@
 
         messages.splice(index, 1);
         if (!messages.length) {
-          SDK.multitargetConsoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
+          SDK.consoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
           next();
         }
       }
 
-      SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
+      SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
       SDK.multitargetNetworkManager.setNetworkConditions(preset);
     }
 
@@ -835,7 +832,7 @@
   };
 
   TestSuite.prototype.testWindowInitializedOnNavigateBack = function() {
-    var messages = SDK.multitargetConsoleModel.messages();
+    var messages = SDK.consoleModel.messages();
     this.assertEquals(1, messages.length);
     var text = messages[0].messageText;
     if (text.indexOf('Uncaught') !== -1)
@@ -870,7 +867,7 @@
   };
 
   TestSuite.prototype.waitForTestResultsInConsole = function() {
-    var messages = SDK.multitargetConsoleModel.messages();
+    var messages = SDK.consoleModel.messages();
     for (var i = 0; i < messages.length; ++i) {
       var text = messages[i].messageText;
       if (text === 'PASS')
@@ -887,7 +884,7 @@
         this.fail(text);
     }
 
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
     this.takeControl();
   };
 
@@ -931,12 +928,12 @@
         Array.prototype.slice.call(arguments, 1, -1).map(arg => JSON.stringify(arg)).join(',') + ',';
     this.evaluateInConsole_(
         `${functionName}(${argsString} function() { console.log('${doneMessage}'); });`, function() {});
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage);
 
     function onConsoleMessage(event) {
       var text = event.data.messageText;
       if (text === doneMessage) {
-        SDK.multitargetConsoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage);
+        SDK.consoleModel.removeEventListener(SDK.ConsoleModel.Events.MessageAdded, onConsoleMessage);
         callback();
       }
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js b/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js
index 591ba99..cb1e587 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js
@@ -44,10 +44,9 @@
     /** @type {!Array.<!Bindings.PresentationConsoleMessage>} */
     this._presentationConsoleMessages = [];
 
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
-    SDK.multitargetConsoleModel.addEventListener(
-        SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
-    SDK.multitargetConsoleModel.messages().forEach(this._consoleMessageAdded, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
+    SDK.consoleModel.messages().forEach(this._consoleMessageAdded, this);
     // TODO(dgozman): setImmediate because we race with DebuggerWorkspaceBinding on ParsedScriptSource event delivery.
     SDK.targetManager.addModelListener(
         SDK.DebuggerModel, SDK.DebuggerModel.Events.ParsedScriptSource,
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js b/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
index caa0df9..0704bbc 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
@@ -191,7 +191,7 @@
     this.setText('');
     var currentExecutionContext = UI.context.flavor(SDK.ExecutionContext);
     if (currentExecutionContext) {
-      SDK.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, text, useCommandLineAPI);
+      SDK.consoleModel.evaluateCommandInConsole(currentExecutionContext, text, useCommandLineAPI);
       if (Console.ConsolePanel.instance().isShowing())
         Host.userMetrics.actionTaken(Host.UserMetrics.Action.CommandEvaluatedInConsolePanel);
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
index 3ca49b0..12af176 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -182,8 +182,7 @@
   }
 
   static clearConsole() {
-    for (var target of SDK.targetManager.targets())
-      target.consoleModel.requestClearMessages();
+    SDK.consoleModel.requestClearMessages();
   }
 
   /**
@@ -226,14 +225,11 @@
   }
 
   _fetchMultitargetMessages() {
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
-    SDK.multitargetConsoleModel.addEventListener(
-        SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
-    SDK.multitargetConsoleModel.addEventListener(
-        SDK.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this);
-    SDK.multitargetConsoleModel.addEventListener(
-        SDK.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
-    SDK.multitargetConsoleModel.messages().forEach(this._addConsoleMessage, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
+    SDK.consoleModel.messages().forEach(this._addConsoleMessage, this);
     this._viewport.invalidate();
   }
 
@@ -806,7 +802,7 @@
           result.target(), exceptionDetails, SDK.ConsoleMessage.MessageType.Result, undefined, undefined);
     }
     message.setOriginatingMessage(originatingConsoleMessage);
-    result.target().consoleModel.addMessage(message);
+    SDK.consoleModel.addMessage(message);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 10ca4c15..bfa3748 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -165,7 +165,7 @@
     UI.ContextMenu.installHandler(document);
     UI.Tooltip.installHandler(document);
     Components.dockController = new Components.DockController(canDock);
-    SDK.multitargetConsoleModel = new SDK.MultitargetConsoleModel();
+    SDK.consoleModel = new SDK.ConsoleModel();
     SDK.multitargetNetworkManager = new SDK.MultitargetNetworkManager();
     SDK.targetManager.addEventListener(
         SDK.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged.bind(this));
@@ -602,9 +602,9 @@
     this._warnings = this._createItem(shadowRoot, 'smallicon-warning');
     this._titles = [];
 
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._update, this);
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, this._update, this);
-    SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.MessageUpdated, this._update, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._update, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, this._update, this);
+    SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageUpdated, this._update, this);
     this._update();
   }
 
@@ -637,13 +637,8 @@
   }
 
   _update() {
-    var errors = 0;
-    var warnings = 0;
-    var targets = SDK.targetManager.targets();
-    for (var i = 0; i < targets.length; ++i) {
-      errors += targets[i].consoleModel.errors();
-      warnings += targets[i].consoleModel.warnings();
-    }
+    var errors = SDK.consoleModel.errors();
+    var warnings = SDK.consoleModel.warnings();
 
     this._titles = [];
     this._toolbarItem.setVisible(!!(errors || warnings));
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
index 960b93b..2001eb2 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -86,7 +86,7 @@
         columnConfig.titleDOMFragment = this._makeHeaderFragment(columnConfig.title, columnConfig.subtitle);
       this._columns.push(columnConfig);
     }
-    this._loadColumns();
+    this._loadCustomColumnsAndSettings();
 
     this._popoverHelper = new UI.PopoverHelper(this._networkLogView.element);
     this._popoverHelper.initializeCallbacks(
@@ -330,13 +330,13 @@
    * @param {!Network.NetworkLogViewColumns.Descriptor} columnConfig
    */
   _toggleColumnVisibility(columnConfig) {
-    this._loadColumns();
+    this._loadCustomColumnsAndSettings();
     columnConfig.visible = !columnConfig.visible;
-    this._saveColumns();
+    this._saveColumnsSettings();
     this._updateColumns();
   }
 
-  _saveColumns() {
+  _saveColumnsSettings() {
     var saveableSettings = {};
     for (var columnConfig of this._columns)
       saveableSettings[columnConfig.id] = {visible: columnConfig.visible, title: columnConfig.title};
@@ -344,7 +344,7 @@
     this._persistantSettings.set(saveableSettings);
   }
 
-  _loadColumns() {
+  _loadCustomColumnsAndSettings() {
     var savedSettings = this._persistantSettings.get();
     var columnIds = Object.keys(savedSettings);
     for (var columnId of columnIds) {
@@ -461,7 +461,7 @@
       return false;
     this._columns.splice(index, 1);
     this._dataGrid.removeColumn(headerId);
-    this._saveColumns();
+    this._saveColumnsSettings();
     this._updateColumns();
     return true;
   }
@@ -494,7 +494,7 @@
     this._columns.splice(index, 0, columnConfig);
     if (this._dataGrid)
       this._dataGrid.addColumn(Network.NetworkLogViewColumns._convertToDataGridDescriptor(columnConfig), index);
-    this._saveColumns();
+    this._saveColumnsSettings();
     this._updateColumns();
     return columnConfig;
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
index 167cb26..f9e5681 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
@@ -29,52 +29,76 @@
  */
 
 /**
- * @unrestricted
+ * @implements {SDK.TargetManager.Observer}
  */
-SDK.ConsoleModel = class extends SDK.SDKModel {
-  /**
-   * @param {!SDK.Target} target
-   */
-  constructor(target) {
-    super(target);
+SDK.ConsoleModel = class extends Common.Object {
+  constructor() {
+    super();
 
     /** @type {!Array.<!SDK.ConsoleMessage>} */
     this._messages = [];
-    /** @type {!Map<number, !SDK.ConsoleMessage>} */
+    /** @type {!Map<!SDK.Target, !Map<number, !SDK.ConsoleMessage>>} */
     this._messageByExceptionId = new Map();
     this._warnings = 0;
     this._errors = 0;
 
+    SDK.targetManager.observeTargets(this);
+  }
+
+  /**
+   * @override
+   * @param {!SDK.Target} target
+   */
+  targetAdded(target) {
+    var eventListeners = [];
+
     var logModel = target.model(SDK.LogModel);
     if (logModel)
-      logModel.on(SDK.LogModel.EntryAddedEvent, this._logEntryAdded, this);
+      eventListeners.push(logModel.on(SDK.LogModel.EntryAddedEvent, this._logEntryAdded, this));
 
     var cpuProfilerModel = target.model(SDK.CPUProfilerModel);
     if (cpuProfilerModel) {
-      cpuProfilerModel.addEventListener(
-          SDK.CPUProfilerModel.Events.ConsoleProfileStarted, this._consoleProfileStarted, this);
-      cpuProfilerModel.addEventListener(
-          SDK.CPUProfilerModel.Events.ConsoleProfileFinished, this._consoleProfileFinished, this);
+      eventListeners.push(cpuProfilerModel.addEventListener(
+          SDK.CPUProfilerModel.Events.ConsoleProfileStarted, this._consoleProfileStarted.bind(this, cpuProfilerModel)));
+      eventListeners.push(cpuProfilerModel.addEventListener(
+          SDK.CPUProfilerModel.Events.ConsoleProfileFinished,
+          this._consoleProfileFinished.bind(this, cpuProfilerModel)));
     }
 
     var resourceTreeModel = target.model(SDK.ResourceTreeModel);
     if (resourceTreeModel) {
-      resourceTreeModel.addEventListener(
-          SDK.ResourceTreeModel.Events.MainFrameStartedLoading, this._mainFrameStartedLoading, this);
-      resourceTreeModel.addEventListener(
-          SDK.ResourceTreeModel.Events.MainFrameNavigated, this._mainFrameNavigated, this);
+      eventListeners.push(resourceTreeModel.addEventListener(
+          SDK.ResourceTreeModel.Events.MainFrameStartedLoading, this._mainFrameStartedLoading, this));
+      eventListeners.push(resourceTreeModel.addEventListener(
+          SDK.ResourceTreeModel.Events.MainFrameNavigated, this._mainFrameNavigated, this));
     }
 
     var runtimeModel = target.model(SDK.RuntimeModel);
     if (runtimeModel) {
-      runtimeModel.addEventListener(SDK.RuntimeModel.Events.ExceptionThrown, this._exceptionThrown, this);
-      runtimeModel.addEventListener(SDK.RuntimeModel.Events.ExceptionRevoked, this._exceptionRevoked, this);
-      runtimeModel.addEventListener(SDK.RuntimeModel.Events.ConsoleAPICalled, this._consoleAPICalled, this);
+      eventListeners.push(runtimeModel.addEventListener(
+          SDK.RuntimeModel.Events.ExceptionThrown, this._exceptionThrown.bind(this, runtimeModel)));
+      eventListeners.push(runtimeModel.addEventListener(
+          SDK.RuntimeModel.Events.ExceptionRevoked, this._exceptionRevoked.bind(this, runtimeModel)));
+      eventListeners.push(runtimeModel.addEventListener(
+          SDK.RuntimeModel.Events.ConsoleAPICalled, this._consoleAPICalled.bind(this, runtimeModel)));
     }
 
     var networkManager = target.model(SDK.NetworkManager);
-    if (networkManager)
-      networkManager.addEventListener(SDK.NetworkManager.Events.WarningGenerated, this._networkWarningGenerated, this);
+    if (networkManager) {
+      eventListeners.push(networkManager.addEventListener(
+          SDK.NetworkManager.Events.WarningGenerated, this._networkWarningGenerated.bind(this, networkManager)));
+    }
+
+    target[SDK.ConsoleModel._events] = eventListeners;
+  }
+
+  /**
+   * @override
+   * @param {!SDK.Target} target
+   */
+  targetRemoved(target) {
+    this._messageByExceptionId.delete(target);
+    Common.EventTarget.removeEventListeners(target[SDK.ConsoleModel._events]);
   }
 
   /**
@@ -82,29 +106,29 @@
    * @param {string} text
    * @param {boolean} useCommandLineAPI
    */
-  static evaluateCommandInConsole(executionContext, text, useCommandLineAPI) {
+  evaluateCommandInConsole(executionContext, text, useCommandLineAPI) {
     var target = executionContext.target();
     var requestedText = text;
 
     var commandMessage = new SDK.ConsoleMessage(
         target, SDK.ConsoleMessage.MessageSource.JS, null, text, SDK.ConsoleMessage.MessageType.Command);
     commandMessage.setExecutionContextId(executionContext.id);
-    target.consoleModel.addMessage(commandMessage);
+    this.addMessage(commandMessage);
 
     /**
      * @param {?SDK.RemoteObject} result
      * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails
+     * @this {SDK.ConsoleModel}
      */
     function printResult(result, exceptionDetails) {
       if (!result)
         return;
 
-      Common.console.showPromise().then(reportUponEvaluation);
-      function reportUponEvaluation() {
-        target.consoleModel.dispatchEventToListeners(
+      Common.console.showPromise().then(() => {
+        this.dispatchEventToListeners(
             SDK.ConsoleModel.Events.CommandEvaluated,
             {result: result, text: requestedText, commandMessage: commandMessage, exceptionDetails: exceptionDetails});
-      }
+      });
     }
 
     /**
@@ -133,7 +157,7 @@
     if (looksLikeAnObjectLiteral(text))
       text = '(' + text + ')';
 
-    executionContext.evaluate(text, 'console', useCommandLineAPI, false, false, true, true, printResult);
+    executionContext.evaluate(text, 'console', useCommandLineAPI, false, false, true, true, printResult.bind(this));
     Host.userMetrics.actionTaken(Host.UserMetrics.Action.ConsoleEvaluated);
   }
 
@@ -145,11 +169,20 @@
       return;
 
     if (msg.source === SDK.ConsoleMessage.MessageSource.ConsoleAPI && msg.type === SDK.ConsoleMessage.MessageType.Clear)
-      this.clear();
+      this._clear();
 
     this._messages.push(msg);
-    if (msg._exceptionId)
-      this._messageByExceptionId.set(msg._exceptionId, msg);
+    if (msg._exceptionId && msg.target()) {
+      // TODO(dgozman): make target non-nullable, as we only have messages without a target
+      // internally in ConsoleView.
+      var target = /** @type {!SDK.Target} */ (msg.target());
+      var targetMap = this._messageByExceptionId.get(target);
+      if (!targetMap) {
+        targetMap = new Map();
+        this._messageByExceptionId.set(target, targetMap);
+      }
+      targetMap.set(msg._exceptionId, msg);
+    }
     this._incrementErrorWarningCount(msg);
     this.dispatchEventToListeners(SDK.ConsoleModel.Events.MessageAdded, msg);
   }
@@ -159,29 +192,32 @@
    */
   _logEntryAdded(event) {
     var consoleMessage = new SDK.ConsoleMessage(
-        this.target(), event.entry.source, event.entry.level, event.entry.text, undefined, event.entry.url,
+        event.logModel.target(), event.entry.source, event.entry.level, event.entry.text, undefined, event.entry.url,
         event.entry.lineNumber, undefined, event.entry.networkRequestId, undefined, event.entry.stackTrace,
         event.entry.timestamp, undefined, undefined, event.entry.workerId);
     this.addMessage(consoleMessage);
   }
 
   /**
+   * @param {!SDK.RuntimeModel} runtimeModel
    * @param {!Common.Event} event
    */
-  _exceptionThrown(event) {
+  _exceptionThrown(runtimeModel, event) {
     var exceptionWithTimestamp = /** @type {!SDK.RuntimeModel.ExceptionWithTimestamp} */ (event.data);
     var consoleMessage = SDK.ConsoleMessage.fromException(
-        this.target(), exceptionWithTimestamp.details, undefined, exceptionWithTimestamp.timestamp, undefined);
+        runtimeModel.target(), exceptionWithTimestamp.details, undefined, exceptionWithTimestamp.timestamp, undefined);
     consoleMessage.setExceptionId(exceptionWithTimestamp.details.exceptionId);
     this.addMessage(consoleMessage);
   }
 
   /**
+   * @param {!SDK.RuntimeModel} runtimeModel
    * @param {!Common.Event} event
    */
-  _exceptionRevoked(event) {
+  _exceptionRevoked(runtimeModel, event) {
     var exceptionId = /** @type {number} */ (event.data);
-    var exceptionMessage = this._messageByExceptionId.get(exceptionId);
+    var targetMap = this._messageByExceptionId.get(runtimeModel.target());
+    var exceptionMessage = targetMap ? targetMap.get(exceptionId) : null;
     if (!exceptionMessage)
       return;
     this._errors--;
@@ -190,9 +226,10 @@
   }
 
   /**
+   * @param {!SDK.RuntimeModel} runtimeModel
    * @param {!Common.Event} event
    */
-  _consoleAPICalled(event) {
+  _consoleAPICalled(runtimeModel, event) {
     var call = /** @type {!SDK.RuntimeModel.ConsoleAPICall} */ (event.data);
     var level = SDK.ConsoleMessage.MessageLevel.Info;
     if (call.type === SDK.ConsoleMessage.MessageType.Debug)
@@ -210,7 +247,7 @@
       message = call.args[0].description;
     var callFrame = call.stackTrace && call.stackTrace.callFrames.length ? call.stackTrace.callFrames[0] : null;
     var consoleMessage = new SDK.ConsoleMessage(
-        this.target(), SDK.ConsoleMessage.MessageSource.ConsoleAPI, level,
+        runtimeModel.target(), SDK.ConsoleMessage.MessageSource.ConsoleAPI, level,
         /** @type {string} */ (message), call.type, callFrame ? callFrame.url : undefined,
         callFrame ? callFrame.lineNumber : undefined, callFrame ? callFrame.columnNumber : undefined, undefined,
         call.args, call.stackTrace, call.timestamp, call.executionContextId, undefined);
@@ -222,7 +259,7 @@
    */
   _mainFrameStartedLoading(event) {
     if (!Common.moduleSetting('preserveConsoleLog').get())
-      this.clear();
+      this._clear();
   }
 
   /**
@@ -234,31 +271,34 @@
   }
 
   /**
+   * @param {!SDK.CPUProfilerModel} cpuProfilerModel
    * @param {!Common.Event} event
    */
-  _consoleProfileStarted(event) {
+  _consoleProfileStarted(cpuProfilerModel, event) {
     var data = /** @type {!SDK.CPUProfilerModel.EventData} */ (event.data);
     this._addConsoleProfileMessage(
-        SDK.ConsoleMessage.MessageType.Profile, data.scriptLocation,
+        cpuProfilerModel, SDK.ConsoleMessage.MessageType.Profile, data.scriptLocation,
         Common.UIString('Profile \'%s\' started.', data.title));
   }
 
   /**
+   * @param {!SDK.CPUProfilerModel} cpuProfilerModel
    * @param {!Common.Event} event
    */
-  _consoleProfileFinished(event) {
+  _consoleProfileFinished(cpuProfilerModel, event) {
     var data = /** @type {!SDK.CPUProfilerModel.EventData} */ (event.data);
     this._addConsoleProfileMessage(
-        SDK.ConsoleMessage.MessageType.ProfileEnd, data.scriptLocation,
+        cpuProfilerModel, SDK.ConsoleMessage.MessageType.ProfileEnd, data.scriptLocation,
         Common.UIString('Profile \'%s\' finished.', data.title));
   }
 
   /**
+   * @param {!SDK.CPUProfilerModel} cpuProfilerModel
    * @param {string} type
    * @param {!SDK.DebuggerModel.Location} scriptLocation
    * @param {string} messageText
    */
-  _addConsoleProfileMessage(type, scriptLocation, messageText) {
+  _addConsoleProfileMessage(cpuProfilerModel, type, scriptLocation, messageText) {
     var stackTrace = [{
       functionName: '',
       scriptId: scriptLocation.scriptId,
@@ -267,17 +307,18 @@
       columnNumber: scriptLocation.columnNumber || 0
     }];
     this.addMessage(new SDK.ConsoleMessage(
-        this.target(), SDK.ConsoleMessage.MessageSource.ConsoleAPI, SDK.ConsoleMessage.MessageLevel.Info, messageText,
-        type, undefined, undefined, undefined, undefined, stackTrace));
+        cpuProfilerModel.target(), SDK.ConsoleMessage.MessageSource.ConsoleAPI, SDK.ConsoleMessage.MessageLevel.Info,
+        messageText, type, undefined, undefined, undefined, undefined, stackTrace));
   }
 
   /**
+   * @param {!SDK.NetworkManager} networkManager
    * @param {!Common.Event} event
    */
-  _networkWarningGenerated(event) {
+  _networkWarningGenerated(networkManager, event) {
     var warning = /** @type {!SDK.NetworkManager.Warning} */ (event.data);
     this.addMessage(new SDK.ConsoleMessage(
-        this.target(), SDK.ConsoleMessage.MessageSource.Network, SDK.ConsoleMessage.MessageLevel.Warning,
+        networkManager.target(), SDK.ConsoleMessage.MessageSource.Network, SDK.ConsoleMessage.MessageLevel.Warning,
         warning.message, undefined, undefined, undefined, undefined, warning.requestId));
   }
 
@@ -305,17 +346,16 @@
   }
 
   requestClearMessages() {
-    var logModel = this.target().model(SDK.LogModel);
-    if (logModel)
+    for (var logModel of SDK.targetManager.models(SDK.LogModel))
       logModel.requestClear();
-    this.clear();
-    this.target().runtimeModel.discardConsoleEntries();
+    for (var runtimeModel of SDK.targetManager.models(SDK.RuntimeModel))
+      runtimeModel.discardConsoleEntries();
+    this._clear();
   }
 
-  clear() {
+  _clear() {
     this._messages = [];
     this._messageByExceptionId.clear();
-    // TODO(dgozman): clear exceptions and console api entries in runtimeModel.
     this._errors = 0;
     this._warnings = 0;
     this.dispatchEventToListeners(SDK.ConsoleModel.Events.ConsoleCleared);
@@ -336,8 +376,6 @@
   }
 };
 
-SDK.SDKModel.register(SDK.ConsoleModel, SDK.Target.Capability.None);
-
 /** @enum {symbol} */
 SDK.ConsoleModel.Events = {
   ConsoleCleared: Symbol('ConsoleCleared'),
@@ -632,82 +670,9 @@
   return 3;
 };
 
-/**
- * @implements {SDK.TargetManager.Observer}
- * @unrestricted
- */
-SDK.MultitargetConsoleModel = class extends Common.Object {
-  constructor() {
-    super();
-    SDK.targetManager.observeTargets(this);
-    SDK.targetManager.addModelListener(
-        SDK.ConsoleModel, SDK.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
-    SDK.targetManager.addModelListener(
-        SDK.ConsoleModel, SDK.ConsoleModel.Events.MessageUpdated, this._consoleMessageUpdated, this);
-    SDK.targetManager.addModelListener(
-        SDK.ConsoleModel, SDK.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
-  }
-
-  /**
-   * @override
-   * @param {!SDK.Target} target
-   */
-  targetAdded(target) {
-    if (!this._mainTarget) {
-      this._mainTarget = target;
-      target.consoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
-    }
-  }
-
-  /**
-   * @override
-   * @param {!SDK.Target} target
-   */
-  targetRemoved(target) {
-    if (this._mainTarget === target) {
-      delete this._mainTarget;
-      target.consoleModel.removeEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
-    }
-  }
-
-  /**
-   * @return {!Array.<!SDK.ConsoleMessage>}
-   */
-  messages() {
-    var targets = SDK.targetManager.targets();
-    var result = [];
-    for (var i = 0; i < targets.length; ++i)
-      result = result.concat(targets[i].consoleModel.messages());
-    return result;
-  }
-
-  _consoleCleared() {
-    this.dispatchEventToListeners(SDK.ConsoleModel.Events.ConsoleCleared);
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _consoleMessageAdded(event) {
-    this.dispatchEventToListeners(SDK.ConsoleModel.Events.MessageAdded, event.data);
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _consoleMessageUpdated(event) {
-    this.dispatchEventToListeners(SDK.ConsoleModel.Events.MessageUpdated, event.data);
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _commandEvaluated(event) {
-    this.dispatchEventToListeners(SDK.ConsoleModel.Events.CommandEvaluated, event.data);
-  }
-};
+SDK.ConsoleModel._events = Symbol('SDK.ConsoleModel.events');
 
 /**
- * @type {!SDK.MultitargetConsoleModel}
+ * @type {!SDK.ConsoleModel}
  */
-SDK.multitargetConsoleModel;
+SDK.consoleModel;
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/LogModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/LogModel.js
index 57b1bdd..66dabf7d 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/LogModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/LogModel.js
@@ -28,7 +28,7 @@
    * @param {!Protocol.Log.LogEntry} payload
    */
   entryAdded(payload) {
-    this.emit(new SDK.LogModel.EntryAddedEvent(payload));
+    this.emit(new SDK.LogModel.EntryAddedEvent(this, payload));
   }
 
   requestClear() {
@@ -41,9 +41,11 @@
 /** @implements {Common.Emittable} */
 SDK.LogModel.EntryAddedEvent = class {
   /**
+   * @param {!SDK.LogModel} logModel
    * @param {!Protocol.Log.LogEntry} entry
    */
-  constructor(entry) {
+  constructor(logModel, entry) {
+    this.logModel = logModel;
     this.entry = entry;
   }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
index c40b739e..25eb04cd 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
@@ -253,8 +253,6 @@
     target.runtimeModel = /** @type {!SDK.RuntimeModel} */ (target.model(SDK.RuntimeModel));
     target.model(SDK.DebuggerModel);
     target.model(SDK.LogModel);
-    /** @type {!SDK.ConsoleModel} */
-    target.consoleModel = /** @type {!SDK.ConsoleModel} */ (target.model(SDK.ConsoleModel));
     target.model(SDK.DOMModel);
     target.model(SDK.CSSModel);
     target.model(SDK.CPUProfilerModel);
diff --git a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
index ca8b6ca..c25cf7c 100644
--- a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
@@ -278,7 +278,7 @@
     var consoleMessage = new SDK.ConsoleMessage(
         target, SDK.ConsoleMessage.MessageSource.JS, SDK.ConsoleMessage.MessageLevel.Info, '', undefined, sourceURL,
         undefined, undefined, undefined, [result], undefined, undefined, undefined, scriptId);
-    target.consoleModel.addMessage(consoleMessage);
+    SDK.consoleModel.addMessage(consoleMessage);
   }
 
   /**
@@ -287,7 +287,7 @@
    * @param {?string=} sourceURL
    */
   _printRunOrCompileScriptResultFailure(target, exceptionDetails, sourceURL) {
-    target.consoleModel.addMessage(
+    SDK.consoleModel.addMessage(
         SDK.ConsoleMessage.fromException(target, exceptionDetails, undefined, undefined, sourceURL || undefined));
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
index 735c0da..695c0a1 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
@@ -956,7 +956,7 @@
       if (wasThrown || !result || result.type !== 'string') {
         failedToSave(result);
       } else {
-        SDK.ConsoleModel.evaluateCommandInConsole(
+        SDK.consoleModel.evaluateCommandInConsole(
             /** @type {!SDK.ExecutionContext} */ (currentExecutionContext), result.value,
             /* useCommandLineAPI */ false);
       }
@@ -1268,7 +1268,7 @@
           var text = frame.textEditor.text(frame.textEditor.selection());
           var executionContext = UI.context.flavor(SDK.ExecutionContext);
           if (executionContext)
-            SDK.ConsoleModel.evaluateCommandInConsole(executionContext, text, /* useCommandLineAPI */ true);
+            SDK.consoleModel.evaluateCommandInConsole(executionContext, text, /* useCommandLineAPI */ true);
         }
         return true;
     }
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
index caf7682..36bad05 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -731,7 +731,7 @@
     if (node->isConnected() && node->isElementNode()) {
       RefPtr<ComputedStyle> style =
           document->ensureStyleResolver().styleForElement(toElement(node));
-      return style->display() == EDisplay::None ||
+      return style->display() == EDisplay::kNone ||
              style->visibility() != EVisibility::kVisible;
     }
   }
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
index 8e1b0cb9..1ff409c 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
@@ -50,7 +50,7 @@
     [CallWith=ScriptState] MediaStreamTrack clone();
     [ImplementedAs=stopTrack, RaisesException] void stop();
 
-    [RuntimeEnabled=ImageCapture] MediaTrackCapabilities getCapabilities();
+    [RuntimeEnabled=MediaTrackCapabilities] MediaTrackCapabilities getCapabilities();
     [RuntimeEnabled=MediaConstraints] MediaTrackConstraints getConstraints();
     [RuntimeEnabled=MediaGetSettings] MediaTrackSettings getSettings();
 };
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 95748be8..cc882ac 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -541,6 +541,10 @@
       name: "MediaGetSettings",
       status: "stable",
     },
+    {
+      name: "MediaQueryShape",
+      status: "experimental",
+    },
     // MediaSession is enabled by default on Android only.
     // TODO(rbyers): Add parameter to specify platform.
     {
@@ -560,11 +564,12 @@
       status: "experimental",
     },
     {
-      name: "MediaQueryShape",
+      name: "MediaStreamTrackContentHint",
       status: "experimental",
     },
     {
-      name: "MediaStreamTrackContentHint",
+      name: "MediaTrackCapabilities",
+      implied_by: ["ImageCapture"],
       status: "experimental",
     },
     {
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollTypes.h b/third_party/WebKit/Source/platform/scroll/ScrollTypes.h
index 32d22c8..b7e117f5 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollTypes.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollTypes.h
@@ -43,8 +43,12 @@
   return ScrollOffset(p.x(), p.y());
 }
 
+// Platform overlay scrollbars are controlled and painted by the operating
+// system (e.g., OSX and Android).  CSS overlay scrollbars are created by
+// setting overflow:overlay, and they are painted by chromium.
 enum OverlayScrollbarClipBehavior {
-  IgnoreOverlayScrollbarSize,
+  IgnorePlatformOverlayScrollbarSize,
+  IgnorePlatformAndCSSOverlayScrollbarSize,
   ExcludeOverlayScrollbarSizeForHitTesting
 };
 
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp b/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
index 9beeba9..3991c98 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
@@ -635,7 +635,7 @@
 
 int ScrollableArea::verticalScrollbarWidth(
     OverlayScrollbarClipBehavior behavior) const {
-  DCHECK_EQ(behavior, IgnoreOverlayScrollbarSize);
+  DCHECK_EQ(behavior, IgnorePlatformOverlayScrollbarSize);
   if (Scrollbar* verticalBar = verticalScrollbar())
     return !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
   return 0;
@@ -643,7 +643,7 @@
 
 int ScrollableArea::horizontalScrollbarHeight(
     OverlayScrollbarClipBehavior behavior) const {
-  DCHECK_EQ(behavior, IgnoreOverlayScrollbarSize);
+  DCHECK_EQ(behavior, IgnorePlatformOverlayScrollbarSize);
   if (Scrollbar* horizontalBar = horizontalScrollbar())
     return !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
   return 0;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
index 5c74eff..a284ce6 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
@@ -335,9 +335,9 @@
   IntSize excludeScrollbars(const IntSize&) const;
 
   virtual int verticalScrollbarWidth(
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const;
+      OverlayScrollbarClipBehavior = IgnorePlatformOverlayScrollbarSize) const;
   virtual int horizontalScrollbarHeight(
-      OverlayScrollbarClipBehavior = IgnoreOverlayScrollbarSize) const;
+      OverlayScrollbarClipBehavior = IgnorePlatformOverlayScrollbarSize) const;
 
   // Returns the widget associated with this ScrollableArea.
   virtual FrameViewBase* getFrameViewBase() { return nullptr; }
diff --git a/third_party/WebKit/Source/platform/wtf/AddressSanitizer.h b/third_party/WebKit/Source/platform/wtf/AddressSanitizer.h
new file mode 100644
index 0000000..5fd627a
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/AddressSanitizer.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WTF_AddressSanitizer_h
+#define WTF_AddressSanitizer_h
+// TODO(kojii): This file will need to be renamed, because it's no more
+// specific to AddressSanitizer.
+
+#include "platform/wtf/build_config.h"
+
+// TODO(sof): Add SyZyASan support?
+#if defined(ADDRESS_SANITIZER)
+#include <sanitizer/asan_interface.h>
+#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+#else
+#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
+#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
+#define NO_SANITIZE_ADDRESS
+#endif
+
+#if defined(LEAK_SANITIZER)
+#include <sanitizer/lsan_interface.h>
+#else
+#define __lsan_register_root_region(addr, size) ((void)(addr), (void)(size))
+#define __lsan_unregister_root_region(addr, size) ((void)(addr), (void)(size))
+#endif
+
+#if defined(MEMORY_SANITIZER)
+#include <sanitizer/msan_interface.h>
+#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
+#else
+#define NO_SANITIZE_MEMORY
+#endif
+
+#if defined(THREAD_SANITIZER)
+#define NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+#else
+#define NO_SANITIZE_THREAD
+#endif
+
+#endif  // WTF_AddressSanitizer_h
diff --git a/third_party/WebKit/Source/platform/wtf/Alignment.h b/third_party/WebKit/Source/platform/wtf/Alignment.h
new file mode 100644
index 0000000..638f81a
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/Alignment.h
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WTF_Alignment_h
+#define WTF_Alignment_h
+
+#include "platform/wtf/Compiler.h"
+#include <algorithm>
+#include <stdint.h>
+#include <utility>
+
+namespace WTF {
+
+#if COMPILER(GCC)
+#define WTF_ALIGN_OF(type) __alignof__(type)
+#define WTF_ALIGNED(variable_type, variable, n) \
+  variable_type variable __attribute__((__aligned__(n)))
+#elif COMPILER(MSVC)
+#define WTF_ALIGN_OF(type) __alignof(type)
+#define WTF_ALIGNED(variable_type, variable, n) \
+  __declspec(align(n)) variable_type variable
+#else
+#error WTF_ALIGN macros need alignment control.
+#endif
+
+#if COMPILER(GCC)
+typedef char __attribute__((__may_alias__)) AlignedBufferChar;  // NOLINT
+#else
+typedef char AlignedBufferChar;
+#endif
+
+template <size_t size, size_t alignment>
+struct AlignedBuffer;
+template <size_t size>
+struct AlignedBuffer<size, 1> {
+  AlignedBufferChar buffer[size];
+};
+template <size_t size>
+struct AlignedBuffer<size, 2> {
+  WTF_ALIGNED(AlignedBufferChar, buffer[size], 2);
+};
+template <size_t size>
+struct AlignedBuffer<size, 4> {
+  WTF_ALIGNED(AlignedBufferChar, buffer[size], 4);
+};
+template <size_t size>
+struct AlignedBuffer<size, 8> {
+  WTF_ALIGNED(AlignedBufferChar, buffer[size], 8);
+};
+template <size_t size>
+struct AlignedBuffer<size, 16> {
+  WTF_ALIGNED(AlignedBufferChar, buffer[size], 16);
+};
+template <size_t size>
+struct AlignedBuffer<size, 32> {
+  WTF_ALIGNED(AlignedBufferChar, buffer[size], 32);
+};
+template <size_t size>
+struct AlignedBuffer<size, 64> {
+  WTF_ALIGNED(AlignedBufferChar, buffer[size], 64);
+};
+
+template <size_t size, size_t alignment>
+void swap(AlignedBuffer<size, alignment>& a,
+          AlignedBuffer<size, alignment>& b) {
+  for (size_t i = 0; i < size; ++i)
+    std::swap(a.buffer[i], b.buffer[i]);
+}
+
+template <uintptr_t mask>
+inline bool isAlignedTo(const void* pointer) {
+  return !(reinterpret_cast<uintptr_t>(pointer) & mask);
+}
+
+}  // namespace WTF
+
+#endif  // WTF_Alignment_h
diff --git a/third_party/WebKit/Source/platform/wtf/AutoReset.h b/third_party/WebKit/Source/platform/wtf/AutoReset.h
new file mode 100644
index 0000000..b74eaf8
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/AutoReset.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AutoReset_h
+#define AutoReset_h
+
+#include "base/auto_reset.h"
+
+namespace WTF {
+
+// WTF::AutoReset is base::AutoReset. See base/auto_reset.h for documentation.
+
+template <typename T>
+using AutoReset = base::AutoReset<T>;
+
+}  // namespace WTF
+
+using WTF::AutoReset;
+
+#endif
diff --git a/third_party/WebKit/Source/platform/wtf/BUILD.gn b/third_party/WebKit/Source/platform/wtf/BUILD.gn
index 6873bdb..879124f3 100644
--- a/third_party/WebKit/Source/platform/wtf/BUILD.gn
+++ b/third_party/WebKit/Source/platform/wtf/BUILD.gn
@@ -59,9 +59,29 @@
 
 component("platform_wtf") {
   sources = [
+    "AddressSanitizer.h",
+    "Alignment.h",
+    "AutoReset.h",
+    "BitwiseOperations.h",
+    "ByteSwap.h",
+    "CPU.h",
+    "CheckedNumeric.h",
+    "Compiler.h",
+    "ConditionalDestructor.h",
+    "ContainerAnnotations.h",
     "CryptographicallyRandomNumber.cpp",
     "CryptographicallyRandomNumber.h",
+    "CurrentTime.cpp",
+    "CurrentTime.h",
+    "DynamicAnnotations.cpp",
+    "DynamicAnnotations.h",
+    "Forward.h",
+    "GetPtr.h",
+    "LeakAnnotations.h",
+    "Noncopyable.h",
+    "TypeTraits.h",
     "WTFExport.h",
+    "build_config.h",
   ]
 
   configs += [
diff --git a/third_party/WebKit/Source/wtf/CurrentTime.cpp b/third_party/WebKit/Source/platform/wtf/BitwiseOperations.h
similarity index 64%
copy from third_party/WebKit/Source/wtf/CurrentTime.cpp
copy to third_party/WebKit/Source/platform/wtf/BitwiseOperations.h
index 71ffbcc..8d5d86a 100644
--- a/third_party/WebKit/Source/wtf/CurrentTime.cpp
+++ b/third_party/WebKit/Source/platform/wtf/BitwiseOperations.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -28,35 +28,25 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "wtf/CurrentTime.h"
+// TODO(palmer): The only caller of this code in Blink is PartitionAlloc. When
+// PA is moved to base, we can remove this file.
+// https://bugs.chromium.org/p/chromium/issues/detail?id=632441
 
-#include "base/time/time.h"
+#ifndef WTF_BitwiseOperations_h
+#define WTF_BitwiseOperations_h
+
+#include "base/bits.h"
+#include "platform/wtf/CPU.h"
 
 namespace WTF {
 
-static TimeFunction mockTimeFunctionForTesting = nullptr;
+using base::bits::CountLeadingZeroBits32;
+using base::bits::CountLeadingZeroBitsSizeT;
 
-double currentTime() {
-  if (mockTimeFunctionForTesting)
-    return mockTimeFunctionForTesting();
-  return base::Time::Now().ToDoubleT();
-}
-
-double monotonicallyIncreasingTime() {
-  if (mockTimeFunctionForTesting)
-    return mockTimeFunctionForTesting();
-  return base::TimeTicks::Now().ToInternalValue() /
-         static_cast<double>(base::Time::kMicrosecondsPerSecond);
-}
-
-TimeFunction setTimeFunctionsForTesting(TimeFunction newFunction) {
-  TimeFunction oldFunction = mockTimeFunctionForTesting;
-  mockTimeFunctionForTesting = newFunction;
-  return oldFunction;
-}
-
-TimeFunction getTimeFunctionForTesting() {
-  return mockTimeFunctionForTesting;
-}
+#if CPU(64BIT)
+using base::bits::CountLeadingZeroBits64;
+#endif
 
 }  // namespace WTF
+
+#endif  // WTF_BitwiseOperations_h
diff --git a/third_party/WebKit/Source/platform/wtf/ByteSwap.h b/third_party/WebKit/Source/platform/wtf/ByteSwap.h
new file mode 100644
index 0000000..979f7ac
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/ByteSwap.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_ByteSwap_h
+#define WTF_ByteSwap_h
+
+#include "platform/wtf/CPU.h"
+#include "platform/wtf/Compiler.h"
+
+#include <stdint.h>
+
+#if COMPILER(MSVC)
+#include <stdlib.h>
+#endif
+
+namespace WTF {
+
+inline uint32_t wswap32(uint32_t x) {
+  return ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16);
+}
+
+#if COMPILER(MSVC)
+
+ALWAYS_INLINE uint64_t bswap64(uint64_t x) {
+  return _byteswap_uint64(x);
+}
+ALWAYS_INLINE uint32_t bswap32(uint32_t x) {
+  return _byteswap_ulong(x);
+}
+ALWAYS_INLINE uint16_t bswap16(uint16_t x) {
+  return _byteswap_ushort(x);
+}
+
+#else
+
+ALWAYS_INLINE uint64_t bswap64(uint64_t x) {
+  return __builtin_bswap64(x);
+}
+ALWAYS_INLINE uint32_t bswap32(uint32_t x) {
+  return __builtin_bswap32(x);
+}
+ALWAYS_INLINE uint16_t bswap16(uint16_t x) {
+  return __builtin_bswap16(x);
+}
+
+#endif
+
+#if CPU(64BIT)
+
+ALWAYS_INLINE size_t bswapuintptrt(size_t x) {
+  return bswap64(x);
+}
+
+#else
+
+ALWAYS_INLINE size_t bswapuintptrt(size_t x) {
+  return bswap32(x);
+}
+
+#endif
+
+}  // namespace WTF
+
+#endif  // WTF_ByteSwap_h
diff --git a/third_party/WebKit/Source/platform/wtf/CPU.h b/third_party/WebKit/Source/platform/wtf/CPU.h
new file mode 100644
index 0000000..409b58c
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/CPU.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_CPU_h
+#define WTF_CPU_h
+
+#include "platform/wtf/Compiler.h"
+
+/* CPU() - the target CPU architecture */
+#define CPU(WTF_FEATURE) \
+  (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE)
+
+/* ==== CPU() - the target CPU architecture ==== */
+
+/* This defines CPU(BIG_ENDIAN) or nothing, as appropriate. */
+/* This defines CPU(32BIT) or CPU(64BIT), as appropriate. */
+
+/* CPU(X86) - i386 / x86 32-bit */
+#if defined(__i386__) || defined(i386) || defined(_M_IX86) || \
+    defined(_X86_) || defined(__THW_INTEL)
+#define WTF_CPU_X86 1
+#endif
+
+/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */
+#if defined(__x86_64__) || defined(_M_X64)
+#define WTF_CPU_X86_64 1
+#define WTF_CPU_64BIT 1
+#endif
+
+/* CPU(ARM) - ARM, any version*/
+#define WTF_ARM_ARCH_AT_LEAST(N) \
+  (CPU(ARM) && defined(WTF_ARM_ARCH_VERSION) && WTF_ARM_ARCH_VERSION >= N)
+
+#if defined(arm) || defined(__arm__) || defined(ARM) || defined(_ARM_)
+#define WTF_CPU_ARM 1
+
+#if defined(__ARMEB__)
+#define WTF_CPU_BIG_ENDIAN 1
+
+#elif !defined(__ARM_EABI__) && !defined(__EABI__) && !defined(__VFP_FP__) && \
+    !defined(_WIN32_WCE) && !defined(ANDROID)
+#define WTF_CPU_MIDDLE_ENDIAN 1
+
+#endif
+
+/* Set WTF_ARM_ARCH_VERSION */
+#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
+    defined(__MARM_ARMV4__)
+#define WTF_ARM_ARCH_VERSION 4
+
+#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
+    defined(__MARM_ARMV5__)
+#define WTF_ARM_ARCH_VERSION 5
+
+#elif defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) || \
+    defined(__ARM_ARCH_5TEJ__)
+#define WTF_ARM_ARCH_VERSION 5
+
+#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) ||  \
+    defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) ||   \
+    defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \
+    defined(__ARMV6__)
+#define WTF_ARM_ARCH_VERSION 6
+
+#elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \
+    defined(__ARM_ARCH_7S__)
+#define WTF_ARM_ARCH_VERSION 7
+
+/* MSVC sets _M_ARM */
+#elif defined(_M_ARM)
+#define WTF_ARM_ARCH_VERSION _M_ARM
+#else
+#define WTF_ARM_ARCH_VERSION 0
+
+#endif
+
+/* Set WTF_THUMB_ARCH_VERSION */
+#if defined(__ARM_ARCH_4T__)
+#define WTF_THUMB_ARCH_VERSION 1
+
+#elif defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) || \
+    defined(__ARM_ARCH_5TEJ__)
+#define WTF_THUMB_ARCH_VERSION 2
+
+#elif defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || \
+    defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) ||  \
+    defined(__ARM_ARCH_6M__)
+#define WTF_THUMB_ARCH_VERSION 3
+
+#elif defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_7__) || \
+    defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7M__) ||   \
+    defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7S__)
+#define WTF_THUMB_ARCH_VERSION 4
+
+#else
+#define WTF_THUMB_ARCH_VERSION 0
+#endif
+
+/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */
+#if !defined(WTF_CPU_ARM_THUMB2)
+#if defined(thumb2) || defined(__thumb2__) || \
+    ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4)
+#define WTF_CPU_ARM_THUMB2 1
+#elif WTF_ARM_ARCH_AT_LEAST(4)
+#define WTF_CPU_ARM_THUMB2 0
+#else
+#error "Unsupported ARM architecture"
+#endif
+#endif /* !defined(WTF_CPU_ARM_THUMB2) */
+
+#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON)
+#define WTF_CPU_ARM_NEON 1
+#endif
+
+#if CPU(ARM_NEON) && \
+    (COMPILER(CLANG) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0))
+// All NEON intrinsics usage can be disabled by this macro.
+#define HAVE_ARM_NEON_INTRINSICS 1
+#endif
+
+#endif /* ARM */
+
+/* CPU(ARM64) - AArch64 64-bit */
+#if defined(__aarch64__)
+#define WTF_CPU_ARM64 1
+#define WTF_CPU_64BIT 1
+#endif
+
+/* CPU(MIPS), CPU(MIPS64) */
+#if defined(__mips__) && (__mips == 64)
+#define WTF_CPU_MIPS64 1
+#define WTF_CPU_64BIT 1
+#elif defined(__mips__)
+#define WTF_CPU_MIPS 1
+#endif
+
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+// All MSA intrinsics usage can be disabled by this macro.
+#define HAVE_MIPS_MSA_INTRINSICS 1
+#endif
+
+#if !defined(WTF_CPU_64BIT)
+#define WTF_CPU_32BIT 1
+#endif
+
+#endif /* WTF_CPU_h */
diff --git a/third_party/WebKit/Source/platform/wtf/CheckedNumeric.h b/third_party/WebKit/Source/platform/wtf/CheckedNumeric.h
new file mode 100644
index 0000000..f94ba48
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/CheckedNumeric.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_CheckedNumeric_h
+#define WTF_CheckedNumeric_h
+
+/* See base/numerics/safe_math.h for usage.
+ */
+#include "base/numerics/safe_math.h"
+
+namespace WTF {
+using base::CheckedNumeric;
+}  // namespace WTF
+
+using WTF::CheckedNumeric;
+
+#endif
diff --git a/third_party/WebKit/Source/platform/wtf/Compiler.h b/third_party/WebKit/Source/platform/wtf/Compiler.h
new file mode 100644
index 0000000..1b81bb9
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/Compiler.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_Compiler_h
+#define WTF_Compiler_h
+
+#include "base/compiler_specific.h"
+
+/* COMPILER() - the compiler being used to build the project */
+#define COMPILER(WTF_FEATURE) \
+  (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE)
+
+/* ==== COMPILER() - the compiler being used to build the project ==== */
+
+/* COMPILER(CLANG) - Clang  */
+#if defined(__clang__)
+#define WTF_COMPILER_CLANG 1
+#endif
+
+/* COMPILER(MSVC) - Microsoft Visual C++ (and Clang when compiling for Windows).
+ */
+#if defined(_MSC_VER)
+#define WTF_COMPILER_MSVC 1
+#endif
+
+/* COMPILER(GCC) - GNU Compiler Collection (and Clang when compiling for
+ * platforms other than Windows). */
+#if defined(__GNUC__)
+#define WTF_COMPILER_GCC 1
+#define GCC_VERSION \
+  (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#define GCC_VERSION_AT_LEAST(major, minor, patch) \
+  (GCC_VERSION >= (major * 10000 + minor * 100 + patch))
+#else
+/* Define this for !GCC compilers, just so we can write things like
+ * GCC_VERSION_AT_LEAST(4, 1, 0). */
+#define GCC_VERSION_AT_LEAST(major, minor, patch) 0
+#endif
+
+/* ==== Compiler features ==== */
+
+/* NEVER_INLINE */
+
+// TODO(palmer): Remove this and update callers to use NOINLINE from Chromium
+// base. https://bugs.chromium.org/p/chromium/issues/detail?id=632441
+#define NEVER_INLINE NOINLINE
+
+/* OBJC_CLASS */
+
+#ifndef OBJC_CLASS
+#ifdef __OBJC__
+#define OBJC_CLASS @class
+#else
+#define OBJC_CLASS class
+#endif
+#endif
+
+/* WTF_PRETTY_FUNCTION */
+
+#if COMPILER(GCC)
+#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#elif COMPILER(MSVC)
+#define WTF_PRETTY_FUNCTION __FUNCSIG__
+#else
+#define WTF_PRETTY_FUNCTION __func__
+#endif
+
+/* NO_SANITIZE_UNRELATED_CAST - Disable runtime checks related to casts between
+ * unrelated objects (-fsanitize=cfi-unrelated-cast or -fsanitize=vptr). */
+
+#if COMPILER(CLANG)
+#define NO_SANITIZE_UNRELATED_CAST \
+  __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
+#else
+#define NO_SANITIZE_UNRELATED_CAST
+#endif
+
+#endif /* WTF_Compiler_h */
diff --git a/third_party/WebKit/Source/platform/wtf/ConditionalDestructor.h b/third_party/WebKit/Source/platform/wtf/ConditionalDestructor.h
new file mode 100644
index 0000000..c2fdd1c5
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/ConditionalDestructor.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ConditionalDestructor_h
+#define ConditionalDestructor_h
+
+namespace WTF {
+
+// ConditionalDestructor defines the destructor of the derived object.
+// This base is used in order to completely avoid creating a destructor
+// for an object that does not need to be destructed. By doing so,
+// the clang compiler will have correct information about whether or not
+// the object has a trivial destructor.
+// Note: the derived object MUST release all its recources at the finalize()
+// method.
+template <typename Derived, bool noDestructor>
+class ConditionalDestructor {
+ public:
+  ~ConditionalDestructor() { static_cast<Derived*>(this)->finalize(); }
+};
+
+template <typename Derived>
+class ConditionalDestructor<Derived, true> {};
+
+}  // namespace WTF
+
+#endif  // ConditionalDestructor_h
diff --git a/third_party/WebKit/Source/platform/wtf/ContainerAnnotations.h b/third_party/WebKit/Source/platform/wtf/ContainerAnnotations.h
new file mode 100644
index 0000000..d0b7459d
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/ContainerAnnotations.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WTF_ContainerAnnotations_h
+#define WTF_ContainerAnnotations_h
+
+#include "platform/wtf/AddressSanitizer.h"
+#include "platform/wtf/CPU.h"
+
+// TODO(ochang): Remove the CPU(X86_64) condition to enable this for X86 once
+// the crashes there have been fixed: http://crbug.com/461406
+#if defined(ADDRESS_SANITIZER) && OS(LINUX) && CPU(X86_64)
+#define ANNOTATE_CONTIGUOUS_CONTAINER
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)                       \
+  if (buffer) {                                                              \
+    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+                                              (buffer) + (capacity),         \
+                                              (buffer) + (newSize));         \
+  }
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)                    \
+  if (buffer) {                                                              \
+    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+                                              (buffer) + (oldSize),          \
+                                              (buffer) + (capacity));        \
+  }
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)             \
+  if (buffer) {                                                              \
+    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+                                              (buffer) + (oldSize),          \
+                                              (buffer) + (newSize));         \
+  }
+#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity) \
+  ANNOTATE_DELETE_BUFFER(buffer, oldCapacity, bufferSize);                     \
+  ANNOTATE_NEW_BUFFER(buffer, newCapacity, bufferSize);
+// Annotations require buffers to begin on an 8-byte boundary.
+#else  // defined(ADDRESS_SANITIZER) && OS(LINUX) && CPU(X86_64)
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)
+#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity)
+#endif  // defined(ADDRESS_SANITIZER) && OS(LINUX) && CPU(X86_64)
+
+#endif  // WTF_ContainerAnnotations_h
diff --git a/third_party/WebKit/Source/wtf/CurrentTime.cpp b/third_party/WebKit/Source/platform/wtf/CurrentTime.cpp
similarity index 98%
rename from third_party/WebKit/Source/wtf/CurrentTime.cpp
rename to third_party/WebKit/Source/platform/wtf/CurrentTime.cpp
index 71ffbcc..13ec076 100644
--- a/third_party/WebKit/Source/wtf/CurrentTime.cpp
+++ b/third_party/WebKit/Source/platform/wtf/CurrentTime.cpp
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "wtf/CurrentTime.h"
+#include "platform/wtf/CurrentTime.h"
 
 #include "base/time/time.h"
 
diff --git a/third_party/WebKit/Source/platform/wtf/CurrentTime.h b/third_party/WebKit/Source/platform/wtf/CurrentTime.h
new file mode 100644
index 0000000..705a2c9
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/CurrentTime.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2008 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CurrentTime_h
+#define CurrentTime_h
+
+#include "platform/wtf/WTFExport.h"
+
+namespace WTF {
+
+// Returns the current UTC time in seconds, counted from January 1, 1970.
+// Precision varies depending on platform but is usually as good or better
+// than a millisecond.
+WTF_EXPORT double currentTime();
+
+// Same thing, in milliseconds.
+inline double currentTimeMS() {
+  return currentTime() * 1000.0;
+}
+
+// Provides a monotonically increasing time in seconds since an arbitrary point
+// in the past.  On unsupported platforms, this function only guarantees the
+// result will be non-decreasing.
+WTF_EXPORT double monotonicallyIncreasingTime();
+
+// Same thing, in milliseconds.
+inline double monotonicallyIncreasingTimeMS() {
+  return monotonicallyIncreasingTime() * 1000.0;
+}
+
+using TimeFunction = double (*)();
+
+// Make all the time functions (currentTime(), monotonicallyIncreasingTime(),
+// systemTraceTime()) return the result of the supplied function. Returns the
+// pointer to the old time function. For both setting and getting, nullptr
+// means using the default timing function returning the actual time.
+WTF_EXPORT TimeFunction setTimeFunctionsForTesting(TimeFunction);
+
+// Allows wtf/Time.h to use the same mock time function
+WTF_EXPORT TimeFunction getTimeFunctionForTesting();
+
+}  // namespace WTF
+
+using WTF::currentTime;
+using WTF::currentTimeMS;
+using WTF::monotonicallyIncreasingTime;
+using WTF::monotonicallyIncreasingTimeMS;
+using WTF::TimeFunction;
+using WTF::setTimeFunctionsForTesting;
+
+#endif  // CurrentTime_h
diff --git a/third_party/WebKit/Source/wtf/DynamicAnnotations.cpp b/third_party/WebKit/Source/platform/wtf/DynamicAnnotations.cpp
similarity index 97%
rename from third_party/WebKit/Source/wtf/DynamicAnnotations.cpp
rename to third_party/WebKit/Source/platform/wtf/DynamicAnnotations.cpp
index ae6b8879..f8a84a2c 100644
--- a/third_party/WebKit/Source/wtf/DynamicAnnotations.cpp
+++ b/third_party/WebKit/Source/platform/wtf/DynamicAnnotations.cpp
@@ -24,7 +24,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "DynamicAnnotations.h"
+#include "platform/wtf/DynamicAnnotations.h"
 
 #if USE(DYNAMIC_ANNOTATIONS) && !USE(DYNAMIC_ANNOTATIONS_NOIMPL)
 
diff --git a/third_party/WebKit/Source/platform/wtf/DynamicAnnotations.h b/third_party/WebKit/Source/platform/wtf/DynamicAnnotations.h
new file mode 100644
index 0000000..8c5d939
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/DynamicAnnotations.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_DynamicAnnotations_h
+#define WTF_DynamicAnnotations_h
+
+/* This file defines dynamic annotations for use with dynamic analysis
+ * tool such as ThreadSanitizer, Valgrind, etc.
+ *
+ * Dynamic annotation is a source code annotation that affects
+ * the generated code (that is, the annotation is not a comment).
+ * Each such annotation is attached to a particular
+ * instruction and/or to a particular object (address) in the program.
+ *
+ * By using dynamic annotations a developer can give more details to the dynamic
+ * analysis tool to improve its precision.
+ *
+ * In C/C++ program the annotations are represented as C macros.
+ * With the default build flags, these macros are empty, hence don't affect
+ * performance of a compiled binary.
+ * If dynamic annotations are enabled, they just call no-op functions.
+ * The dynamic analysis tools can intercept these functions and replace them
+ * with their own implementations.
+ *
+ * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more
+ * information.
+ */
+
+#include "platform/wtf/WTFExport.h"
+#include "platform/wtf/build_config.h"
+
+#if USE(DYNAMIC_ANNOTATIONS)
+/* Tell data race detector that we're not interested in reports on the given
+ * address range. */
+#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
+  WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
+#define WTF_ANNOTATE_BENIGN_RACE(pointer, description)                        \
+  WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), \
+                             description)
+
+/* Annotations for user-defined synchronization mechanisms.
+ * These annotations can be used to define happens-before arcs in user-defined
+ * synchronization mechanisms: the race detector will infer an arc from
+ * the former to the latter when they share the same argument pointer.
+ *
+ * The most common case requiring annotations is atomic reference counting:
+ * bool deref() {
+ *     ANNOTATE_HAPPENS_BEFORE(&m_refCount);
+ *     if (!atomicDecrement(&m_refCount)) {
+ *         // m_refCount is now 0
+ *         ANNOTATE_HAPPENS_AFTER(&m_refCount);
+ *         // "return true; happens-after each atomicDecrement of m_refCount"
+ *         return true;
+ *     }
+ *     return false;
+ * }
+ */
+#define WTF_ANNOTATE_HAPPENS_BEFORE(address) \
+  WTFAnnotateHappensBefore(__FILE__, __LINE__, address)
+#define WTF_ANNOTATE_HAPPENS_AFTER(address) \
+  WTFAnnotateHappensAfter(__FILE__, __LINE__, address)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Don't use these directly, use the above macros instead. */
+WTF_EXPORT void WTFAnnotateBenignRaceSized(const char* file,
+                                           int line,
+                                           const volatile void* memory,
+                                           long size,
+                                           const char* description);
+WTF_EXPORT void WTFAnnotateHappensBefore(const char* file,
+                                         int line,
+                                         const volatile void* address);
+WTF_EXPORT void WTFAnnotateHappensAfter(const char* file,
+                                        int line,
+                                        const volatile void* address);
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#else  // USE(DYNAMIC_ANNOTATIONS)
+/* These macros are empty when dynamic annotations are not enabled so you can
+ * use them without affecting the performance of release binaries. */
+#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description)
+#define WTF_ANNOTATE_BENIGN_RACE(pointer, description)
+#define WTF_ANNOTATE_HAPPENS_BEFORE(address)
+#define WTF_ANNOTATE_HAPPENS_AFTER(address)
+#endif  // USE(DYNAMIC_ANNOTATIONS)
+
+#endif  // WTF_DynamicAnnotations_h
diff --git a/third_party/WebKit/Source/platform/wtf/Forward.h b/third_party/WebKit/Source/platform/wtf/Forward.h
new file mode 100644
index 0000000..c6d12c1
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/Forward.h
@@ -0,0 +1,84 @@
+/*
+ *  Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WTF_Forward_h
+#define WTF_Forward_h
+
+#include "platform/wtf/Compiler.h"
+#include <stddef.h>
+
+namespace WTF {
+
+template <typename T>
+class PassRefPtr;
+template <typename T>
+class RefPtr;
+template <typename T>
+class StringBuffer;
+template <typename T, size_t inlineCapacity, typename Allocator>
+class Vector;
+
+class ArrayBuffer;
+class ArrayBufferView;
+class ArrayPiece;
+class AtomicString;
+class CString;
+class Float32Array;
+class Float64Array;
+class Int8Array;
+class Int16Array;
+class Int32Array;
+class OrdinalNumber;
+class String;
+class StringBuilder;
+class StringImpl;
+class StringView;
+class Uint8Array;
+class Uint8ClampedArray;
+class Uint16Array;
+class Uint32Array;
+
+}  // namespace WTF
+
+using WTF::PassRefPtr;
+using WTF::RefPtr;
+using WTF::Vector;
+
+using WTF::ArrayBuffer;
+using WTF::ArrayBufferView;
+using WTF::ArrayPiece;
+using WTF::AtomicString;
+using WTF::CString;
+using WTF::Float32Array;
+using WTF::Float64Array;
+using WTF::Int8Array;
+using WTF::Int16Array;
+using WTF::Int32Array;
+using WTF::String;
+using WTF::StringBuffer;
+using WTF::StringBuilder;
+using WTF::StringImpl;
+using WTF::StringView;
+using WTF::Uint8Array;
+using WTF::Uint8ClampedArray;
+using WTF::Uint16Array;
+using WTF::Uint32Array;
+
+#endif  // WTF_Forward_h
diff --git a/third_party/WebKit/Source/platform/wtf/GetPtr.h b/third_party/WebKit/Source/platform/wtf/GetPtr.h
new file mode 100644
index 0000000..f211f20
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/GetPtr.h
@@ -0,0 +1,38 @@
+/*
+ *  Copyright (C) 2006 Apple Computer, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WTF_GetPtr_h
+#define WTF_GetPtr_h
+
+namespace WTF {
+
+template <typename T>
+inline T* getPtr(T* p) {
+  return p;
+}
+
+template <typename T>
+inline T* getPtr(T& p) {
+  return &p;
+}
+
+}  // namespace WTF
+
+#endif  // WTF_GetPtr_h
diff --git a/third_party/WebKit/Source/platform/wtf/LeakAnnotations.h b/third_party/WebKit/Source/platform/wtf/LeakAnnotations.h
new file mode 100644
index 0000000..82321412
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/LeakAnnotations.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_LeakAnnotations_h
+#define WTF_LeakAnnotations_h
+
+// This file defines macros for working with LeakSanitizer, allowing memory
+// and allocations to be registered as exempted from LSan consideration.
+
+#include "platform/wtf/Noncopyable.h"
+#if defined(LEAK_SANITIZER)
+#include "platform/wtf/AddressSanitizer.h"
+#include "platform/wtf/TypeTraits.h"
+#endif
+
+namespace WTF {
+
+#if defined(LEAK_SANITIZER)
+class LeakSanitizerDisabler {
+  WTF_MAKE_NONCOPYABLE(LeakSanitizerDisabler);
+
+ public:
+  LeakSanitizerDisabler() { __lsan_disable(); }
+
+  ~LeakSanitizerDisabler() { __lsan_enable(); }
+};
+
+// WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE: all allocations made in the
+// current scope will be exempted from LSan consideration. Only to be
+// used internal to wtf/, Blink should use LEAK_SANITIZER_DISABLED_SCOPE
+// elsewhere.
+//
+// TODO(sof): once layering rules allow wtf/ to make use of the Oilpan
+// infrastructure, remove this macro.
+#define WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE  \
+  WTF::LeakSanitizerDisabler leakSanitizerDisabler; \
+  static_cast<void>(0)
+
+// LEAK_SANITIZER_IGNORE_OBJECT(X): the heap object referenced by pointer X
+// will be ignored by LSan.
+//
+// "Ignorance" means that LSan's reachability traversal is stopped short
+// upon encountering an ignored memory chunk. Consequently, LSan will not
+// scan an ignored memory chunk for live, reachable pointers. However, should
+// those embedded pointers be reachable by some other path, they will be
+// reported as leaking.
+#define LEAK_SANITIZER_IGNORE_OBJECT(X) __lsan_ignore_object(X)
+
+// If the object pointed to by the static local is on the Oilpan heap, a strong
+// Persistent<> is created to keep the pointed-to heap object alive. This makes
+// both the Persistent<> and the heap object _reachable_ by LeakSanitizer's leak
+// detection pass. We do not want these intentional leaks to be reported by
+// LSan, hence the static local is registered with Oilpan
+// (see RegisterStaticLocalReference<> below.)
+//
+// Upon Blink shutdown, all the registered statics are released and a final
+// round of GCs are performed to sweep out their now-unreachable object graphs.
+// The end result being a tidied heap that the LeakSanitizer can then scan to
+// report real leaks.
+//
+// The CanRegisterStaticLocalReference<> and RegisterStaticLocalReference<>
+// templates arrange for this -- for a class type T, a registerStatic()
+// implementation is provided if "T* T::registerAsStaticReference(T*)" is a
+// method on T (inherited or otherwise.)
+//
+// An empty, trivial registerStatic() method is provided for all other class
+// types T.
+template <typename T>
+class CanRegisterStaticLocalReference {
+  typedef char YesType;
+  typedef struct NoType { char padding[8]; } NoType;
+
+  // Check if class T has public method "T* registerAsStaticReference()".
+  template <typename V>
+  static YesType checkHasRegisterAsStaticReferenceMethod(
+      V* p,
+      typename std::enable_if<IsSubclass<
+          V,
+          typename std::remove_pointer<decltype(
+              p->registerAsStaticReference())>::type>::value>::type* = 0);
+  template <typename V>
+  static NoType checkHasRegisterAsStaticReferenceMethod(...);
+
+ public:
+  static const bool value =
+      sizeof(YesType) + sizeof(T) ==
+      sizeof(checkHasRegisterAsStaticReferenceMethod<T>(nullptr)) + sizeof(T);
+};
+
+template <typename T, bool = CanRegisterStaticLocalReference<T>::value>
+class RegisterStaticLocalReference {
+ public:
+  static T* registerStatic(T* ptr) { return ptr; }
+};
+
+template <typename T>
+class RegisterStaticLocalReference<T, true> {
+ public:
+  static T* registerStatic(T* ptr) {
+    return static_cast<T*>(ptr->registerAsStaticReference());
+  }
+};
+
+#define LEAK_SANITIZER_REGISTER_STATIC_LOCAL(Type, Object) \
+  WTF::RegisterStaticLocalReference<Type>::registerStatic(Object)
+#else
+#define WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE
+#define LEAK_SANITIZER_IGNORE_OBJECT(X) ((void)0)
+#define LEAK_SANITIZER_REGISTER_STATIC_LOCAL(Type, Object) Object
+#endif  // defined(LEAK_SANITIZER)
+
+}  // namespace WTF
+
+#endif  // WTF_LeakAnnotations_h
diff --git a/third_party/WebKit/Source/platform/wtf/Noncopyable.h b/third_party/WebKit/Source/platform/wtf/Noncopyable.h
new file mode 100644
index 0000000..dade16e
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/Noncopyable.h
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WTF_Noncopyable_h
+#define WTF_Noncopyable_h
+
+#define WTF_MAKE_NONCOPYABLE(ClassName) \
+ private:                               \
+  ClassName(const ClassName&) = delete; \
+  ClassName& operator=(const ClassName&) = delete
+
+#endif  // WTF_Noncopyable_h
diff --git a/third_party/WebKit/Source/platform/wtf/TypeTraits.h b/third_party/WebKit/Source/platform/wtf/TypeTraits.h
new file mode 100644
index 0000000..ad0c60b
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/TypeTraits.h
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef TypeTraits_h
+#define TypeTraits_h
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+#include "platform/wtf/Compiler.h"
+
+namespace WTF {
+
+// Returns a string that contains the type name of |T| as a substring.
+template <typename T>
+inline const char* getStringWithTypeName() {
+  return WTF_PRETTY_FUNCTION;
+}
+
+template <typename T>
+struct IsWeak {
+  static const bool value = false;
+};
+
+enum WeakHandlingFlag {
+  NoWeakHandlingInCollections,
+  WeakHandlingInCollections
+};
+
+template <typename T, typename From>
+class IsAssignable {
+  typedef char YesType;
+  struct NoType {
+    char padding[8];
+  };
+
+  template <typename T2,
+            typename From2,
+            typename = decltype(std::declval<T2&>() = std::declval<From2>())>
+  static YesType checkAssignability(int);
+  template <typename T2, typename From2>
+  static NoType checkAssignability(...);
+
+ public:
+  static const bool value =
+      sizeof(checkAssignability<T, From>(0)) == sizeof(YesType);
+};
+
+template <typename T>
+struct IsCopyAssignable {
+  static_assert(!std::is_reference<T>::value, "T must not be a reference.");
+  static const bool value = IsAssignable<T, const T&>::value;
+};
+
+template <typename T>
+struct IsMoveAssignable {
+  static_assert(!std::is_reference<T>::value, "T must not be a reference.");
+  static const bool value = IsAssignable<T, T&&>::value;
+};
+
+template <typename T>
+struct IsTriviallyCopyAssignable {
+  static const bool value =
+      __has_trivial_assign(T) && IsCopyAssignable<T>::value;
+};
+
+template <typename T>
+struct IsTriviallyMoveAssignable {
+  // TODO(yutak): This isn't really correct, because __has_trivial_assign
+  // appears to look only at copy assignment.  However,
+  // std::is_trivially_move_assignable isn't available at this moment, and
+  // there isn't a good way to write that ourselves.
+  //
+  // Here we use IsTriviallyCopyAssignable as a conservative approximation: if T
+  // is trivially copy assignable, T is trivially move assignable, too. This
+  // definition misses a case where T is trivially move-only assignable, but
+  // such cases should be rare.
+  static const bool value = IsTriviallyCopyAssignable<T>::value;
+};
+
+template <typename T>
+class IsDestructible {
+  typedef char YesType;
+  struct NoType {
+    char padding[8];
+  };
+
+  template <typename T2, typename = decltype(std::declval<T2>().~T2())>
+  static YesType checkDestructibility(int);
+  template <typename T2>
+  static NoType checkDestructibility(...);
+
+ public:
+  static const bool value =
+      sizeof(checkDestructibility<T>(0)) == sizeof(YesType);
+};
+
+template <typename T>
+struct IsTriviallyDefaultConstructible {
+  static const bool value =
+      __has_trivial_constructor(T) && std::is_constructible<T>::value;
+};
+
+template <typename T>
+struct IsTriviallyDestructible {
+  static const bool value =
+      __has_trivial_destructor(T) && IsDestructible<T>::value;
+};
+
+template <typename T, typename U>
+struct IsSubclass {
+ private:
+  typedef char YesType;
+  struct NoType {
+    char padding[8];
+  };
+
+  static YesType subclassCheck(U*);
+  static NoType subclassCheck(...);
+  static T* t;
+
+ public:
+  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
+};
+
+template <typename T, template <typename... V> class U>
+struct IsSubclassOfTemplate {
+ private:
+  typedef char YesType;
+  struct NoType {
+    char padding[8];
+  };
+
+  template <typename... W>
+  static YesType subclassCheck(U<W...>*);
+  static NoType subclassCheck(...);
+  static T* t;
+
+ public:
+  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
+};
+
+template <typename T, template <typename V, size_t W> class U>
+struct IsSubclassOfTemplateTypenameSize {
+ private:
+  typedef char YesType;
+  struct NoType {
+    char padding[8];
+  };
+
+  template <typename X, size_t Y>
+  static YesType subclassCheck(U<X, Y>*);
+  static NoType subclassCheck(...);
+  static T* t;
+
+ public:
+  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
+};
+
+template <typename T, template <typename V, size_t W, typename X> class U>
+struct IsSubclassOfTemplateTypenameSizeTypename {
+ private:
+  typedef char YesType;
+  struct NoType {
+    char padding[8];
+  };
+
+  template <typename Y, size_t Z, typename A>
+  static YesType subclassCheck(U<Y, Z, A>*);
+  static NoType subclassCheck(...);
+  static T* t;
+
+ public:
+  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
+};
+
+template <typename T, template <class V> class OuterTemplate>
+struct RemoveTemplate {
+  typedef T Type;
+};
+
+template <typename T, template <class V> class OuterTemplate>
+struct RemoveTemplate<OuterTemplate<T>, OuterTemplate> {
+  typedef T Type;
+};
+
+#if (COMPILER(MSVC) || !GCC_VERSION_AT_LEAST(4, 9, 0)) && !COMPILER(CLANG)
+// FIXME: MSVC bug workaround. Remove once MSVC STL is fixed.
+// FIXME: GCC before 4.9.0 seems to have the same issue.
+// C++ 2011 Spec (ISO/IEC 14882:2011(E)) 20.9.6.2 Table 51 states that
+// the template parameters shall be a complete type if they are different types.
+// However, MSVC checks for type completeness even if they are the same type.
+// Here, we use a template specialization for same type case to allow incomplete
+// types.
+
+template <typename T, typename U>
+struct IsConvertible {
+  static const bool value = std::is_convertible<T, U>::value;
+};
+
+template <typename T>
+struct IsConvertible<T, T> {
+  static const bool value = true;
+};
+
+#define EnsurePtrConvertibleArgDecl(From, To)                             \
+  typename std::enable_if<WTF::IsConvertible<From*, To*>::value>::type* = \
+      nullptr
+#define EnsurePtrConvertibleArgDefn(From, To) \
+  typename std::enable_if<WTF::IsConvertible<From*, To*>::value>::type*
+#else
+#define EnsurePtrConvertibleArgDecl(From, To)                              \
+  typename std::enable_if<std::is_convertible<From*, To*>::value>::type* = \
+      nullptr
+#define EnsurePtrConvertibleArgDefn(From, To) \
+  typename std::enable_if<std::is_convertible<From*, To*>::value>::type*
+#endif
+
+}  // namespace WTF
+
+namespace blink {
+
+class Visitor;
+
+}  // namespace blink
+
+namespace WTF {
+
+template <typename T>
+class IsTraceable {
+  typedef char YesType;
+  typedef struct NoType { char padding[8]; } NoType;
+
+  // Note that this also checks if a superclass of V has a trace method.
+  template <typename V>
+  static YesType checkHasTraceMethod(
+      V* v,
+      blink::Visitor* p = nullptr,
+      typename std::enable_if<
+          std::is_same<decltype(v->trace(p)), void>::value>::type* g = nullptr);
+  template <typename V>
+  static NoType checkHasTraceMethod(...);
+
+ public:
+  // We add sizeof(T) to both sides here, because we want it to fail for
+  // incomplete types. Otherwise it just assumes that incomplete types do not
+  // have a trace method, which may not be true.
+  static const bool value = sizeof(YesType) + sizeof(T) ==
+                            sizeof(checkHasTraceMethod<T>(nullptr)) + sizeof(T);
+};
+
+// Convenience template wrapping the IsTraceableInCollection template in
+// Collection Traits. It helps make the code more readable.
+template <typename Traits>
+class IsTraceableInCollectionTrait {
+ public:
+  static const bool value = Traits::template IsTraceableInCollection<>::value;
+};
+
+template <typename T, typename U>
+struct IsTraceable<std::pair<T, U>> {
+  static const bool value = IsTraceable<T>::value || IsTraceable<U>::value;
+};
+
+// This is used to check that DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects are not
+// stored in off-heap Vectors, HashTables etc.
+template <typename T>
+struct AllowsOnlyPlacementNew {
+ private:
+  using YesType = char;
+  struct NoType {
+    char padding[8];
+  };
+
+  template <typename U>
+  static YesType checkMarker(typename U::IsAllowOnlyPlacementNew*);
+  template <typename U>
+  static NoType checkMarker(...);
+
+ public:
+  static const bool value = sizeof(checkMarker<T>(nullptr)) == sizeof(YesType);
+};
+
+template <typename T>
+class IsGarbageCollectedType {
+  typedef char YesType;
+  typedef struct NoType { char padding[8]; } NoType;
+
+  static_assert(sizeof(T), "T must be fully defined");
+
+  using NonConstType = typename std::remove_const<T>::type;
+  template <typename U>
+  static YesType checkGarbageCollectedType(
+      typename U::IsGarbageCollectedTypeMarker*);
+  template <typename U>
+  static NoType checkGarbageCollectedType(...);
+
+  // Separately check for GarbageCollectedMixin, which declares a different
+  // marker typedef, to avoid resolution ambiguity for cases like
+  // IsGarbageCollectedType<B> over:
+  //
+  //    class A : public GarbageCollected<A>, public GarbageCollectedMixin {
+  //        USING_GARBAGE_COLLECTED_MIXIN(A);
+  //        ...
+  //    };
+  //    class B : public A, public GarbageCollectedMixin { ... };
+  //
+  template <typename U>
+  static YesType checkGarbageCollectedMixinType(
+      typename U::IsGarbageCollectedMixinMarker*);
+  template <typename U>
+  static NoType checkGarbageCollectedMixinType(...);
+
+ public:
+  static const bool value =
+      (sizeof(YesType) ==
+       sizeof(checkGarbageCollectedType<NonConstType>(nullptr))) ||
+      (sizeof(YesType) ==
+       sizeof(checkGarbageCollectedMixinType<NonConstType>(nullptr)));
+};
+
+template <>
+class IsGarbageCollectedType<void> {
+ public:
+  static const bool value = false;
+};
+
+template <typename T>
+class IsPersistentReferenceType {
+  typedef char YesType;
+  typedef struct NoType { char padding[8]; } NoType;
+
+  template <typename U>
+  static YesType checkPersistentReferenceType(
+      typename U::IsPersistentReferenceTypeMarker*);
+  template <typename U>
+  static NoType checkPersistentReferenceType(...);
+
+ public:
+  static const bool value =
+      (sizeof(YesType) == sizeof(checkPersistentReferenceType<T>(nullptr)));
+};
+
+template <typename T,
+          bool = std::is_function<typename std::remove_const<
+                     typename std::remove_pointer<T>::type>::type>::value ||
+                 std::is_void<typename std::remove_const<
+                     typename std::remove_pointer<T>::type>::type>::value>
+class IsPointerToGarbageCollectedType {
+ public:
+  static const bool value = false;
+};
+
+template <typename T>
+class IsPointerToGarbageCollectedType<T*, false> {
+ public:
+  static const bool value = IsGarbageCollectedType<T>::value;
+};
+
+}  // namespace WTF
+
+using WTF::IsGarbageCollectedType;
+
+#endif  // TypeTraits_h
diff --git a/third_party/WebKit/Source/platform/wtf/build_config.h b/third_party/WebKit/Source/platform/wtf/build_config.h
new file mode 100644
index 0000000..57d89fd8
--- /dev/null
+++ b/third_party/WebKit/Source/platform/wtf/build_config.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WTF_build_config_h
+#define WTF_build_config_h
+
+#include "build/build_config.h"
+#include "platform/wtf/Compiler.h"
+
+/* ==== Platform adaptation macros: these describe properties of the target
+ * environment. ==== */
+
+/* HAVE() - specific system features (headers, functions or similar) that are
+ * present or not */
+#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE)
+/* OS() - underlying operating system; only to be used for mandated low-level
+   services like
+   virtual memory, not to choose a GUI toolkit */
+#define OS(WTF_FEATURE) (defined OS_##WTF_FEATURE && OS_##WTF_FEATURE)
+
+/* ==== Policy decision macros: these define policy choices for a particular
+ * port. ==== */
+
+/* USE() - use a particular third-party library or optional OS service */
+#define USE(WTF_FEATURE) \
+  (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE)
+/* ENABLE() - turn on a specific feature of WebKit */
+#define ENABLE(WTF_FEATURE) \
+  (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE)
+
+/* There is an assumption in the project that either OS(WIN) or OS(POSIX) is
+ * set. */
+#if !OS(WIN) && !OS(POSIX)
+#error Either OS(WIN) or OS(POSIX) needs to be set.
+#endif
+
+#endif  // WTF_build_config_h
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 0d92d85..bf4efd0 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -929,7 +929,6 @@
           frameElement->scrollingMode(), frameElement->marginWidth(),
           frameElement->marginHeight(), frameElement->allowFullscreen(),
           frameElement->allowPaymentRequest(), frameElement->csp(),
-          frameElement->delegatedPermissions(),
           frameElement->allowedFeatures()));
 }
 
diff --git a/third_party/WebKit/Source/web/PopupMenuImpl.cpp b/third_party/WebKit/Source/web/PopupMenuImpl.cpp
index 9d4a06a0..5d277c2 100644
--- a/third_party/WebKit/Source/web/PopupMenuImpl.cpp
+++ b/third_party/WebKit/Source/web/PopupMenuImpl.cpp
@@ -316,7 +316,7 @@
   PagePopupClient::addString("style: {\n", data);
   if (style->visibility() == EVisibility::kHidden)
     addProperty("visibility", String("hidden"), data);
-  if (style->display() == EDisplay::None)
+  if (style->display() == EDisplay::kNone)
     addProperty("display", String("none"), data);
   const ComputedStyle& baseStyle = context.baseStyle();
   if (baseStyle.direction() != style->direction()) {
diff --git a/third_party/WebKit/Source/web/RemoteFrameOwner.h b/third_party/WebKit/Source/web/RemoteFrameOwner.h
index 428f5b8..8d432b9 100644
--- a/third_party/WebKit/Source/web/RemoteFrameOwner.h
+++ b/third_party/WebKit/Source/web/RemoteFrameOwner.h
@@ -48,10 +48,6 @@
   bool allowFullscreen() const override { return m_allowFullscreen; }
   bool allowPaymentRequest() const override { return m_allowPaymentRequest; }
   AtomicString csp() const override { return m_csp; }
-  const WebVector<mojom::blink::PermissionName>& delegatedPermissions()
-      const override {
-    return m_delegatedPermissions;
-  }
   const WebVector<WebFeaturePolicyFeature>& allowedFeatures() const override {
     return m_allowedFeatures;
   }
@@ -69,10 +65,6 @@
     m_allowPaymentRequest = allowPaymentRequest;
   }
   void setCsp(const WebString& csp) { m_csp = csp; }
-  void setDelegatedpermissions(
-      const WebVector<mojom::blink::PermissionName>& delegatedPermissions) {
-    m_delegatedPermissions = delegatedPermissions;
-  }
   void setAllowedFeatures(
       const WebVector<WebFeaturePolicyFeature>& allowedFeatures) {
     m_allowedFeatures = allowedFeatures;
@@ -97,7 +89,6 @@
   bool m_allowFullscreen;
   bool m_allowPaymentRequest;
   WebString m_csp;
-  WebVector<mojom::blink::PermissionName> m_delegatedPermissions;
   WebVector<WebFeaturePolicyFeature> m_allowedFeatures;
 };
 
diff --git a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
index e68acb7..e50ce42be 100644
--- a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
+++ b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
@@ -374,7 +374,7 @@
   m_session->append(pageAgent);
 
   m_session->append(new InspectorLogAgent(
-      &m_inspectedFrames->root()->host()->consoleMessageStorage(),
+      &m_inspectedFrames->root()->page()->consoleMessageStorage(),
       m_inspectedFrames->root()->performanceMonitor()));
 
   m_session->append(
diff --git a/third_party/WebKit/Source/web/WebFrame.cpp b/third_party/WebKit/Source/web/WebFrame.cpp
index 02182db..099be8f5 100644
--- a/third_party/WebKit/Source/web/WebFrame.cpp
+++ b/third_party/WebKit/Source/web/WebFrame.cpp
@@ -164,7 +164,6 @@
   owner->setAllowFullscreen(properties.allowFullscreen);
   owner->setAllowPaymentRequest(properties.allowPaymentRequest);
   owner->setCsp(properties.requiredCsp);
-  owner->setDelegatedpermissions(properties.delegatedPermissions);
   owner->setAllowedFeatures(properties.allowedFeatures);
 }
 
diff --git a/third_party/WebKit/Source/web/WebInputMethodControllerImpl.cpp b/third_party/WebKit/Source/web/WebInputMethodControllerImpl.cpp
index e71d80d..7b491d2 100644
--- a/third_party/WebKit/Source/web/WebInputMethodControllerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebInputMethodControllerImpl.cpp
@@ -50,8 +50,8 @@
     int selectionStart,
     int selectionEnd) {
   if (WebPlugin* plugin = focusedPluginIfInputMethodSupported()) {
-    return plugin->setComposition(text, underlines, selectionStart,
-                                  selectionEnd);
+    return plugin->setComposition(text, underlines, replacementRange,
+                                  selectionStart, selectionEnd);
   }
 
   // We should use this |editor| object only to complete the ongoing
@@ -128,8 +128,10 @@
   UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
       frame()->document(), UserGestureToken::NewGesture));
 
-  if (WebPlugin* plugin = focusedPluginIfInputMethodSupported())
-    return plugin->commitText(text, underlines, relativeCaretPosition);
+  if (WebPlugin* plugin = focusedPluginIfInputMethodSupported()) {
+    return plugin->commitText(text, underlines, replacementRange,
+                              relativeCaretPosition);
+  }
 
   // Select the range to be replaced with the composition later.
   if (!replacementRange.isNull())
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index 01a75f3..34a3e9c3 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -1638,7 +1638,7 @@
       ownerElement->scrollingMode(), ownerElement->marginWidth(),
       ownerElement->marginHeight(), ownerElement->allowFullscreen(),
       ownerElement->allowPaymentRequest(), ownerElement->csp(),
-      ownerElement->delegatedPermissions(), ownerElement->allowedFeatures());
+      ownerElement->allowedFeatures());
   // FIXME: Using subResourceAttributeName as fallback is not a perfect
   // solution. subResourceAttributeName returns just one attribute name. The
   // element might not have the attribute, and there might be other attributes
diff --git a/third_party/WebKit/Source/wtf/AddressSanitizer.h b/third_party/WebKit/Source/wtf/AddressSanitizer.h
index 07b9934..812da44 100644
--- a/third_party/WebKit/Source/wtf/AddressSanitizer.h
+++ b/third_party/WebKit/Source/wtf/AddressSanitizer.h
@@ -1,42 +1,9 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium 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 WTF_AddressSanitizer_h
-#define WTF_AddressSanitizer_h
-// TODO(kojii): This file will need to be renamed, because it's no more
-// specific to AddressSanitizer.
+#include "platform/wtf/AddressSanitizer.h"
 
-#include "wtf/build_config.h"
-
-// TODO(sof): Add SyZyASan support?
-#if defined(ADDRESS_SANITIZER)
-#include <sanitizer/asan_interface.h>
-#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#else
-#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
-#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
-#define NO_SANITIZE_ADDRESS
-#endif
-
-#if defined(LEAK_SANITIZER)
-#include <sanitizer/lsan_interface.h>
-#else
-#define __lsan_register_root_region(addr, size) ((void)(addr), (void)(size))
-#define __lsan_unregister_root_region(addr, size) ((void)(addr), (void)(size))
-#endif
-
-#if defined(MEMORY_SANITIZER)
-#include <sanitizer/msan_interface.h>
-#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
-#else
-#define NO_SANITIZE_MEMORY
-#endif
-
-#if defined(THREAD_SANITIZER)
-#define NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
-#else
-#define NO_SANITIZE_THREAD
-#endif
-
-#endif  // WTF_AddressSanitizer_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/Alignment.h b/third_party/WebKit/Source/wtf/Alignment.h
index c2ea860..a6f8295f 100644
--- a/third_party/WebKit/Source/wtf/Alignment.h
+++ b/third_party/WebKit/Source/wtf/Alignment.h
@@ -1,94 +1,9 @@
-/*
- *  Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public License
- *  along with this library; see the file COPYING.LIB.  If not, write to
- *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- *  Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium 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 WTF_Alignment_h
-#define WTF_Alignment_h
+#include "platform/wtf/Alignment.h"
 
-#include "wtf/Compiler.h"
-#include <algorithm>
-#include <stdint.h>
-#include <utility>
-
-namespace WTF {
-
-#if COMPILER(GCC)
-#define WTF_ALIGN_OF(type) __alignof__(type)
-#define WTF_ALIGNED(variable_type, variable, n) \
-  variable_type variable __attribute__((__aligned__(n)))
-#elif COMPILER(MSVC)
-#define WTF_ALIGN_OF(type) __alignof(type)
-#define WTF_ALIGNED(variable_type, variable, n) \
-  __declspec(align(n)) variable_type variable
-#else
-#error WTF_ALIGN macros need alignment control.
-#endif
-
-#if COMPILER(GCC)
-typedef char __attribute__((__may_alias__)) AlignedBufferChar;
-#else
-typedef char AlignedBufferChar;
-#endif
-
-template <size_t size, size_t alignment>
-struct AlignedBuffer;
-template <size_t size>
-struct AlignedBuffer<size, 1> {
-  AlignedBufferChar buffer[size];
-};
-template <size_t size>
-struct AlignedBuffer<size, 2> {
-  WTF_ALIGNED(AlignedBufferChar, buffer[size], 2);
-};
-template <size_t size>
-struct AlignedBuffer<size, 4> {
-  WTF_ALIGNED(AlignedBufferChar, buffer[size], 4);
-};
-template <size_t size>
-struct AlignedBuffer<size, 8> {
-  WTF_ALIGNED(AlignedBufferChar, buffer[size], 8);
-};
-template <size_t size>
-struct AlignedBuffer<size, 16> {
-  WTF_ALIGNED(AlignedBufferChar, buffer[size], 16);
-};
-template <size_t size>
-struct AlignedBuffer<size, 32> {
-  WTF_ALIGNED(AlignedBufferChar, buffer[size], 32);
-};
-template <size_t size>
-struct AlignedBuffer<size, 64> {
-  WTF_ALIGNED(AlignedBufferChar, buffer[size], 64);
-};
-
-template <size_t size, size_t alignment>
-void swap(AlignedBuffer<size, alignment>& a,
-          AlignedBuffer<size, alignment>& b) {
-  for (size_t i = 0; i < size; ++i)
-    std::swap(a.buffer[i], b.buffer[i]);
-}
-
-template <uintptr_t mask>
-inline bool isAlignedTo(const void* pointer) {
-  return !(reinterpret_cast<uintptr_t>(pointer) & mask);
-}
-
-}  // namespace WTF
-
-#endif  // WTF_Alignment_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/AutoReset.h b/third_party/WebKit/Source/wtf/AutoReset.h
index b74eaf8..9af14ce 100644
--- a/third_party/WebKit/Source/wtf/AutoReset.h
+++ b/third_party/WebKit/Source/wtf/AutoReset.h
@@ -1,42 +1,9 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 AutoReset_h
-#define AutoReset_h
+#include "platform/wtf/AutoReset.h"
 
-#include "base/auto_reset.h"
-
-namespace WTF {
-
-// WTF::AutoReset is base::AutoReset. See base/auto_reset.h for documentation.
-
-template <typename T>
-using AutoReset = base::AutoReset<T>;
-
-}  // namespace WTF
-
-using WTF::AutoReset;
-
-#endif
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/BUILD.gn b/third_party/WebKit/Source/wtf/BUILD.gn
index 44973f2..7878ea1 100644
--- a/third_party/WebKit/Source/wtf/BUILD.gn
+++ b/third_party/WebKit/Source/wtf/BUILD.gn
@@ -39,7 +39,6 @@
     "ConditionalDestructor.h",
     "ContainerAnnotations.h",
     "CryptographicallyRandomNumber.h",
-    "CurrentTime.cpp",
     "CurrentTime.h",
     "DataLog.cpp",
     "DataLog.h",
@@ -47,7 +46,6 @@
     "DateMath.h",
     "Deque.h",
     "DoublyLinkedList.h",
-    "DynamicAnnotations.cpp",
     "DynamicAnnotations.h",
     "FilePrintStream.cpp",
     "FilePrintStream.h",
diff --git a/third_party/WebKit/Source/wtf/BitwiseOperations.h b/third_party/WebKit/Source/wtf/BitwiseOperations.h
index e07227a..9e313a571 100644
--- a/third_party/WebKit/Source/wtf/BitwiseOperations.h
+++ b/third_party/WebKit/Source/wtf/BitwiseOperations.h
@@ -1,52 +1,9 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
 
-// TODO(palmer): The only caller of this code in Blink is PartitionAlloc. When
-// PA is moved to base, we can remove this file.
-// https://bugs.chromium.org/p/chromium/issues/detail?id=632441
+#include "platform/wtf/WTFExport.h"
 
-#ifndef WTF_BitwiseOperations_h
-#define WTF_BitwiseOperations_h
-
-#include "base/bits.h"
-#include "wtf/CPU.h"
-
-namespace WTF {
-
-using base::bits::CountLeadingZeroBits32;
-using base::bits::CountLeadingZeroBitsSizeT;
-
-#if CPU(64BIT)
-using base::bits::CountLeadingZeroBits64;
-#endif
-
-}  // namespace WTF
-
-#endif  // WTF_BitwiseOperations_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/ByteSwap.h b/third_party/WebKit/Source/wtf/ByteSwap.h
index 3f4462c..21a7fe7 100644
--- a/third_party/WebKit/Source/wtf/ByteSwap.h
+++ b/third_party/WebKit/Source/wtf/ByteSwap.h
@@ -1,91 +1,9 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 WTF_ByteSwap_h
-#define WTF_ByteSwap_h
+#include "platform/wtf/ByteSwap.h"
 
-#include "wtf/CPU.h"
-#include "wtf/Compiler.h"
-
-#include <stdint.h>
-
-#if COMPILER(MSVC)
-#include <stdlib.h>
-#endif
-
-namespace WTF {
-
-inline uint32_t wswap32(uint32_t x) {
-  return ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16);
-}
-
-#if COMPILER(MSVC)
-
-ALWAYS_INLINE uint64_t bswap64(uint64_t x) {
-  return _byteswap_uint64(x);
-}
-ALWAYS_INLINE uint32_t bswap32(uint32_t x) {
-  return _byteswap_ulong(x);
-}
-ALWAYS_INLINE uint16_t bswap16(uint16_t x) {
-  return _byteswap_ushort(x);
-}
-
-#else
-
-ALWAYS_INLINE uint64_t bswap64(uint64_t x) {
-  return __builtin_bswap64(x);
-}
-ALWAYS_INLINE uint32_t bswap32(uint32_t x) {
-  return __builtin_bswap32(x);
-}
-ALWAYS_INLINE uint16_t bswap16(uint16_t x) {
-  return __builtin_bswap16(x);
-}
-
-#endif
-
-#if CPU(64BIT)
-
-ALWAYS_INLINE size_t bswapuintptrt(size_t x) {
-  return bswap64(x);
-}
-
-#else
-
-ALWAYS_INLINE size_t bswapuintptrt(size_t x) {
-  return bswap32(x);
-}
-
-#endif
-
-}  // namespace WTF
-
-#endif  // WTF_ByteSwap_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/CPU.h b/third_party/WebKit/Source/wtf/CPU.h
index daedf8e..b617339 100644
--- a/third_party/WebKit/Source/wtf/CPU.h
+++ b/third_party/WebKit/Source/wtf/CPU.h
@@ -1,171 +1,9 @@
-/*
- * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
- * Copyright (C) 2007-2009 Torch Mobile, Inc.
- * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
- * Copyright (C) 2013 Samsung Electronics. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 WTF_CPU_h
-#define WTF_CPU_h
+#include "platform/wtf/CPU.h"
 
-#include "wtf/Compiler.h"
-
-/* CPU() - the target CPU architecture */
-#define CPU(WTF_FEATURE) \
-  (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE)
-
-/* ==== CPU() - the target CPU architecture ==== */
-
-/* This defines CPU(BIG_ENDIAN) or nothing, as appropriate. */
-/* This defines CPU(32BIT) or CPU(64BIT), as appropriate. */
-
-/* CPU(X86) - i386 / x86 32-bit */
-#if defined(__i386__) || defined(i386) || defined(_M_IX86) || \
-    defined(_X86_) || defined(__THW_INTEL)
-#define WTF_CPU_X86 1
-#endif
-
-/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */
-#if defined(__x86_64__) || defined(_M_X64)
-#define WTF_CPU_X86_64 1
-#define WTF_CPU_64BIT 1
-#endif
-
-/* CPU(ARM) - ARM, any version*/
-#define WTF_ARM_ARCH_AT_LEAST(N) \
-  (CPU(ARM) && defined(WTF_ARM_ARCH_VERSION) && WTF_ARM_ARCH_VERSION >= N)
-
-#if defined(arm) || defined(__arm__) || defined(ARM) || defined(_ARM_)
-#define WTF_CPU_ARM 1
-
-#if defined(__ARMEB__)
-#define WTF_CPU_BIG_ENDIAN 1
-
-#elif !defined(__ARM_EABI__) && !defined(__EABI__) && !defined(__VFP_FP__) && \
-    !defined(_WIN32_WCE) && !defined(ANDROID)
-#define WTF_CPU_MIDDLE_ENDIAN 1
-
-#endif
-
-/* Set WTF_ARM_ARCH_VERSION */
-#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
-    defined(__MARM_ARMV4__)
-#define WTF_ARM_ARCH_VERSION 4
-
-#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
-    defined(__MARM_ARMV5__)
-#define WTF_ARM_ARCH_VERSION 5
-
-#elif defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) || \
-    defined(__ARM_ARCH_5TEJ__)
-#define WTF_ARM_ARCH_VERSION 5
-
-#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) ||  \
-    defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) ||   \
-    defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \
-    defined(__ARMV6__)
-#define WTF_ARM_ARCH_VERSION 6
-
-#elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \
-    defined(__ARM_ARCH_7S__)
-#define WTF_ARM_ARCH_VERSION 7
-
-/* MSVC sets _M_ARM */
-#elif defined(_M_ARM)
-#define WTF_ARM_ARCH_VERSION _M_ARM
-#else
-#define WTF_ARM_ARCH_VERSION 0
-
-#endif
-
-/* Set WTF_THUMB_ARCH_VERSION */
-#if defined(__ARM_ARCH_4T__)
-#define WTF_THUMB_ARCH_VERSION 1
-
-#elif defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) || \
-    defined(__ARM_ARCH_5TEJ__)
-#define WTF_THUMB_ARCH_VERSION 2
-
-#elif defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || \
-    defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) ||  \
-    defined(__ARM_ARCH_6M__)
-#define WTF_THUMB_ARCH_VERSION 3
-
-#elif defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_7__) || \
-    defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7M__) ||   \
-    defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7S__)
-#define WTF_THUMB_ARCH_VERSION 4
-
-#else
-#define WTF_THUMB_ARCH_VERSION 0
-#endif
-
-/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */
-#if !defined(WTF_CPU_ARM_THUMB2)
-#if defined(thumb2) || defined(__thumb2__) || \
-    ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4)
-#define WTF_CPU_ARM_THUMB2 1
-#elif WTF_ARM_ARCH_AT_LEAST(4)
-#define WTF_CPU_ARM_THUMB2 0
-#else
-#error "Unsupported ARM architecture"
-#endif
-#endif /* !defined(WTF_CPU_ARM_THUMB2) */
-
-#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON)
-#define WTF_CPU_ARM_NEON 1
-#endif
-
-#if CPU(ARM_NEON) && \
-    (COMPILER(CLANG) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0))
-// All NEON intrinsics usage can be disabled by this macro.
-#define HAVE_ARM_NEON_INTRINSICS 1
-#endif
-
-#endif /* ARM */
-
-/* CPU(ARM64) - AArch64 64-bit */
-#if defined(__aarch64__)
-#define WTF_CPU_ARM64 1
-#define WTF_CPU_64BIT 1
-#endif
-
-/* CPU(MIPS), CPU(MIPS64) */
-#if defined(__mips__) && (__mips == 64)
-#define WTF_CPU_MIPS64 1
-#define WTF_CPU_64BIT 1
-#elif defined(__mips__)
-#define WTF_CPU_MIPS 1
-#endif
-
-#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
-// All MSA intrinsics usage can be disabled by this macro.
-#define HAVE_MIPS_MSA_INTRINSICS 1
-#endif
-
-#if !defined(WTF_CPU_64BIT)
-#define WTF_CPU_32BIT 1
-#endif
-
-#endif /* WTF_CPU_h */
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/CheckedNumeric.h b/third_party/WebKit/Source/wtf/CheckedNumeric.h
index f94ba48..4fe900f8 100644
--- a/third_party/WebKit/Source/wtf/CheckedNumeric.h
+++ b/third_party/WebKit/Source/wtf/CheckedNumeric.h
@@ -1,39 +1,9 @@
-/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 WTF_CheckedNumeric_h
-#define WTF_CheckedNumeric_h
+#include "platform/wtf/CheckedNumeric.h"
 
-/* See base/numerics/safe_math.h for usage.
- */
-#include "base/numerics/safe_math.h"
-
-namespace WTF {
-using base::CheckedNumeric;
-}  // namespace WTF
-
-using WTF::CheckedNumeric;
-
-#endif
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/Compiler.h b/third_party/WebKit/Source/wtf/Compiler.h
index 1b81bb9..a554dcd1 100644
--- a/third_party/WebKit/Source/wtf/Compiler.h
+++ b/third_party/WebKit/Source/wtf/Compiler.h
@@ -1,100 +1,9 @@
-/*
- * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 WTF_Compiler_h
-#define WTF_Compiler_h
+#include "platform/wtf/Compiler.h"
 
-#include "base/compiler_specific.h"
-
-/* COMPILER() - the compiler being used to build the project */
-#define COMPILER(WTF_FEATURE) \
-  (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE)
-
-/* ==== COMPILER() - the compiler being used to build the project ==== */
-
-/* COMPILER(CLANG) - Clang  */
-#if defined(__clang__)
-#define WTF_COMPILER_CLANG 1
-#endif
-
-/* COMPILER(MSVC) - Microsoft Visual C++ (and Clang when compiling for Windows).
- */
-#if defined(_MSC_VER)
-#define WTF_COMPILER_MSVC 1
-#endif
-
-/* COMPILER(GCC) - GNU Compiler Collection (and Clang when compiling for
- * platforms other than Windows). */
-#if defined(__GNUC__)
-#define WTF_COMPILER_GCC 1
-#define GCC_VERSION \
-  (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
-#define GCC_VERSION_AT_LEAST(major, minor, patch) \
-  (GCC_VERSION >= (major * 10000 + minor * 100 + patch))
-#else
-/* Define this for !GCC compilers, just so we can write things like
- * GCC_VERSION_AT_LEAST(4, 1, 0). */
-#define GCC_VERSION_AT_LEAST(major, minor, patch) 0
-#endif
-
-/* ==== Compiler features ==== */
-
-/* NEVER_INLINE */
-
-// TODO(palmer): Remove this and update callers to use NOINLINE from Chromium
-// base. https://bugs.chromium.org/p/chromium/issues/detail?id=632441
-#define NEVER_INLINE NOINLINE
-
-/* OBJC_CLASS */
-
-#ifndef OBJC_CLASS
-#ifdef __OBJC__
-#define OBJC_CLASS @class
-#else
-#define OBJC_CLASS class
-#endif
-#endif
-
-/* WTF_PRETTY_FUNCTION */
-
-#if COMPILER(GCC)
-#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__
-#elif COMPILER(MSVC)
-#define WTF_PRETTY_FUNCTION __FUNCSIG__
-#else
-#define WTF_PRETTY_FUNCTION __func__
-#endif
-
-/* NO_SANITIZE_UNRELATED_CAST - Disable runtime checks related to casts between
- * unrelated objects (-fsanitize=cfi-unrelated-cast or -fsanitize=vptr). */
-
-#if COMPILER(CLANG)
-#define NO_SANITIZE_UNRELATED_CAST \
-  __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
-#else
-#define NO_SANITIZE_UNRELATED_CAST
-#endif
-
-#endif /* WTF_Compiler_h */
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/ConditionalDestructor.h b/third_party/WebKit/Source/wtf/ConditionalDestructor.h
index c2fdd1c5..fc4ba2a4 100644
--- a/third_party/WebKit/Source/wtf/ConditionalDestructor.h
+++ b/third_party/WebKit/Source/wtf/ConditionalDestructor.h
@@ -1,28 +1,9 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium 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 ConditionalDestructor_h
-#define ConditionalDestructor_h
+#include "platform/wtf/ConditionalDestructor.h"
 
-namespace WTF {
-
-// ConditionalDestructor defines the destructor of the derived object.
-// This base is used in order to completely avoid creating a destructor
-// for an object that does not need to be destructed. By doing so,
-// the clang compiler will have correct information about whether or not
-// the object has a trivial destructor.
-// Note: the derived object MUST release all its recources at the finalize()
-// method.
-template <typename Derived, bool noDestructor>
-class ConditionalDestructor {
- public:
-  ~ConditionalDestructor() { static_cast<Derived*>(this)->finalize(); }
-};
-
-template <typename Derived>
-class ConditionalDestructor<Derived, true> {};
-
-}  // namespace WTF
-
-#endif  // ConditionalDestructor_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/ContainerAnnotations.h b/third_party/WebKit/Source/wtf/ContainerAnnotations.h
index 401cc60..ff2a3cb 100644
--- a/third_party/WebKit/Source/wtf/ContainerAnnotations.h
+++ b/third_party/WebKit/Source/wtf/ContainerAnnotations.h
@@ -1,44 +1,9 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium 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 WTF_ContainerAnnotations_h
-#define WTF_ContainerAnnotations_h
+#include "platform/wtf/ContainerAnnotations.h"
 
-#include "wtf/AddressSanitizer.h"
-#include "wtf/CPU.h"
-
-// TODO(ochang): Remove the CPU(X86_64) condition to enable this for X86 once
-// the crashes there have been fixed: http://crbug.com/461406
-#if defined(ADDRESS_SANITIZER) && OS(LINUX) && CPU(X86_64)
-#define ANNOTATE_CONTIGUOUS_CONTAINER
-#define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)                       \
-  if (buffer) {                                                              \
-    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
-                                              (buffer) + (capacity),         \
-                                              (buffer) + (newSize));         \
-  }
-#define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)                    \
-  if (buffer) {                                                              \
-    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
-                                              (buffer) + (oldSize),          \
-                                              (buffer) + (capacity));        \
-  }
-#define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)             \
-  if (buffer) {                                                              \
-    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
-                                              (buffer) + (oldSize),          \
-                                              (buffer) + (newSize));         \
-  }
-#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity) \
-  ANNOTATE_DELETE_BUFFER(buffer, oldCapacity, bufferSize);                     \
-  ANNOTATE_NEW_BUFFER(buffer, newCapacity, bufferSize);
-// Annotations require buffers to begin on an 8-byte boundary.
-#else  // defined(ADDRESS_SANITIZER) && OS(LINUX) && CPU(X86_64)
-#define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)
-#define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)
-#define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)
-#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity)
-#endif  // defined(ADDRESS_SANITIZER) && OS(LINUX) && CPU(X86_64)
-
-#endif  // WTF_ContainerAnnotations_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/CurrentTime.h b/third_party/WebKit/Source/wtf/CurrentTime.h
index b1acf736..d57fec06 100644
--- a/third_party/WebKit/Source/wtf/CurrentTime.h
+++ b/third_party/WebKit/Source/wtf/CurrentTime.h
@@ -1,79 +1,9 @@
-/*
- * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
- * Copyright (C) 2008 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 CurrentTime_h
-#define CurrentTime_h
+#include "platform/wtf/CurrentTime.h"
 
-#include "wtf/WTFExport.h"
-
-namespace WTF {
-
-// Returns the current UTC time in seconds, counted from January 1, 1970.
-// Precision varies depending on platform but is usually as good or better
-// than a millisecond.
-WTF_EXPORT double currentTime();
-
-// Same thing, in milliseconds.
-inline double currentTimeMS() {
-  return currentTime() * 1000.0;
-}
-
-// Provides a monotonically increasing time in seconds since an arbitrary point
-// in the past.  On unsupported platforms, this function only guarantees the
-// result will be non-decreasing.
-WTF_EXPORT double monotonicallyIncreasingTime();
-
-// Same thing, in milliseconds.
-inline double monotonicallyIncreasingTimeMS() {
-  return monotonicallyIncreasingTime() * 1000.0;
-}
-
-using TimeFunction = double (*)();
-
-// Make all the time functions (currentTime(), monotonicallyIncreasingTime(),
-// systemTraceTime()) return the result of the supplied function. Returns the
-// pointer to the old time function. For both setting and getting, nullptr
-// means using the default timing function returning the actual time.
-WTF_EXPORT TimeFunction setTimeFunctionsForTesting(TimeFunction);
-
-// Allows wtf/Time.h to use the same mock time function
-WTF_EXPORT TimeFunction getTimeFunctionForTesting();
-
-}  // namespace WTF
-
-using WTF::currentTime;
-using WTF::currentTimeMS;
-using WTF::monotonicallyIncreasingTime;
-using WTF::monotonicallyIncreasingTimeMS;
-using WTF::TimeFunction;
-using WTF::setTimeFunctionsForTesting;
-
-#endif  // CurrentTime_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/DynamicAnnotations.h b/third_party/WebKit/Source/wtf/DynamicAnnotations.h
index 33e9cd3..a7fc401 100644
--- a/third_party/WebKit/Source/wtf/DynamicAnnotations.h
+++ b/third_party/WebKit/Source/wtf/DynamicAnnotations.h
@@ -1,114 +1,9 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 WTF_DynamicAnnotations_h
-#define WTF_DynamicAnnotations_h
+#include "platform/wtf/DynamicAnnotations.h"
 
-/* This file defines dynamic annotations for use with dynamic analysis
- * tool such as ThreadSanitizer, Valgrind, etc.
- *
- * Dynamic annotation is a source code annotation that affects
- * the generated code (that is, the annotation is not a comment).
- * Each such annotation is attached to a particular
- * instruction and/or to a particular object (address) in the program.
- *
- * By using dynamic annotations a developer can give more details to the dynamic
- * analysis tool to improve its precision.
- *
- * In C/C++ program the annotations are represented as C macros.
- * With the default build flags, these macros are empty, hence don't affect
- * performance of a compiled binary.
- * If dynamic annotations are enabled, they just call no-op functions.
- * The dynamic analysis tools can intercept these functions and replace them
- * with their own implementations.
- *
- * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more
- * information.
- */
-
-#include "wtf/WTFExport.h"
-#include "wtf/build_config.h"
-
-#if USE(DYNAMIC_ANNOTATIONS)
-/* Tell data race detector that we're not interested in reports on the given
- * address range. */
-#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
-  WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
-#define WTF_ANNOTATE_BENIGN_RACE(pointer, description)                        \
-  WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), \
-                             description)
-
-/* Annotations for user-defined synchronization mechanisms.
- * These annotations can be used to define happens-before arcs in user-defined
- * synchronization mechanisms: the race detector will infer an arc from
- * the former to the latter when they share the same argument pointer.
- *
- * The most common case requiring annotations is atomic reference counting:
- * bool deref() {
- *     ANNOTATE_HAPPENS_BEFORE(&m_refCount);
- *     if (!atomicDecrement(&m_refCount)) {
- *         // m_refCount is now 0
- *         ANNOTATE_HAPPENS_AFTER(&m_refCount);
- *         // "return true; happens-after each atomicDecrement of m_refCount"
- *         return true;
- *     }
- *     return false;
- * }
- */
-#define WTF_ANNOTATE_HAPPENS_BEFORE(address) \
-  WTFAnnotateHappensBefore(__FILE__, __LINE__, address)
-#define WTF_ANNOTATE_HAPPENS_AFTER(address) \
-  WTFAnnotateHappensAfter(__FILE__, __LINE__, address)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-/* Don't use these directly, use the above macros instead. */
-WTF_EXPORT void WTFAnnotateBenignRaceSized(const char* file,
-                                           int line,
-                                           const volatile void* memory,
-                                           long size,
-                                           const char* description);
-WTF_EXPORT void WTFAnnotateHappensBefore(const char* file,
-                                         int line,
-                                         const volatile void* address);
-WTF_EXPORT void WTFAnnotateHappensAfter(const char* file,
-                                        int line,
-                                        const volatile void* address);
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#else  // USE(DYNAMIC_ANNOTATIONS)
-/* These macros are empty when dynamic annotations are not enabled so you can
- * use them without affecting the performance of release binaries. */
-#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description)
-#define WTF_ANNOTATE_BENIGN_RACE(pointer, description)
-#define WTF_ANNOTATE_HAPPENS_BEFORE(address)
-#define WTF_ANNOTATE_HAPPENS_AFTER(address)
-#endif  // USE(DYNAMIC_ANNOTATIONS)
-
-#endif  // WTF_DynamicAnnotations_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/Forward.h b/third_party/WebKit/Source/wtf/Forward.h
index 4eee7d7..d9e42c4 100644
--- a/third_party/WebKit/Source/wtf/Forward.h
+++ b/third_party/WebKit/Source/wtf/Forward.h
@@ -1,84 +1,9 @@
-/*
- *  Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved.
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public License
- *  along with this library; see the file COPYING.LIB.  If not, write to
- *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- *  Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium 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 WTF_Forward_h
-#define WTF_Forward_h
+#include "platform/wtf/Forward.h"
 
-#include "wtf/Compiler.h"
-#include <stddef.h>
-
-namespace WTF {
-
-template <typename T>
-class PassRefPtr;
-template <typename T>
-class RefPtr;
-template <typename T>
-class StringBuffer;
-template <typename T, size_t inlineCapacity, typename Allocator>
-class Vector;
-
-class ArrayBuffer;
-class ArrayBufferView;
-class ArrayPiece;
-class AtomicString;
-class CString;
-class Float32Array;
-class Float64Array;
-class Int8Array;
-class Int16Array;
-class Int32Array;
-class OrdinalNumber;
-class String;
-class StringBuilder;
-class StringImpl;
-class StringView;
-class Uint8Array;
-class Uint8ClampedArray;
-class Uint16Array;
-class Uint32Array;
-
-}  // namespace WTF
-
-using WTF::PassRefPtr;
-using WTF::RefPtr;
-using WTF::Vector;
-
-using WTF::ArrayBuffer;
-using WTF::ArrayBufferView;
-using WTF::ArrayPiece;
-using WTF::AtomicString;
-using WTF::CString;
-using WTF::Float32Array;
-using WTF::Float64Array;
-using WTF::Int8Array;
-using WTF::Int16Array;
-using WTF::Int32Array;
-using WTF::String;
-using WTF::StringBuffer;
-using WTF::StringBuilder;
-using WTF::StringImpl;
-using WTF::StringView;
-using WTF::Uint8Array;
-using WTF::Uint8ClampedArray;
-using WTF::Uint16Array;
-using WTF::Uint32Array;
-
-#endif  // WTF_Forward_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/GetPtr.h b/third_party/WebKit/Source/wtf/GetPtr.h
index f211f20..fc06e5c 100644
--- a/third_party/WebKit/Source/wtf/GetPtr.h
+++ b/third_party/WebKit/Source/wtf/GetPtr.h
@@ -1,38 +1,9 @@
-/*
- *  Copyright (C) 2006 Apple Computer, Inc.
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public License
- *  along with this library; see the file COPYING.LIB.  If not, write to
- *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- *  Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium 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 WTF_GetPtr_h
-#define WTF_GetPtr_h
+#include "platform/wtf/GetPtr.h"
 
-namespace WTF {
-
-template <typename T>
-inline T* getPtr(T* p) {
-  return p;
-}
-
-template <typename T>
-inline T* getPtr(T& p) {
-  return &p;
-}
-
-}  // namespace WTF
-
-#endif  // WTF_GetPtr_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/LeakAnnotations.h b/third_party/WebKit/Source/wtf/LeakAnnotations.h
index d218794..f43de27 100644
--- a/third_party/WebKit/Source/wtf/LeakAnnotations.h
+++ b/third_party/WebKit/Source/wtf/LeakAnnotations.h
@@ -1,142 +1,9 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- * Copyright (C) 2013 Samsung Electronics. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright 2017 The Chromium 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 WTF_LeakAnnotations_h
-#define WTF_LeakAnnotations_h
+#include "platform/wtf/LeakAnnotations.h"
 
-// This file defines macros for working with LeakSanitizer, allowing memory
-// and allocations to be registered as exempted from LSan consideration.
-
-#include "wtf/Noncopyable.h"
-#if defined(LEAK_SANITIZER)
-#include "wtf/AddressSanitizer.h"
-#include "wtf/TypeTraits.h"
-#endif
-
-namespace WTF {
-
-#if defined(LEAK_SANITIZER)
-class LeakSanitizerDisabler {
-  WTF_MAKE_NONCOPYABLE(LeakSanitizerDisabler);
-
- public:
-  LeakSanitizerDisabler() { __lsan_disable(); }
-
-  ~LeakSanitizerDisabler() { __lsan_enable(); }
-};
-
-// WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE: all allocations made in the
-// current scope will be exempted from LSan consideration. Only to be
-// used internal to wtf/, Blink should use LEAK_SANITIZER_DISABLED_SCOPE
-// elsewhere.
-//
-// TODO(sof): once layering rules allow wtf/ to make use of the Oilpan
-// infrastructure, remove this macro.
-#define WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE  \
-  WTF::LeakSanitizerDisabler leakSanitizerDisabler; \
-  static_cast<void>(0)
-
-// LEAK_SANITIZER_IGNORE_OBJECT(X): the heap object referenced by pointer X
-// will be ignored by LSan.
-//
-// "Ignorance" means that LSan's reachability traversal is stopped short
-// upon encountering an ignored memory chunk. Consequently, LSan will not
-// scan an ignored memory chunk for live, reachable pointers. However, should
-// those embedded pointers be reachable by some other path, they will be
-// reported as leaking.
-#define LEAK_SANITIZER_IGNORE_OBJECT(X) __lsan_ignore_object(X)
-
-// If the object pointed to by the static local is on the Oilpan heap, a strong
-// Persistent<> is created to keep the pointed-to heap object alive. This makes
-// both the Persistent<> and the heap object _reachable_ by LeakSanitizer's leak
-// detection pass. We do not want these intentional leaks to be reported by
-// LSan, hence the static local is registered with Oilpan
-// (see RegisterStaticLocalReference<> below.)
-//
-// Upon Blink shutdown, all the registered statics are released and a final
-// round of GCs are performed to sweep out their now-unreachable object graphs.
-// The end result being a tidied heap that the LeakSanitizer can then scan to
-// report real leaks.
-//
-// The CanRegisterStaticLocalReference<> and RegisterStaticLocalReference<>
-// templates arrange for this -- for a class type T, a registerStatic()
-// implementation is provided if "T* T::registerAsStaticReference(T*)" is a
-// method on T (inherited or otherwise.)
-//
-// An empty, trivial registerStatic() method is provided for all other class
-// types T.
-template <typename T>
-class CanRegisterStaticLocalReference {
-  typedef char YesType;
-  typedef struct NoType { char padding[8]; } NoType;
-
-  // Check if class T has public method "T* registerAsStaticReference()".
-  template <typename V>
-  static YesType checkHasRegisterAsStaticReferenceMethod(
-      V* p,
-      typename std::enable_if<IsSubclass<
-          V,
-          typename std::remove_pointer<decltype(
-              p->registerAsStaticReference())>::type>::value>::type* = 0);
-  template <typename V>
-  static NoType checkHasRegisterAsStaticReferenceMethod(...);
-
- public:
-  static const bool value =
-      sizeof(YesType) + sizeof(T) ==
-      sizeof(checkHasRegisterAsStaticReferenceMethod<T>(nullptr)) + sizeof(T);
-};
-
-template <typename T, bool = CanRegisterStaticLocalReference<T>::value>
-class RegisterStaticLocalReference {
- public:
-  static T* registerStatic(T* ptr) { return ptr; }
-};
-
-template <typename T>
-class RegisterStaticLocalReference<T, true> {
- public:
-  static T* registerStatic(T* ptr) {
-    return static_cast<T*>(ptr->registerAsStaticReference());
-  }
-};
-
-#define LEAK_SANITIZER_REGISTER_STATIC_LOCAL(Type, Object) \
-  WTF::RegisterStaticLocalReference<Type>::registerStatic(Object)
-#else
-#define WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE
-#define LEAK_SANITIZER_IGNORE_OBJECT(X) ((void)0)
-#define LEAK_SANITIZER_REGISTER_STATIC_LOCAL(Type, Object) Object
-#endif  // defined(LEAK_SANITIZER)
-
-}  // namespace WTF
-
-#endif  // WTF_LeakAnnotations_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/Noncopyable.h b/third_party/WebKit/Source/wtf/Noncopyable.h
index dade16e..e49a54b 100644
--- a/third_party/WebKit/Source/wtf/Noncopyable.h
+++ b/third_party/WebKit/Source/wtf/Noncopyable.h
@@ -1,29 +1,9 @@
-/*
- *  Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public License
- *  along with this library; see the file COPYING.LIB.  If not, write to
- *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- *  Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium 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 WTF_Noncopyable_h
-#define WTF_Noncopyable_h
+#include "platform/wtf/Noncopyable.h"
 
-#define WTF_MAKE_NONCOPYABLE(ClassName) \
- private:                               \
-  ClassName(const ClassName&) = delete; \
-  ClassName& operator=(const ClassName&) = delete
-
-#endif  // WTF_Noncopyable_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/TypeTraits.h b/third_party/WebKit/Source/wtf/TypeTraits.h
index 987d3d36..1fafd32a 100644
--- a/third_party/WebKit/Source/wtf/TypeTraits.h
+++ b/third_party/WebKit/Source/wtf/TypeTraits.h
@@ -1,383 +1,9 @@
-/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
- * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB.  If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium 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 TypeTraits_h
-#define TypeTraits_h
+#include "platform/wtf/TypeTraits.h"
 
-#include <cstddef>
-#include <type_traits>
-#include <utility>
-
-#include "wtf/Compiler.h"
-
-namespace WTF {
-
-// Returns a string that contains the type name of |T| as a substring.
-template <typename T>
-inline const char* getStringWithTypeName() {
-  return WTF_PRETTY_FUNCTION;
-}
-
-template <typename T>
-struct IsWeak {
-  static const bool value = false;
-};
-
-enum WeakHandlingFlag {
-  NoWeakHandlingInCollections,
-  WeakHandlingInCollections
-};
-
-template <typename T, typename From>
-class IsAssignable {
-  typedef char YesType;
-  struct NoType {
-    char padding[8];
-  };
-
-  template <typename T2,
-            typename From2,
-            typename = decltype(std::declval<T2&>() = std::declval<From2>())>
-  static YesType checkAssignability(int);
-  template <typename T2, typename From2>
-  static NoType checkAssignability(...);
-
- public:
-  static const bool value =
-      sizeof(checkAssignability<T, From>(0)) == sizeof(YesType);
-};
-
-template <typename T>
-struct IsCopyAssignable {
-  static_assert(!std::is_reference<T>::value, "T must not be a reference.");
-  static const bool value = IsAssignable<T, const T&>::value;
-};
-
-template <typename T>
-struct IsMoveAssignable {
-  static_assert(!std::is_reference<T>::value, "T must not be a reference.");
-  static const bool value = IsAssignable<T, T&&>::value;
-};
-
-template <typename T>
-struct IsTriviallyCopyAssignable {
-  static const bool value =
-      __has_trivial_assign(T) && IsCopyAssignable<T>::value;
-};
-
-template <typename T>
-struct IsTriviallyMoveAssignable {
-  // TODO(yutak): This isn't really correct, because __has_trivial_assign
-  // appears to look only at copy assignment.  However,
-  // std::is_trivially_move_assignable isn't available at this moment, and
-  // there isn't a good way to write that ourselves.
-  //
-  // Here we use IsTriviallyCopyAssignable as a conservative approximation: if T
-  // is trivially copy assignable, T is trivially move assignable, too. This
-  // definition misses a case where T is trivially move-only assignable, but
-  // such cases should be rare.
-  static const bool value = IsTriviallyCopyAssignable<T>::value;
-};
-
-template <typename T>
-class IsDestructible {
-  typedef char YesType;
-  struct NoType {
-    char padding[8];
-  };
-
-  template <typename T2, typename = decltype(std::declval<T2>().~T2())>
-  static YesType checkDestructibility(int);
-  template <typename T2>
-  static NoType checkDestructibility(...);
-
- public:
-  static const bool value =
-      sizeof(checkDestructibility<T>(0)) == sizeof(YesType);
-};
-
-template <typename T>
-struct IsTriviallyDefaultConstructible {
-  static const bool value =
-      __has_trivial_constructor(T) && std::is_constructible<T>::value;
-};
-
-template <typename T>
-struct IsTriviallyDestructible {
-  static const bool value =
-      __has_trivial_destructor(T) && IsDestructible<T>::value;
-};
-
-template <typename T, typename U>
-struct IsSubclass {
- private:
-  typedef char YesType;
-  struct NoType {
-    char padding[8];
-  };
-
-  static YesType subclassCheck(U*);
-  static NoType subclassCheck(...);
-  static T* t;
-
- public:
-  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
-};
-
-template <typename T, template <typename... V> class U>
-struct IsSubclassOfTemplate {
- private:
-  typedef char YesType;
-  struct NoType {
-    char padding[8];
-  };
-
-  template <typename... W>
-  static YesType subclassCheck(U<W...>*);
-  static NoType subclassCheck(...);
-  static T* t;
-
- public:
-  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
-};
-
-template <typename T, template <typename V, size_t W> class U>
-struct IsSubclassOfTemplateTypenameSize {
- private:
-  typedef char YesType;
-  struct NoType {
-    char padding[8];
-  };
-
-  template <typename X, size_t Y>
-  static YesType subclassCheck(U<X, Y>*);
-  static NoType subclassCheck(...);
-  static T* t;
-
- public:
-  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
-};
-
-template <typename T, template <typename V, size_t W, typename X> class U>
-struct IsSubclassOfTemplateTypenameSizeTypename {
- private:
-  typedef char YesType;
-  struct NoType {
-    char padding[8];
-  };
-
-  template <typename Y, size_t Z, typename A>
-  static YesType subclassCheck(U<Y, Z, A>*);
-  static NoType subclassCheck(...);
-  static T* t;
-
- public:
-  static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
-};
-
-template <typename T, template <class V> class OuterTemplate>
-struct RemoveTemplate {
-  typedef T Type;
-};
-
-template <typename T, template <class V> class OuterTemplate>
-struct RemoveTemplate<OuterTemplate<T>, OuterTemplate> {
-  typedef T Type;
-};
-
-#if (COMPILER(MSVC) || !GCC_VERSION_AT_LEAST(4, 9, 0)) && !COMPILER(CLANG)
-// FIXME: MSVC bug workaround. Remove once MSVC STL is fixed.
-// FIXME: GCC before 4.9.0 seems to have the same issue.
-// C++ 2011 Spec (ISO/IEC 14882:2011(E)) 20.9.6.2 Table 51 states that
-// the template parameters shall be a complete type if they are different types.
-// However, MSVC checks for type completeness even if they are the same type.
-// Here, we use a template specialization for same type case to allow incomplete
-// types.
-
-template <typename T, typename U>
-struct IsConvertible {
-  static const bool value = std::is_convertible<T, U>::value;
-};
-
-template <typename T>
-struct IsConvertible<T, T> {
-  static const bool value = true;
-};
-
-#define EnsurePtrConvertibleArgDecl(From, To)                             \
-  typename std::enable_if<WTF::IsConvertible<From*, To*>::value>::type* = \
-      nullptr
-#define EnsurePtrConvertibleArgDefn(From, To) \
-  typename std::enable_if<WTF::IsConvertible<From*, To*>::value>::type*
-#else
-#define EnsurePtrConvertibleArgDecl(From, To)                              \
-  typename std::enable_if<std::is_convertible<From*, To*>::value>::type* = \
-      nullptr
-#define EnsurePtrConvertibleArgDefn(From, To) \
-  typename std::enable_if<std::is_convertible<From*, To*>::value>::type*
-#endif
-
-}  // namespace WTF
-
-namespace blink {
-
-class Visitor;
-
-}  // namespace blink
-
-namespace WTF {
-
-template <typename T>
-class IsTraceable {
-  typedef char YesType;
-  typedef struct NoType { char padding[8]; } NoType;
-
-  // Note that this also checks if a superclass of V has a trace method.
-  template <typename V>
-  static YesType checkHasTraceMethod(
-      V* v,
-      blink::Visitor* p = nullptr,
-      typename std::enable_if<
-          std::is_same<decltype(v->trace(p)), void>::value>::type* g = nullptr);
-  template <typename V>
-  static NoType checkHasTraceMethod(...);
-
- public:
-  // We add sizeof(T) to both sides here, because we want it to fail for
-  // incomplete types. Otherwise it just assumes that incomplete types do not
-  // have a trace method, which may not be true.
-  static const bool value = sizeof(YesType) + sizeof(T) ==
-                            sizeof(checkHasTraceMethod<T>(nullptr)) + sizeof(T);
-};
-
-// Convenience template wrapping the IsTraceableInCollection template in
-// Collection Traits. It helps make the code more readable.
-template <typename Traits>
-class IsTraceableInCollectionTrait {
- public:
-  static const bool value = Traits::template IsTraceableInCollection<>::value;
-};
-
-template <typename T, typename U>
-struct IsTraceable<std::pair<T, U>> {
-  static const bool value = IsTraceable<T>::value || IsTraceable<U>::value;
-};
-
-// This is used to check that DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects are not
-// stored in off-heap Vectors, HashTables etc.
-template <typename T>
-struct AllowsOnlyPlacementNew {
- private:
-  using YesType = char;
-  struct NoType {
-    char padding[8];
-  };
-
-  template <typename U>
-  static YesType checkMarker(typename U::IsAllowOnlyPlacementNew*);
-  template <typename U>
-  static NoType checkMarker(...);
-
- public:
-  static const bool value = sizeof(checkMarker<T>(nullptr)) == sizeof(YesType);
-};
-
-template <typename T>
-class IsGarbageCollectedType {
-  typedef char YesType;
-  typedef struct NoType { char padding[8]; } NoType;
-
-  static_assert(sizeof(T), "T must be fully defined");
-
-  using NonConstType = typename std::remove_const<T>::type;
-  template <typename U>
-  static YesType checkGarbageCollectedType(
-      typename U::IsGarbageCollectedTypeMarker*);
-  template <typename U>
-  static NoType checkGarbageCollectedType(...);
-
-  // Separately check for GarbageCollectedMixin, which declares a different
-  // marker typedef, to avoid resolution ambiguity for cases like
-  // IsGarbageCollectedType<B> over:
-  //
-  //    class A : public GarbageCollected<A>, public GarbageCollectedMixin {
-  //        USING_GARBAGE_COLLECTED_MIXIN(A);
-  //        ...
-  //    };
-  //    class B : public A, public GarbageCollectedMixin { ... };
-  //
-  template <typename U>
-  static YesType checkGarbageCollectedMixinType(
-      typename U::IsGarbageCollectedMixinMarker*);
-  template <typename U>
-  static NoType checkGarbageCollectedMixinType(...);
-
- public:
-  static const bool value =
-      (sizeof(YesType) ==
-       sizeof(checkGarbageCollectedType<NonConstType>(nullptr))) ||
-      (sizeof(YesType) ==
-       sizeof(checkGarbageCollectedMixinType<NonConstType>(nullptr)));
-};
-
-template <>
-class IsGarbageCollectedType<void> {
- public:
-  static const bool value = false;
-};
-
-template <typename T>
-class IsPersistentReferenceType {
-  typedef char YesType;
-  typedef struct NoType { char padding[8]; } NoType;
-
-  template <typename U>
-  static YesType checkPersistentReferenceType(
-      typename U::IsPersistentReferenceTypeMarker*);
-  template <typename U>
-  static NoType checkPersistentReferenceType(...);
-
- public:
-  static const bool value =
-      (sizeof(YesType) == sizeof(checkPersistentReferenceType<T>(nullptr)));
-};
-
-template <typename T,
-          bool = std::is_function<typename std::remove_const<
-                     typename std::remove_pointer<T>::type>::type>::value ||
-                 std::is_void<typename std::remove_const<
-                     typename std::remove_pointer<T>::type>::type>::value>
-class IsPointerToGarbageCollectedType {
- public:
-  static const bool value = false;
-};
-
-template <typename T>
-class IsPointerToGarbageCollectedType<T*, false> {
- public:
-  static const bool value = IsGarbageCollectedType<T>::value;
-};
-
-}  // namespace WTF
-
-using WTF::IsGarbageCollectedType;
-
-#endif  // TypeTraits_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Source/wtf/build_config.h b/third_party/WebKit/Source/wtf/build_config.h
index 5d0b90d..5980e3c 100644
--- a/third_party/WebKit/Source/wtf/build_config.h
+++ b/third_party/WebKit/Source/wtf/build_config.h
@@ -1,57 +1,9 @@
-/*
- * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc.
- * Copyright (C) 2009 Google Inc. All rights reserved.
- * Copyright (C) 2007-2009 Torch Mobile, Inc.
- * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB.  If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium 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 WTF_build_config_h
-#define WTF_build_config_h
+#include "platform/wtf/build_config.h"
 
-#include "build/build_config.h"
-#include "wtf/Compiler.h"
-
-/* ==== Platform adaptation macros: these describe properties of the target
- * environment. ==== */
-
-/* HAVE() - specific system features (headers, functions or similar) that are
- * present or not */
-#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE)
-/* OS() - underlying operating system; only to be used for mandated low-level
-   services like
-   virtual memory, not to choose a GUI toolkit */
-#define OS(WTF_FEATURE) (defined OS_##WTF_FEATURE && OS_##WTF_FEATURE)
-
-/* ==== Policy decision macros: these define policy choices for a particular
- * port. ==== */
-
-/* USE() - use a particular third-party library or optional OS service */
-#define USE(WTF_FEATURE) \
-  (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE)
-/* ENABLE() - turn on a specific feature of WebKit */
-#define ENABLE(WTF_FEATURE) \
-  (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE)
-
-/* There is an assumption in the project that either OS(WIN) or OS(POSIX) is
- * set. */
-#if !OS(WIN) && !OS(POSIX)
-#error Either OS(WIN) or OS(POSIX) needs to be set.
-#endif
-
-#endif  // WTF_build_config_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
index 831d26ce..61ca77f 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/bindings/bindings_tests.py
@@ -77,7 +77,7 @@
 
 # core/inspector/InspectorInstrumentation.idl is not a valid Blink IDL.
 NON_BLINK_IDL_FILES = frozenset([
-    'InspectorInstrumentation.idl',
+    'InstrumentingProbes.idl',
 ])
 
 COMPONENT_DIRECTORY = frozenset(['core', 'modules'])
diff --git a/third_party/WebKit/public/web/WebFrameOwnerProperties.h b/third_party/WebKit/public/web/WebFrameOwnerProperties.h
index 5ba8d741..9dce98f 100644
--- a/third_party/WebKit/public/web/WebFrameOwnerProperties.h
+++ b/third_party/WebKit/public/web/WebFrameOwnerProperties.h
@@ -8,7 +8,6 @@
 #include "../platform/WebFeaturePolicy.h"
 #include "../platform/WebString.h"
 #include "../platform/WebVector.h"
-#include "third_party/WebKit/public/platform/modules/permissions/permission.mojom-shared.h"
 
 #include <algorithm>
 
@@ -24,7 +23,6 @@
   bool allowFullscreen;
   bool allowPaymentRequest;
   WebString requiredCsp;
-  WebVector<mojom::PermissionName> delegatedPermissions;
 
  public:
   WebVector<WebFeaturePolicyFeature> allowedFeatures;
@@ -45,7 +43,6 @@
       bool allowFullscreen,
       bool allowPaymentRequest,
       const WebString& requiredCsp,
-      const WebVector<mojom::PermissionName>& delegatedPermissions,
       const WebVector<WebFeaturePolicyFeature>& allowedFeatures)
       : name(name),
         scrollingMode(static_cast<ScrollingMode>(scrollingMode)),
@@ -54,7 +51,6 @@
         allowFullscreen(allowFullscreen),
         allowPaymentRequest(allowPaymentRequest),
         requiredCsp(requiredCsp),
-        delegatedPermissions(delegatedPermissions),
         allowedFeatures(allowedFeatures) {}
 #endif
 };
diff --git a/third_party/WebKit/public/web/WebPlugin.h b/third_party/WebKit/public/web/WebPlugin.h
index 8d2078b..6e6403a7 100644
--- a/third_party/WebKit/public/web/WebPlugin.h
+++ b/third_party/WebKit/public/web/WebPlugin.h
@@ -167,19 +167,23 @@
   }
 
   // Sets composition text from input method, and returns true if the
-  // composition is set successfully.
+  // composition is set successfully. If |replacementRange| is not null, the
+  // text inside |replacementRange| will be replaced by |text|
   virtual bool setComposition(
       const WebString& text,
       const WebVector<WebCompositionUnderline>& underlines,
+      const WebRange& replacementRange,
       int selectionStart,
       int selectionEnd) {
     return false;
   }
 
   // Deletes the ongoing composition if any, inserts the specified text, and
-  // moves the caret according to relativeCaretPosition.
+  // moves the caret according to relativeCaretPosition. If |replacementRange|
+  // is not null, the text inside |replacementRange| will be replaced by |text|.
   virtual bool commitText(const WebString& text,
                           const WebVector<WebCompositionUnderline>& underlines,
+                          const WebRange& replacementRange,
                           int relativeCaretPosition) {
     return false;
   }
diff --git a/third_party/closure_compiler/README.chromium b/third_party/closure_compiler/README.chromium
index a0ef64d..e02ecbe 100644
--- a/third_party/closure_compiler/README.chromium
+++ b/third_party/closure_compiler/README.chromium
@@ -3,7 +3,7 @@
 URL: http://github.com/google/closure-compiler
 Version: v20150729-236-gad656a1
 Date: 2015/08/26 08:46
-Revision: 72c88ff7e9e85b4c2413274539897b6d43024d48
+Revision: b2f2d2f3309c7ee61816e068050651af27bccdfa
 License: Apache 2.0
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/closure_compiler/compiler/compiler.jar b/third_party/closure_compiler/compiler/compiler.jar
index c57eed9..a895591 100644
--- a/third_party/closure_compiler/compiler/compiler.jar
+++ b/third_party/closure_compiler/compiler/compiler.jar
Binary files differ
diff --git a/third_party/closure_compiler/externs/chrome_extensions.js b/third_party/closure_compiler/externs/chrome_extensions.js
index ac0cd611..7088c14 100644
--- a/third_party/closure_compiler/externs/chrome_extensions.js
+++ b/third_party/closure_compiler/externs/chrome_extensions.js
@@ -2559,7 +2559,7 @@
 
 /**
  * @const
- * @see https://developer.chrome.com/extensions/tabs.html
+ * @see https://developer.chrome.com/extensions/tabs
  */
 chrome.tabs = {};
 
@@ -2770,7 +2770,11 @@
  * @typedef {?{
  *   active: (boolean|undefined),
  *   pinned: (boolean|undefined),
+ *   audible: (boolean|undefined),
+ *   muted: (boolean|undefined),
  *   highlighted: (boolean|undefined),
+ *   discarded: (boolean|undefined),
+ *   autoDiscardable: (boolean|undefined),
  *   currentWindow: (boolean|undefined),
  *   lastFocusedWindow: (boolean|undefined),
  *   status: (!chrome.tabs.TabStatus|string|undefined),
@@ -6028,18 +6032,17 @@
 
 /**
  * Most event listeners for WebRequest take extra arguments.
- * @see https://developer.chrome.com/extensions/webRequest.html.
+ * @see https://developer.chrome.com/extensions/webRequest
  * @constructor
  */
 function WebRequestEvent() {}
 
 
 /**
- * @param {function(!Object): (void|!BlockingResponse)} listener Listener
- *     function.
+ * @param {function(!Object): void} listener Listener function.
  * @param {!RequestFilter} filter A set of filters that restrict
  *     the events that will be sent to this listener.
- * @param {Array<string>=} opt_extraInfoSpec Array of extra information
+ * @param {!Array<string>=} opt_extraInfoSpec Array of extra information
  *     that should be passed to the listener function.
  * @return {undefined}
  */
@@ -6048,33 +6051,123 @@
 
 
 /**
- * @param {function(!Object): (void|!BlockingResponse)} listener Listener
- *     function.
+ * @param {function(!Object): void} listener Listener function.
  * @return {undefined}
  */
 WebRequestEvent.prototype.removeListener = function(listener) {};
 
 
 /**
- * @param {function(!Object): (void|!BlockingResponse)} listener Listener
- *     function.
+ * @param {function(!Object): void} listener Listener function.
  * @return {undefined}
  */
 WebRequestEvent.prototype.hasListener = function(listener) {};
 
 
 /**
- * @param {function(!Object): (void|!BlockingResponse)} listener Listener
- *     function.
+ * @param {function(!Object): void} listener Listener function.
  * @return {undefined}
  */
 WebRequestEvent.prototype.hasListeners = function(listener) {};
 
 
+/**
+ * Some event listeners can be optionally synchronous.
+ * @see https://developer.chrome.com/extensions/webRequest
+ * @constructor
+ */
+function WebRequestOptionallySynchronousEvent() {}
+
 
 /**
- * The onErrorOccurred event takes one less parameter than the others.
- * @see https://developer.chrome.com/extensions/webRequest.html.
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @param {!RequestFilter} filter A set of filters that restrict
+ *     the events that will be sent to this listener.
+ * @param {!Array<string>=} opt_extraInfoSpec Array of extra information
+ *     that should be passed to the listener function.
+ * @return {undefined}
+ */
+WebRequestOptionallySynchronousEvent.prototype.addListener = function(
+    listener, filter, opt_extraInfoSpec) {};
+
+
+/**
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @return {undefined}
+ */
+WebRequestOptionallySynchronousEvent.prototype.removeListener = function(
+    listener) {};
+
+
+/**
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @return {undefined}
+ */
+WebRequestOptionallySynchronousEvent.prototype.hasListener = function(
+    listener) {};
+
+
+/**
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @return {undefined}
+ */
+WebRequestOptionallySynchronousEvent.prototype.hasListeners = function(
+    listener) {};
+
+
+/**
+ * The onAuthRequired event listener can be optionally synchronous, and can also
+ * optionally take a callback.
+ * @see https://developer.chrome.com/extensions/webRequest
+ * @constructor
+ */
+function WebRequestOnAuthRequiredEvent() {}
+
+
+/**
+ * @param {function(!Object, function(!BlockingResponse)=):
+ *     (undefined|!BlockingResponse)} listener Listener function.
+ * @param {!RequestFilter} filter A set of filters that restrict
+ *     the events that will be sent to this listener.
+ * @param {!Array<string>=} opt_extraInfoSpec Array of extra information
+ *     that should be passed to the listener function.
+ * @return {undefined}
+ */
+WebRequestOnAuthRequiredEvent.prototype.addListener = function(
+    listener, filter, opt_extraInfoSpec) {};
+
+
+/**
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @return {undefined}
+ */
+WebRequestOnAuthRequiredEvent.prototype.removeListener = function(listener) {};
+
+
+/**
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @return {undefined}
+ */
+WebRequestOnAuthRequiredEvent.prototype.hasListener = function(listener) {};
+
+
+/**
+ * @param {function(!Object): (undefined|!BlockingResponse)} listener Listener
+ *     function.
+ * @return {undefined}
+ */
+WebRequestOnAuthRequiredEvent.prototype.hasListeners = function(listener) {};
+
+
+/**
+ * The onErrorOccurred event takes one fewer parameter than the others.
+ * @see https://developer.chrome.com/extensions/webRequest
  * @constructor
  */
 function WebRequestOnErrorOccurredEvent() {}
@@ -6113,7 +6206,7 @@
 
 /**
  * @const
- * @see https://developer.chrome.com/extensions/webRequest.html
+ * @see https://developer.chrome.com/extensions/webRequest
  */
 chrome.webRequest = {};
 
@@ -6125,7 +6218,7 @@
 chrome.webRequest.handlerBehaviorChanged = function(opt_callback) {};
 
 
-/** @type {!WebRequestEvent} */
+/** @type {!WebRequestOnAuthRequiredEvent} */
 chrome.webRequest.onAuthRequired;
 
 
@@ -6133,11 +6226,11 @@
 chrome.webRequest.onBeforeRedirect;
 
 
-/** @type {!WebRequestEvent} */
+/** @type {!WebRequestOptionallySynchronousEvent} */
 chrome.webRequest.onBeforeRequest;
 
 
-/** @type {!WebRequestEvent} */
+/** @type {!WebRequestOptionallySynchronousEvent} */
 chrome.webRequest.onBeforeSendHeaders;
 
 
@@ -6149,7 +6242,7 @@
 chrome.webRequest.onErrorOccurred;
 
 
-/** @type {!WebRequestEvent} */
+/** @type {!WebRequestOptionallySynchronousEvent} */
 chrome.webRequest.onHeadersReceived;
 
 
@@ -7034,82 +7127,49 @@
 
 
 /**
- * @see https://developer.chrome.com/extensions/webRequest.html#type-RequestFilter
- * @constructor
+ * @see https://developer.chrome.com/extensions/webRequest#type-RequestFilter
+ * @typedef {?{
+ *   urls: !Array<string>,
+ *   types: (!Array<string>|undefined),
+ *   tabId: (number|undefined),
+ *   windowId: (number|undefined),
+ * }}
  */
-function RequestFilter() {}
-
-
-/** @type {!Array<string>} */
-RequestFilter.prototype.urls;
-
-
-/** @type {!Array<string>} */
-RequestFilter.prototype.types;
-
-
-/** @type {number} */
-RequestFilter.prototype.tabId;
-
-
-/** @type {number} */
-RequestFilter.prototype.windowId;
+var RequestFilter;
 
 
 
 /**
- * @see https://developer.chrome.com/extensions/webRequest.html#type-HttpHeaders
- * @constructor
+ * @see https://developer.chrome.com/extensions/webRequest#type-HttpHeaders
+ * @typedef {?{
+ *   name: string,
+ *   value: (string|undefined),
+ *   binaryValue: (!Array<number>|undefined),
+ * }}
  */
-function HttpHeader() {}
-
-
-/** @type {string} */
-HttpHeader.prototype.name;
-
-
-/** @type {string} */
-HttpHeader.prototype.value;
-
-
-/** @type {!Array<number>} */
-HttpHeader.prototype.binaryValue;
-
-
-/**
- * @see https://developer.chrome.com/extensions/webRequest.html#type-HttpHeaders
- * @typedef {Array<!HttpHeader>}
- * @private
- */
-var HttpHeaders_;
+var HttpHeader;
 
 
 
 /**
- * @see https://developer.chrome.com/extensions/webRequest.html#type-BlockingResponse
- * @constructor
+ * @see https://developer.chrome.com/extensions/webRequest#type-HttpHeaders
+ * @typedef {?Array<!HttpHeader>}
  */
-function BlockingResponse() {}
+chrome.webRequest.HttpHeaders;
 
 
-/** @type {boolean} */
-BlockingResponse.prototype.cancel;
 
-
-/** @type {string} */
-BlockingResponse.prototype.redirectUrl;
-
-
-/** @type {!HttpHeaders_} */
-BlockingResponse.prototype.requestHeaders;
-
-
-/** @type {!HttpHeaders_} */
-BlockingResponse.prototype.responseHeaders;
-
-
-/** @type {Object<string,string>} */
-BlockingResponse.prototype.authCredentials;
+/**
+ * @see https://developer.chrome.com/extensions/webRequest#type-BlockingResponse
+ * @typedef {?{
+ *   cancel: (boolean|undefined),
+ *   redirectUrl: (string|undefined),
+ *   requestHeaders: (!chrome.webRequest.HttpHeaders|undefined),
+ *   responseHeaders: (!chrome.webRequest.HttpHeaders|undefined),
+ *   authCredentials: (!{username: string, password: string}|undefined),
+ * }}
+ */
+var BlockingResponse;
 
 
 
@@ -9980,6 +10040,13 @@
 /**
  * @param {string} deviceAddress
  * @param {function():void=} callback
+ */
+chrome.bluetoothPrivate.disconnectAll = function(deviceAddress, callback) {};
+
+
+/**
+ * @param {string} deviceAddress
+ * @param {function():void=} callback
  * @return {undefined}
  */
 chrome.bluetoothPrivate.forgetDevice = function(deviceAddress, callback) {};
diff --git a/third_party/closure_compiler/externs/compiled_resources2.gyp b/third_party/closure_compiler/externs/compiled_resources2.gyp
index 488f6de..7aab119 100644
--- a/third_party/closure_compiler/externs/compiled_resources2.gyp
+++ b/third_party/closure_compiler/externs/compiled_resources2.gyp
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -81,7 +81,7 @@
       'includes': ['../include_js.gypi'],
     },
     {
-      'target_name': 'pending_compiler_externs',
+      'target_name': 'polymer-1.0',
       'includes': ['../include_js.gypi'],
     },
     {
@@ -109,4 +109,4 @@
       'includes': ['../include_js.gypi'],
     },
   ],
-}
+}
\ No newline at end of file
diff --git a/third_party/closure_compiler/externs/pending_compiler_externs.js b/third_party/closure_compiler/externs/pending_compiler_externs.js
deleted file mode 100644
index 9dd3216..0000000
--- a/third_party/closure_compiler/externs/pending_compiler_externs.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/** @externs */
-
-/**
- * @type {?Document}
- * @see w3c_dom2.js
- * @see http://www.w3.org/TR/html-imports/#interface-import
- */
-HTMLLinkElement.prototype.import;
diff --git a/third_party/closure_compiler/externs/web_animations.js b/third_party/closure_compiler/externs/web_animations.js
index 39ffc37..c233999 100644
--- a/third_party/closure_compiler/externs/web_animations.js
+++ b/third_party/closure_compiler/externs/web_animations.js
@@ -187,13 +187,6 @@
 
 Animation.prototype.cancel = function() {};
 
-/**
- * @param {boolean=} opt_useCapture
- * @override
- */
-Animation.prototype.addEventListener = function(
-    type, listener, opt_useCapture) {};
-
 /** @type {EventHandler} */
 Animation.prototype.onfinish;
 
diff --git a/third_party/closure_compiler/roll_closure_compiler b/third_party/closure_compiler/roll_closure_compiler
index ca11c79f..fa8d01e 100755
--- a/third_party/closure_compiler/roll_closure_compiler
+++ b/third_party/closure_compiler/roll_closure_compiler
@@ -64,7 +64,7 @@
 fi
 
 echo "Building Closure Compiler"
-mvn install -DskipTests=true --projects com.google.javascript:closure-compiler
+mvn clean install -DskipTests=true --projects com.google.javascript:closure-compiler,com.google.javascript:closure-compiler-externs
 
 if [[ "$?" -ne 0 ]]; then
   echo "Failed to build jar, copying nothing" >&2
diff --git a/third_party/closure_compiler/tools/create_include_gyp.py b/third_party/closure_compiler/tools/create_include_gyp.py
index 2258e0ce..08ef525 100755
--- a/third_party/closure_compiler/tools/create_include_gyp.py
+++ b/third_party/closure_compiler/tools/create_include_gyp.py
@@ -46,7 +46,7 @@
 
 
 def ShowUsageAndDie():
-  print "usage: tools/create_include_gyp.py externs > externs/compiled_resources2.gyp"
+  print "usage: tools/create_include_gyp.py externs/ > externs/compiled_resources2.gyp"
   sys.exit(1)
 
 
diff --git a/third_party/protobuf/proto_library.gni b/third_party/protobuf/proto_library.gni
index 84373f3..880a5af4 100644
--- a/third_party/protobuf/proto_library.gni
+++ b/third_party/protobuf/proto_library.gni
@@ -285,7 +285,7 @@
 
     if (defined(invoker.import_dirs)) {
       foreach(path, invoker.import_dirs) {
-        args += ["--import-dir=" + rebase_path(path, root_build_dir)]
+        args += [ "--import-dir=" + rebase_path(path, root_build_dir) ]
       }
     }
 
@@ -373,6 +373,16 @@
             "//third_party/protobuf:protobuf_lite",
           ]
         }
+
+        if (is_win) {
+          cflags = [
+            # disable: C4125 decimal digit terminates octal escape sequence
+            # Protoc generates such sequences frequently, there's no obvious
+            # superior replacement behavior. Since this code is autogenerated,
+            # the warning would never catch a legitimate bug.
+            "/wd4125",
+          ]
+        }
       }
     }
 
diff --git a/third_party/tcmalloc/BUILD.gn b/third_party/tcmalloc/BUILD.gn
new file mode 100644
index 0000000..ea10b28
--- /dev/null
+++ b/third_party/tcmalloc/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright (c) 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/compiler/compiler.gni")
+
+executable("addr2line-pdb") {
+  sources = [
+    "chromium/src/windows/addr2line-pdb.c",
+  ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+}
diff --git a/third_party/tcmalloc/README.chromium b/third_party/tcmalloc/README.chromium
index d2e8176..50862f9 100644
--- a/third_party/tcmalloc/README.chromium
+++ b/third_party/tcmalloc/README.chromium
@@ -105,3 +105,5 @@
 - Added TCMALLOC_DONT_REPLACE_SYSTEM_ALLOC which bypasses the libc_override logic.
 - Backported 7df7f14 "issue-693: enable futex usage on arm" from upstream.
 - Don't use the tls model 'initial-exec' in chromeos on arm with gcc.
+- Update addr2line-pdb.c to fix format string errors and use relative addresses
+  matching linux's behavior more closely.
diff --git a/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c b/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c
index 97b614b4..7a2bf3e6 100644
--- a/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c
+++ b/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c
@@ -35,9 +35,17 @@
  * c:\websymbols without asking.
  */
 
+#ifndef WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
+#endif 
+
+#ifndef _CRT_SECURE_NO_WARNINGS
 #define _CRT_SECURE_NO_WARNINGS
+#endif 
+
+#ifndef _CRT_SECURE_NO_DEPRECATE
 #define _CRT_SECURE_NO_DEPRECATE
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -67,7 +75,8 @@
     } else if (strcmp(argv[i], "--demangle") == 0 ||
                strcmp(argv[i], "-C") == 0) {
       symopts |= SYMOPT_UNDNAME;
-    } else if (strcmp(argv[i], "-e") == 0) {
+    } else if (strcmp(argv[i], "--exe") == 0 ||
+               strcmp(argv[i], "-e") == 0) {
       if (i + 1 >= argc) {
         fprintf(stderr, "FATAL ERROR: -e must be followed by a filename\n");
         return 1;
@@ -86,7 +95,7 @@
 
   if (!SymInitialize(process, NULL, FALSE)) {
     error = GetLastError();
-    fprintf(stderr, "SymInitialize returned error : %d\n", error);
+    fprintf(stderr, "SymInitialize returned error : %lu\n", error);
     return 1;
   }
 
@@ -100,13 +109,13 @@
     strcat(search, ";" WEBSYM);
   } else {
     error = GetLastError();
-    fprintf(stderr, "SymGetSearchPath returned error : %d\n", error);
+    fprintf(stderr, "SymGetSearchPath returned error : %lu\n", error);
     rv = 1;                   /* An error, but not a fatal one */
     strcpy(search, WEBSYM);   /* Use a default value */
   }
   if (!SymSetSearchPath(process, search)) {
     error = GetLastError();
-    fprintf(stderr, "SymSetSearchPath returned error : %d\n", error);
+    fprintf(stderr, "SymSetSearchPath returned error : %lu\n", error);
     rv = 1;                   /* An error, but not a fatal one */
   }
 
@@ -115,7 +124,7 @@
   if (!module_base) {
     /* SymLoadModuleEx failed */
     error = GetLastError();
-    fprintf(stderr, "SymLoadModuleEx returned error : %d for %s\n",
+    fprintf(stderr, "SymLoadModuleEx returned error : %lu for %s\n",
             error, filename);
     SymCleanup(process);
     return 1;
@@ -126,7 +135,7 @@
     /* GNU addr2line seems to just do a strtol and ignore any
      * weird characters it gets, so we will too.
      */
-    unsigned __int64 addr = _strtoui64(buf, NULL, 16);
+    unsigned __int64 reladdr = _strtoui64(buf, NULL, 16);
     ULONG64 buffer[(sizeof(SYMBOL_INFO) +
                     MAX_SYM_NAME*sizeof(TCHAR) +
                     sizeof(ULONG64) - 1)
@@ -134,17 +143,25 @@
     PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
     IMAGEHLP_LINE64 line;
     DWORD dummy;
+
+    // Just ignore overflow. In an overflow scenario, the resulting address
+    // will be lower than module_base which hasn't been mapped by any prior
+    // SymLoadModuleEx() command. This will cause SymFromAddr() and
+    // SymGetLineFromAddr64() both to return failures and print the correct
+    // ?? and ??:0 message variant.
+    ULONG64 absaddr = reladdr + module_base;
+
     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
     pSymbol->MaxNameLen = MAX_SYM_NAME;
     if (print_function_name) {
-      if (SymFromAddr(process, (DWORD64)addr, NULL, pSymbol)) {
+      if (SymFromAddr(process, (DWORD64)absaddr, NULL, pSymbol)) {
         printf("%s\n", pSymbol->Name);
       } else {
         printf("??\n");
       }
     }
     line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-    if (SymGetLineFromAddr64(process, (DWORD64)addr, &dummy, &line)) {
+    if (SymGetLineFromAddr64(process, (DWORD64)absaddr, &dummy, &line)) {
       printf("%s:%d\n", line.FileName, (int)line.LineNumber);
     } else {
       printf("??:0\n");
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 2125245..917f3c6 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1086,10 +1086,14 @@
                                 output_path=None)
 
     if android and test_type != "script":
+      # TODO(crbug.com/693203): Reenable logcat logdog uploading when outage
+      # has been resolved.
       cmdline = [
-          '../../build/android/test_wrapper/logdog_wrapper.py',
-          '--target', target,
-          '--logdog-bin-cmd', '../../bin/logdog_butler']
+          self.PathJoin('bin', 'run_%s' % target),
+          '--logcat-output-file', '${ISOLATED_OUTDIR}/logcats',
+          '--target-devices-file', '${SWARMING_BOT_FILE}',
+          '-v'
+      ]
     elif use_xvfb and test_type == 'windowed_test_launcher':
       extra_files = [
           '../../testing/test_env.py',
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index c9f01f39..a845aed 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -564,6 +564,7 @@
       'linux_chromium_gn_upload': 'gn_linux_upload',
       'linux_chromium_headless_rel': 'headless_linux_release_trybot',
       'linux_chromium_ozone_compile_only_ng': 'ozone_linux_release_trybot',
+      'linux_chromium_ozone_ng': 'ozone_linux_release_trybot',
 
       # This is intentionally a release_bot and not a release_trybot;
       # enabling DCHECKs seems to cause flaky failures that don't show up
@@ -1830,6 +1831,7 @@
 
     'ozone_linux': {
       'gn_args': ('ozone_auto_platforms=false ozone_platform_wayland=true '
+                  'ozone_platform="x11" '
                   'ozone_platform_x11=true ozone_platform_gbm=true '
                   'enable_package_mash_services=true use_ash=false '
                   'use_jessie_sysroot=true use_xkbcommon=true'),
diff --git a/tools/md_browser/md_browser.py b/tools/md_browser/md_browser.py
index 2c416433..d3594bf 100755
--- a/tools/md_browser/md_browser.py
+++ b/tools/md_browser/md_browser.py
@@ -138,6 +138,10 @@
       self._DoMD(path)
     elif os.path.exists(full_path + '/README.md'):
       self._DoMD(path + '/README.md')
+    elif path.lower().endswith('.png'):
+      self._DoImage(full_path, 'image/png')
+    elif path.lower().endswith('.jpg'):
+      self._DoImage(full_path, 'image/jpeg')
     else:
       self._DoDirListing(full_path)
 
@@ -223,6 +227,12 @@
 
     self._WriteTemplate('footer.html')
 
+  def _DoImage(self, full_path, mime_type):
+    self._WriteHeader(mime_type)
+    with open(full_path) as f:
+      self.wfile.write(f.read())
+      f.close()
+
   def _Read(self, relpath, relative_to=None):
     if relative_to is None:
       relative_to = self.server.top_level
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ad8770a..9dab252 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -17676,6 +17676,10 @@
 
 <histogram name="Extensions.DisabledUIUserResponse"
     enum="ExtensionDisabledUIUserResponse">
+  <obsolete>
+    Deprecated 03/2017 because miscounting IGNORE histogram entry. This error is
+    fixed with DisabledUIUserResponse2.
+  </obsolete>
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
     User response to the dialog shown when an extension is disabled due to an
@@ -17683,8 +17687,23 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.DisabledUIUserResponse2"
+    enum="ExtensionDisabledUIUserResponse">
+  <owner>catmullings@chromium.org</owner>
+  <summary>
+    User response to the dialog shown when an extension is disabled due to an
+    update requiring more permissions. A count is recorded when the user takes
+    an action on the dialog (re-enable or remove the extension) or ignores the
+    dialog.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.DisabledUIUserResponseRemoteInstall"
     enum="ExtensionDisabledUIUserResponse">
+  <obsolete>
+    Deprecated 03/2017 because miscounting IGNORE histogram entry. This error is
+    fixed with DisabledUIUserResponseRemoteInstall2.
+  </obsolete>
   <owner>mek@chromium.org</owner>
   <summary>
     User response to the dialog shown when an extension is disabled due to it
@@ -17692,6 +17711,17 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.DisabledUIUserResponseRemoteInstall2"
+    enum="ExtensionDisabledUIUserResponse">
+  <owner>catmullings@chromium.org</owner>
+  <summary>
+    User response to the dialog shown when an extension is disabled due to it
+    having been installed remotely. A count is recorded when the user takes an
+    action on the dialog (re-enable or remove the extension) or ignores the
+    dialog.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.DisableReason" enum="ExtensionDisableReason">
   <owner>asargent@chromium.org</owner>
   <summary>
@@ -24514,6 +24544,15 @@
   </summary>
 </histogram>
 
+<histogram name="LibraryLoader.LoadNativeLibraryWindows"
+    enum="LoadLibraryResultCategory">
+  <owner>chengx@chromium.org</owner>
+  <summary>
+    This metric records the LoadLibraryExW and LoadLibraryW Windows API call
+    results, which are used in native_library_win.cc.
+  </summary>
+</histogram>
+
 <histogram name="LibraryLoader.NativeLibraryHack" enum="BooleanUsage">
   <obsolete>
     Deprecated as of 11/2014, removed from code.
@@ -28322,6 +28361,28 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Memory.OpenFDs" units="files">
+<!-- Name completed by histogram_suffixes name="MemoryFDsBroswerGpuAndRendererProcess" and name="MemoryFDsAllProcesses" -->
+
+  <owner>dcastagna@chromium.org</owner>
+  <owner>primiano@chromium.org</owner>
+  <summary>
+    The total number of open file descriptors opened per process.  Recorded once
+    per UMA ping.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Memory.OpenFDsSoftLimit" units="files">
+<!-- Name completed by histogram_suffixes name="MemoryFDsBroswerGpuAndRendererProcess" -->
+
+  <owner>dcastagna@chromium.org</owner>
+  <owner>primiano@chromium.org</owner>
+  <summary>
+    The limit of open file descriptors that can be opened per process.  Recorded
+    once per UMA ping.
+  </summary>
+</histogram>
+
 <histogram name="Memory.OtherProcessCount" units="processes">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
@@ -85109,6 +85170,8 @@
   <int value="2" label="Clicked 'Always allow pop-ups from'"/>
   <int value="3" label="Clicked one of the list items"/>
   <int value="4" label="Clicked 'Manage pop-up blocking'"/>
+  <int value="5" label="Displayed popup-blocked infobar on mobile"/>
+  <int value="6" label="Clicked 'Always show on mobile'"/>
 </enum>
 
 <enum name="ContentSettingScheme" type="int">
@@ -93403,6 +93466,7 @@
   <int value="1864" label="VRPoseLinearAcceleration"/>
   <int value="1865" label="VRPoseAngularVelocity"/>
   <int value="1866" label="VRPoseAngularAcceleration"/>
+  <int value="1867" label="CSSOverflowPaged"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -99013,6 +99077,14 @@
   <int value="23" label="Unnamed"/>
 </enum>
 
+<enum name="LoadLibraryResultCategory" type="int">
+  <int value="0" label="LoadLibraryExW Succeeded"/>
+  <int value="1" label="LoadLibraryExW Fail, LoadLibraryW Succeeded"/>
+  <int value="2" label="LoadLibraryExW Fail, LoadLibraryW Fail"/>
+  <int value="3" label="LoadLibraryExW Unavailable, LoadLibraryW Succeeded"/>
+  <int value="4" label="LoadLibraryExW Unavailable, LoadLibraryW Fail"/>
+</enum>
+
 <enum name="LoadType" type="int">
   <int value="0" label="UNDEFINED_LOAD">Not yet initialized</int>
   <int value="1" label="RELOAD">User pressed reload</int>
@@ -117680,6 +117752,32 @@
   <affected-histogram name="Media.WebMediaPlayerImpl.Memory"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="MemoryFDsAllProcesses" separator="."
+    ordering="prefix">
+  <suffix name="Browser" label="Browser process"/>
+  <suffix name="Gpu" label="GPU process"/>
+  <suffix name="RendererAll" label="Renderer process"/>
+  <suffix name="Chrome" label="chrome:// renderer process"/>
+  <suffix name="Extension" label="Extension process"/>
+  <suffix name="NativeClient" label="Native client process"/>
+  <suffix name="NativeClientBroker" label="Native client broker process"/>
+  <suffix name="PeperPlugin" label="Pepper plugin process"/>
+  <suffix name="PepperPluginBroker" label="Pepper plugin broker process"/>
+  <suffix name="Renderer" label="Renderer process"/>
+  <suffix name="SandboxHelper" label="Sandbox helper process"/>
+  <suffix name="Utility" label="Utility process"/>
+  <suffix name="Zygote" label="Zygot process"/>
+  <affected-histogram name="Memory.OpenFDs"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="MemoryFDsBroswerGpuAndRendererProcess" separator="."
+    ordering="prefix">
+  <suffix name="Browser" label="Browser process"/>
+  <suffix name="Gpu" label="GPU process"/>
+  <suffix name="RendererAll" label="Renderer process"/>
+  <affected-histogram name="Memory.OpenFDsSoftLimit"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="MemoryStateTransition" separator=".">
   <suffix name="NormalToThrottled"/>
   <suffix name="NormalToSuspended"/>
@@ -119994,6 +120092,16 @@
   <affected-histogram name="PageLoad.ParseTiming.ParseDuration"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="PageLoadMetricsClientsMedia" separator="."
+    ordering="prefix">
+  <suffix name="Clients.Media"
+      label="PageLoadMetrics for page loads that involved playing a media
+             element."/>
+  <affected-histogram name="PageLoad.Experimental.Bytes.Cache"/>
+  <affected-histogram name="PageLoad.Experimental.Bytes.Network"/>
+  <affected-histogram name="PageLoad.Experimental.Bytes.Total"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="PageLoadMetricsClientsOfflinePages" separator="."
     ordering="prefix">
   <suffix name="Clients.Previews.OfflinePages"
diff --git a/ui/file_manager/externs/webview_tag.js b/ui/file_manager/externs/webview_tag.js
index 7bea1fd..cc6eb452 100644
--- a/ui/file_manager/externs/webview_tag.js
+++ b/ui/file_manager/externs/webview_tag.js
@@ -188,12 +188,12 @@
 
 /**
  * @constructor
- * @see https://developer.chrome.com/apps/tags/webview#type-WebRequestEventInteface
+ * @see https://developer.chrome.com/apps/tags/webview#type-WebRequestEventInterface
  */
-function WebRequestEventInteface() {}
+function WebRequestEventInterface() {}
 
-/** @type {!WebRequestEvent} */
-WebRequestEventInteface.prototype.onBeforeSendHeaders;
+/** @type {!WebRequestOptionallySynchronousEvent} */
+WebRequestEventInterface.prototype.onBeforeSendHeaders;
 
 /**
  * @constructor
@@ -208,7 +208,7 @@
 WebView.prototype.contentWindow;
 
 /**
- * @type {!WebRequestEventInteface}
+ * @type {!WebRequestEventInterface}
  * @see https://developer.chrome.com/apps/tags/webview#property-request
  */
 WebView.prototype.request;
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 23cd695..42fe36d1 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -50,12 +50,6 @@
     "android/view_configuration.cc",
     "android/view_configuration.h",
     "break_list.h",
-    "codec/jpeg_codec.cc",
-    "codec/jpeg_codec.h",
-    "codec/png_codec.cc",
-    "codec/png_codec.h",
-    "codec/skia_image_encoder_adapter.cc",
-    "codec/skia_image_encoder_adapter.h",
     "color_analysis.cc",
     "color_analysis.h",
     "color_palette.h",
@@ -255,6 +249,7 @@
     "//skia",
     "//third_party/icu",
     "//ui/gfx/animation",
+    "//ui/gfx/codec",
     "//ui/gfx/geometry",
     "//ui/gfx/range",
   ]
@@ -265,7 +260,6 @@
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
     "//skia",
-    "//third_party/libpng",
     "//third_party/qcms",
     "//third_party/zlib",
   ]
@@ -305,17 +299,12 @@
 
   # iOS.
   if (is_ios) {
-    sources -= [
-      "codec/jpeg_codec.cc",
-      "codec/jpeg_codec.h",
-    ]
     set_sources_assignment_filter([])
     sources += [ "scoped_cg_context_save_gstate_mac.h" ]
     set_sources_assignment_filter(sources_assignment_filter)
   } else {
     deps += [
       "//cc/paint",
-      "//third_party:jpeg",
       "//third_party/harfbuzz-ng",
     ]
   }
@@ -358,9 +347,6 @@
 
   # Windows.
   if (is_win) {
-    cflags = [ "/wd4324" ]  # Structure was padded due to __declspec(align()), which is
-                            # uninteresting.
-
     libs = [
       "setupapi.lib",
       "dwrite.lib",
@@ -381,16 +367,6 @@
     deps += [ "//build/linux:fontconfig" ]
   }
 
-  # Chrome OS
-  if (is_chromeos) {
-    # Robust JPEG decoding for the login screen.
-    sources += [
-      "chromeos/codec/jpeg_codec_robust_slow.cc",
-      "chromeos/codec/jpeg_codec_robust_slow.h",
-    ]
-    deps += [ "//third_party/libjpeg" ]
-  }
-
   if (is_mac) {
     libs = [
       "AppKit.framework",
@@ -718,7 +694,7 @@
   }
 
   if (is_chromeos) {
-    sources += [ "chromeos/codec/jpeg_codec_robust_slow_unittest.cc" ]
+    sources += [ "codec/chromeos/jpeg_codec_robust_slow_unittest.cc" ]
   }
 
   if (is_win) {
diff --git a/ui/gfx/codec/BUILD.gn b/ui/gfx/codec/BUILD.gn
new file mode 100644
index 0000000..6cdc8a7c
--- /dev/null
+++ b/ui/gfx/codec/BUILD.gn
@@ -0,0 +1,51 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+
+component("codec") {
+  sources = [
+    "codec_export.h",
+    "jpeg_codec.cc",
+    "jpeg_codec.h",
+    "png_codec.cc",
+    "png_codec.h",
+    "skia_image_encoder_adapter.cc",
+    "skia_image_encoder_adapter.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//third_party/libpng",
+    "//ui/gfx:geometry_skia",
+    "//ui/gfx:gfx_export",
+    "//ui/gfx/geometry",
+  ]
+
+  if (is_ios) {
+    sources -= [
+      "jpeg_codec.cc",
+      "jpeg_codec.h",
+    ]
+  } else {
+    deps += [ "//third_party:jpeg" ]
+  }
+
+  if (is_chromeos) {
+    # Robust JPEG decoding for the login screen.
+    sources += [
+      "chromeos/jpeg_codec_robust_slow.cc",
+      "chromeos/jpeg_codec_robust_slow.h",
+    ]
+    deps += [ "//third_party/libjpeg" ]
+  }
+
+  if (is_win) {
+    cflags = [ "/wd4324" ]  # Structure was padded due to __declspec(align()),
+                            # which is uninteresting.
+  }
+
+  defines = [ "CODEC_IMPLEMENTATION" ]
+}
diff --git a/ui/gfx/chromeos/codec/DEPS b/ui/gfx/codec/chromeos/DEPS
similarity index 100%
rename from ui/gfx/chromeos/codec/DEPS
rename to ui/gfx/codec/chromeos/DEPS
diff --git a/ui/gfx/chromeos/codec/OWNERS b/ui/gfx/codec/chromeos/OWNERS
similarity index 100%
rename from ui/gfx/chromeos/codec/OWNERS
rename to ui/gfx/codec/chromeos/OWNERS
diff --git a/ui/gfx/chromeos/codec/jpeg_codec_robust_slow.cc b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc
similarity index 95%
rename from ui/gfx/chromeos/codec/jpeg_codec_robust_slow.cc
rename to ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc
index b7ceefd..057dd31e 100644
--- a/ui/gfx/chromeos/codec/jpeg_codec_robust_slow.cc
+++ b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc
@@ -2,21 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h"
+#include "ui/gfx//codec/chromeos/jpeg_codec_robust_slow.h"
 
 #include <setjmp.h>
 
 #include <memory>
 
 #include "base/logging.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColorPriv.h"
 
 extern "C" {
 // IJG provides robust JPEG decode
 #include "third_party/libjpeg/jpeglib.h"
 }
 
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+
 namespace gfx {
 
 // Encoder/decoder shared stuff ------------------------------------------------
@@ -30,7 +31,7 @@
 };
 
 void ErrorExit(jpeg_common_struct* cinfo) {
-  CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
+  CoderErrorMgr* err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
 
   // Return control to the setjmp point.
   longjmp(err->setjmp_buffer, false);
@@ -44,8 +45,7 @@
 
 struct JpegDecoderState {
   JpegDecoderState(const unsigned char* in, size_t len)
-      : input_buffer(in), input_buffer_length(len) {
-  }
+      : input_buffer(in), input_buffer_length(len) {}
 
   const unsigned char* input_buffer;
   size_t input_buffer_length;
@@ -112,8 +112,7 @@
 //  "Terminate source --- called by jpeg_finish_decompress() after all data has
 //   been read to clean up JPEG source manager. NOT called by jpeg_abort() or
 //   jpeg_destroy()."
-void TermSource(j_decompress_ptr cinfo) {
-}
+void TermSource(j_decompress_ptr cinfo) {}
 
 #if !defined(JCS_EXTENSIONS)
 // Converts one row of rgb data to rgba data by adding a fully-opaque alpha
@@ -127,8 +126,7 @@
 
 // Converts one row of RGB data to BGRA by reordering the color components and
 // adding alpha values of 0xff.
-void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
-{
+void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb) {
   for (int x = 0; x < pixel_width; x++) {
     const unsigned char* pixel_in = &bgra[x * 3];
     unsigned char* pixel_out = &rgb[x * 4];
@@ -145,11 +143,8 @@
 // success case).
 class DecompressDestroyer {
  public:
-  DecompressDestroyer() : cinfo_(NULL) {
-  }
-  ~DecompressDestroyer() {
-    DestroyManagedObject();
-  }
+  DecompressDestroyer() : cinfo_(NULL) {}
+  ~DecompressDestroyer() { DestroyManagedObject(); }
   void SetManagedObject(jpeg_decompress_struct* ci) {
     DestroyManagedObject();
     cinfo_ = ci;
@@ -160,15 +155,18 @@
       cinfo_ = NULL;
     }
   }
+
  private:
   jpeg_decompress_struct* cinfo_;
 };
 
 }  // namespace
 
-bool JPEGCodecRobustSlow::Decode(const unsigned char* input, size_t input_size,
+bool JPEGCodecRobustSlow::Decode(const unsigned char* input,
+                                 size_t input_size,
                                  ColorFormat format,
-                                 std::vector<unsigned char>* output, int* w,
+                                 std::vector<unsigned char>* output,
+                                 int* w,
                                  int* h) {
   jpeg_decompress_struct cinfo;
   DecompressDestroyer destroyer;
diff --git a/ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.h
similarity index 76%
rename from ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h
rename to ui/gfx/codec/chromeos/jpeg_codec_robust_slow.h
index 08ca8b07..a6543cf 100644
--- a/ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h
+++ b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_GFX_CHROMEOS_CODEC_JPEG_CODEC_ROBUST_SLOW_H_
-#define UI_GFX_CHROMEOS_CODEC_JPEG_CODEC_ROBUST_SLOW_H_
+#ifndef UI_GFX_CODEC_CHROMEOS_JPEG_CODEC_ROBUST_SLOW_H_
+#define UI_GFX_CODEC_CHROMEOS_JPEG_CODEC_ROBUST_SLOW_H_
 
 #include <stddef.h>
 #include <vector>
 
-#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/codec/codec_export.h"
 
 class SkBitmap;
 
@@ -18,7 +18,7 @@
 // which has an inconvenient interface for callers. This is only used for
 // servicing ChromeUtilityMsg_RobustJPEGDecodeImage and is currently unique
 // to Chrome OS.
-class GFX_EXPORT JPEGCodecRobustSlow {
+class CODEC_EXPORT JPEGCodecRobustSlow {
  public:
   enum ColorFormat {
     // 3 bytes per pixel (packed), in RGB order regardless of endianness.
@@ -41,9 +41,12 @@
   // decoded data will be placed in *output with the dimensions in *w and *h
   // on success (returns true). This data will be written in the'format'
   // format. On failure, the values of these output variables is undefined.
-  static bool Decode(const unsigned char* input, size_t input_size,
-                     ColorFormat format, std::vector<unsigned char>* output,
-                     int* w, int* h);
+  static bool Decode(const unsigned char* input,
+                     size_t input_size,
+                     ColorFormat format,
+                     std::vector<unsigned char>* output,
+                     int* w,
+                     int* h);
 
   // Decodes the JPEG data contained in input of length input_size. If
   // successful, a SkBitmap is created and returned. It is up to the caller
@@ -53,4 +56,4 @@
 
 }  // namespace gfx
 
-#endif  // UI_GFX_CHROMEOS_CODEC_JPEG_CODEC_ROBUST_SLOW_H_
+#endif  // UI_GFX_CODEC_CHROMEOS_JPEG_CODEC_ROBUST_SLOW_H_
diff --git a/ui/gfx/chromeos/codec/jpeg_codec_robust_slow_unittest.cc b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow_unittest.cc
similarity index 87%
rename from ui/gfx/chromeos/codec/jpeg_codec_robust_slow_unittest.cc
rename to ui/gfx/codec/chromeos/jpeg_codec_robust_slow_unittest.cc
index 35e53ea..5304ab3 100644
--- a/ui/gfx/chromeos/codec/jpeg_codec_robust_slow_unittest.cc
+++ b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow_unittest.cc
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h"
+#include "ui/gfx/codec/chromeos/jpeg_codec_robust_slow.h"
 
 namespace {
 
@@ -93,16 +93,12 @@
 TEST(JPEGCodecRobustSlow, InvalidRead) {
   std::vector<unsigned char> output;
   int outw, outh;
-  ASSERT_TRUE(
-      JPEGCodecRobustSlow::Decode(kTopSitesMigrationTestImage,
-                                  arraysize(kTopSitesMigrationTestImage),
-                                  JPEGCodecRobustSlow::FORMAT_RGB, &output,
-                                  &outw, &outh));
-  ASSERT_TRUE(
-      JPEGCodecRobustSlow::Decode(kTopSitesMigrationTestImage,
-                                  arraysize(kTopSitesMigrationTestImage),
-                                  JPEGCodecRobustSlow::FORMAT_RGBA, &output,
-                                  &outw, &outh));
+  ASSERT_TRUE(JPEGCodecRobustSlow::Decode(
+      kTopSitesMigrationTestImage, arraysize(kTopSitesMigrationTestImage),
+      JPEGCodecRobustSlow::FORMAT_RGB, &output, &outw, &outh));
+  ASSERT_TRUE(JPEGCodecRobustSlow::Decode(
+      kTopSitesMigrationTestImage, arraysize(kTopSitesMigrationTestImage),
+      JPEGCodecRobustSlow::FORMAT_RGBA, &output, &outw, &outh));
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/codec/codec_export.h b/ui/gfx/codec/codec_export.h
new file mode 100644
index 0000000..c56a070f
--- /dev/null
+++ b/ui/gfx/codec/codec_export.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_CODEC_CODEC_EXPORT_H_
+#define UI_GFX_CODEC_CODEC_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(CODEC_IMPLEMENTATION)
+#define CODEC_EXPORT __declspec(dllexport)
+#else
+#define CODEC_EXPORT __declspec(dllimport)
+#endif  // defined(CODEC_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(CODEC_IMPLEMENTATION)
+#define CODEC_EXPORT __attribute__((visibility("default")))
+#else
+#define CODEC_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define CODEC_EXPORT
+#endif
+
+#endif  // UI_GFX_CODEC_CODEC_EXPORT_H_
diff --git a/ui/gfx/codec/jpeg_codec.h b/ui/gfx/codec/jpeg_codec.h
index 3cc510b..5d10be5 100644
--- a/ui/gfx/codec/jpeg_codec.h
+++ b/ui/gfx/codec/jpeg_codec.h
@@ -10,7 +10,7 @@
 #include <memory>
 #include <vector>
 
-#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/codec/codec_export.h"
 
 class SkBitmap;
 
@@ -20,7 +20,7 @@
 // which has an inconvenient interface for callers. This is only used for UI
 // elements, WebKit has its own more complicated JPEG decoder which handles,
 // among other things, partially downloaded data.
-class GFX_EXPORT JPEGCodec {
+class CODEC_EXPORT JPEGCodec {
  public:
   enum ColorFormat {
     // 3 bytes per pixel (packed), in RGB order regardless of endianness.
diff --git a/ui/gfx/codec/png_codec.h b/ui/gfx/codec/png_codec.h
index ba8c815..bcc30e75 100644
--- a/ui/gfx/codec/png_codec.h
+++ b/ui/gfx/codec/png_codec.h
@@ -11,7 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/codec/codec_export.h"
 
 class SkBitmap;
 
@@ -25,7 +25,7 @@
 // isn't as robust as would be required for a browser (see Decode() for more).
 // WebKit has its own more complicated PNG decoder which handles, among other
 // things, partially downloaded data.
-class GFX_EXPORT PNGCodec {
+class CODEC_EXPORT PNGCodec {
  public:
   enum ColorFormat {
     // 3 bytes per pixel (packed), in RGB order regardless of endianness.
@@ -47,7 +47,7 @@
   };
 
   // Represents a comment in the tEXt ancillary chunk of the png.
-  struct GFX_EXPORT Comment {
+  struct CODEC_EXPORT Comment {
     Comment(const std::string& k, const std::string& t);
     ~Comment();
 
diff --git a/ui/gfx/codec/skia_image_encoder_adapter.h b/ui/gfx/codec/skia_image_encoder_adapter.h
index f878969..f231a494 100644
--- a/ui/gfx/codec/skia_image_encoder_adapter.h
+++ b/ui/gfx/codec/skia_image_encoder_adapter.h
@@ -6,7 +6,7 @@
 #define UI_GFX_CODEC_SKIA_IMAGE_ENCODER_ADAPTER_H
 
 #include "third_party/skia/include/core/SkEncodedImageFormat.h"
-#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/codec/codec_export.h"
 
 class SkWStream;
 class SkPixmap;
@@ -15,10 +15,10 @@
 
 // Matches signature of Skia's SkEncodeImage, but makes use of Chromium's
 // encoders.
-GFX_EXPORT bool EncodeSkiaImage(SkWStream* dst,
-                                const SkPixmap& pixmap,
-                                SkEncodedImageFormat format,
-                                int quality);
+CODEC_EXPORT bool EncodeSkiaImage(SkWStream* dst,
+                                  const SkPixmap& pixmap,
+                                  SkEncodedImageFormat format,
+                                  int quality);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index d32ddc3..b90b8c9 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -283,6 +283,13 @@
          transfer_ == TransferID::IEC61966_2_1_HDR;
 }
 
+bool ColorSpace::FullRangeEncodedValues() const {
+  return transfer_ == TransferID::LINEAR_HDR ||
+         transfer_ == TransferID::IEC61966_2_1_HDR ||
+         transfer_ == TransferID::BT1361_ECG ||
+         transfer_ == TransferID::IEC61966_2_4;
+}
+
 bool ColorSpace::operator!=(const ColorSpace& other) const {
   return !(*this == other);
 }
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index 8a05ba0..0331be7 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -155,7 +155,10 @@
   bool operator<(const ColorSpace& other) const;
   std::string ToString() const;
 
+  // Returns true if the decoded values can be outside of the 0.0-1.0 range.
   bool IsHDR() const;
+  // Returns true if the encoded values can be outside of the 0.0-1.0 range.
+  bool FullRangeEncodedValues() const;
 
   // Return this color space with any range adjust or YUV to RGB conversion
   // stripped off.
diff --git a/ui/gl/init/gl_factory_ozone.cc b/ui/gl/init/gl_factory_ozone.cc
index 5d740f2..9bd31eb 100644
--- a/ui/gl/init/gl_factory_ozone.cc
+++ b/ui/gl/init/gl_factory_ozone.cc
@@ -18,7 +18,7 @@
 namespace init {
 
 std::vector<GLImplementation> GetAllowedGLImplementations() {
-  ui::OzonePlatform::InitializeForGPU();
+  DCHECK(GetSurfaceFactoryOzone());
   return GetSurfaceFactoryOzone()->GetAllowedGLImplementations();
 }
 
diff --git a/ui/gl/test/gl_image_test_support.cc b/ui/gl/test/gl_image_test_support.cc
index 79709c4..9034918f 100644
--- a/ui/gl/test/gl_image_test_support.cc
+++ b/ui/gl/test/gl_image_test_support.cc
@@ -12,12 +12,19 @@
 
 #if defined(USE_OZONE)
 #include "base/run_loop.h"
+#include "ui/ozone/public/ozone_platform.h"
 #endif
 
 namespace gl {
 
 // static
 void GLImageTestSupport::InitializeGL() {
+#if defined(USE_OZONE)
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForGPU(params);
+#endif
+
   std::vector<GLImplementation> allowed_impls =
       init::GetAllowedGLImplementations();
   DCHECK(!allowed_impls.empty());
diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc
index 272e0fd7..dcfd5cae 100644
--- a/ui/gl/test/gl_surface_test_support.cc
+++ b/ui/gl/test/gl_surface_test_support.cc
@@ -32,6 +32,13 @@
 #if defined(USE_X11)
   XInitThreads();
 #endif
+
+#if defined(USE_OZONE)
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForGPU(params);
+#endif
+
   ui::test::EnableTestConfigForPlatformWindows();
 
   bool use_software_gl = true;
@@ -88,17 +95,21 @@
 // static
 void GLSurfaceTestSupport::InitializeOneOffWithMockBindings() {
 #if defined(USE_OZONE)
-  // This function skips where Ozone is otherwise initialized.
-  ui::OzonePlatform::InitializeForGPU();
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForGPU(params);
 #endif
+
   InitializeOneOffImplementation(kGLImplementationMockGL, false);
 }
 
 void GLSurfaceTestSupport::InitializeOneOffWithStubBindings() {
 #if defined(USE_OZONE)
-  // This function skips where Ozone is otherwise initialized.
-  ui::OzonePlatform::InitializeForGPU();
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForGPU(params);
 #endif
+
   InitializeOneOffImplementation(kGLImplementationStubGL, false);
 }
 
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index b723460a..04caf81 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -51,12 +51,6 @@
 }
 
 // static
-void OzonePlatform::InitializeForGPU() {
-  const InitParams params;
-  OzonePlatform::InitializeForGPU(params);
-}
-
-// static
 void OzonePlatform::InitializeForGPU(const InitParams& args) {
   EnsureInstance();
   if (g_platform_initialized_gpu)
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index f1995c1..ad45363 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -89,10 +89,6 @@
   // InitalizeForUI.
   static void InitializeForUI(const InitParams& args);
 
-  // Initializes the subsystems/resources necessary for rendering (i.e. GPU).
-  // TODO(rjkroege): Remove deprecated entry point (http://crbug.com/620934)
-  static void InitializeForGPU();
-
   // Initializes the subsystems for rendering but with additional properties
   // provided by |args| as with InitalizeForUI.
   static void InitializeForGPU(const InitParams& args);
diff --git a/ui/webui/resources/js/compiled_resources.gyp b/ui/webui/resources/js/compiled_resources.gyp
index d679f4c..a5764736 100644
--- a/ui/webui/resources/js/compiled_resources.gyp
+++ b/ui/webui/resources/js/compiled_resources.gyp
@@ -23,15 +23,13 @@
       'target_name': 'i18n_template_no_process',
       'variables': {
         'depends': ['compiled_resources.gyp:load_time_data'],
-        'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'],
       },
       'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'],
     },
     {
       'target_name': 'i18n_template',
       'variables': {
-        'depends': ['compiled_resources.gyp:load_time_data'],
-        'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'],
+        'depends': ['compiled_resources.gyp:i18n_template_no_process'],
       },
       'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'],
     },
diff --git a/ui/webui/resources/js/compiled_resources2.gyp b/ui/webui/resources/js/compiled_resources2.gyp
index e7849f77..1316c15 100644
--- a/ui/webui/resources/js/compiled_resources2.gyp
+++ b/ui/webui/resources/js/compiled_resources2.gyp
@@ -36,17 +36,13 @@
       'target_name': 'i18n_template_no_process',
       'dependencies': [
         'load_time_data',
-        '<(EXTERNS_GYP):pending_compiler_externs',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
     {
       'target_name': 'i18n_template',
       'dependencies': [
-        'load_time_data',
-        # Ideally, <include> would automatically import externs as well, but
-        # it current doesn't and that sounds hard. Let's just kill <include>.
-        '<(EXTERNS_GYP):pending_compiler_externs',
+        'i18n_template_no_process',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },