diff --git a/BUILD.gn b/BUILD.gn
index 48b7396..97ce0d2 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -681,6 +681,7 @@
       "//chrome/installer/setup:setup_unittests",
       "//chrome_elf:chrome_elf_unittests",
       "//chrome_elf:dll_hash_main",
+      "//cloud_print:cloud_print_unittests",
       "//components/wifi:wifi_test",
       "//net:quic_client",
       "//net:quic_server",
diff --git a/DEPS b/DEPS
index 7bbe07b..5c9c3797 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b048e81c5c27fe6c6134eaf9ab96594e2eee0d1d',
+  'skia_revision': '91db12d89c214235e24599f3ec18df2f952e99eb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'e01db2c9ecb62bbdedb9c74c7e282bbaa8131eb3',
+  'v8_revision': '6cddaff4ec73ce23ceec1e77829d12705dfed900',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -56,7 +56,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '102c16366d8b26e4a116d42f334860756d0e268e',
+  'buildtools_revision': '64e38f0cebdde27aa0cfb405f330063582f9ac76',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # 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': '4bed2af0c049bf499dcdb1327a47d40b4c0db92d',
+  'pdfium_revision': '7341149c634e0ab9a619898826440f6e952cf0aa',
   # 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': '627b0d9726b50a0cbd92ddb741d31ba5c184a2e5',
+  'catapult_revision': '750e652668fcfb915d77007f14c5b6befb3a704e',
   # 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/WATCHLISTS b/WATCHLISTS
index a90202e0..a278cd03 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -578,7 +578,7 @@
       'filepath': 'content/common/sandbox.*linux.cc',
     },
     'mac': {
-      'filepath': '(_|/)(cocoa|mac)(_|\.)|/(cocoa|mac)/|\.mm?$',
+      'filepath': '(_|/)(cocoa|mac)(_|\.)|/(cocoa|mac)/|^((?!ios\/).)*\.mm?$',
     },
     'manifest': {
       'filepath': 'content/(browser|renderer)/manifest/'\
diff --git a/base/feature_list.cc b/base/feature_list.cc
index 89b105d..c2f268b 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
+#include "base/pickle.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 
@@ -27,6 +28,42 @@
 // Tracks whether the FeatureList instance was initialized via an accessor.
 bool g_initialized_from_accessor = false;
 
+const uint32_t kFeatureType = 0x06567CA6 + 1;  // SHA1(FeatureEntry) v1
+
+// An allocator entry for a feature in shared memory. The FeatureEntry is
+// followed by a base::Pickle object that contains the feature and trial name.
+// Any changes to this structure requires a bump in kFeatureType defined above.
+struct FeatureEntry {
+  // Expected size for 32/64-bit check.
+  static constexpr size_t kExpectedInstanceSize = 8;
+
+  // Specifies whether a feature override enables or disables the feature. Same
+  // values as the OverrideState enum in feature_list.h
+  uint32_t override_state;
+
+  // Size of the pickled structure, NOT the total size of this entry.
+  uint32_t pickle_size;
+
+  // Reads the feature and trial name from the pickle. Calling this is only
+  // valid on an initialized entry that's in shared memory.
+  bool GetFeatureAndTrialName(StringPiece* feature_name,
+                              StringPiece* trial_name) const {
+    const char* src =
+        reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
+
+    Pickle pickle(src, pickle_size);
+    PickleIterator pickle_iter(pickle);
+
+    if (!pickle_iter.ReadStringPiece(feature_name))
+      return false;
+
+    // Return true because we are not guaranteed to have a trial name anyways.
+    auto sink = pickle_iter.ReadStringPiece(trial_name);
+    ALLOW_UNUSED_LOCAL(sink);
+    return true;
+  }
+};
+
 // Some characters are not allowed to appear in feature names or the associated
 // field trial names, as they are used as special characters for command-line
 // serialization. This function checks that the strings are ASCII (since they
@@ -56,6 +93,31 @@
   initialized_from_command_line_ = true;
 }
 
+void FeatureList::InitializeFromSharedMemory(
+    PersistentMemoryAllocator* allocator) {
+  DCHECK(!initialized_);
+
+  PersistentMemoryAllocator::Iterator iter(allocator);
+
+  PersistentMemoryAllocator::Reference ref;
+  while ((ref = iter.GetNextOfType(kFeatureType)) !=
+         PersistentMemoryAllocator::kReferenceNull) {
+    const FeatureEntry* entry =
+        allocator->GetAsObject<const FeatureEntry>(ref, kFeatureType);
+
+    OverrideState override_state =
+        static_cast<OverrideState>(entry->override_state);
+
+    StringPiece feature_name;
+    StringPiece trial_name;
+    if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
+      continue;
+
+    FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
+    RegisterOverride(feature_name, override_state, trial);
+  }
+}
+
 bool FeatureList::IsFeatureOverriddenFromCommandLine(
     const std::string& feature_name,
     OverrideState state) const {
@@ -98,6 +160,33 @@
   RegisterOverride(feature_name, override_state, field_trial);
 }
 
+void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
+  DCHECK(initialized_);
+
+  for (const auto& override : overrides_) {
+    Pickle pickle;
+    pickle.WriteString(override.first);
+    if (override.second.field_trial)
+      pickle.WriteString(override.second.field_trial->trial_name());
+
+    size_t total_size = sizeof(FeatureEntry) + pickle.size();
+    PersistentMemoryAllocator::Reference ref =
+        allocator->Allocate(total_size, kFeatureType);
+    if (!ref)
+      return;
+
+    FeatureEntry* entry =
+        allocator->GetAsObject<FeatureEntry>(ref, kFeatureType);
+    entry->override_state = override.second.overridden_state;
+    entry->pickle_size = pickle.size();
+
+    char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
+    memcpy(dst, pickle.data(), pickle.size());
+
+    allocator->MakeIterable(ref);
+  }
+}
+
 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
                                       std::string* disable_overrides) {
   DCHECK(initialized_);
diff --git a/base/feature_list.h b/base/feature_list.h
index 80209ba..daedd73 100644
--- a/base/feature_list.h
+++ b/base/feature_list.h
@@ -13,6 +13,7 @@
 #include "base/base_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "base/metrics/persistent_memory_allocator.h"
 #include "base/strings/string_piece.h"
 #include "base/synchronization/lock.h"
 
@@ -92,6 +93,11 @@
   void InitializeFromCommandLine(const std::string& enable_features,
                                  const std::string& disable_features);
 
+  // Initializes feature overrides through the field trial allocator, which
+  // we're using to store the feature names, their override state, and the name
+  // of the associated field trial.
+  void InitializeFromSharedMemory(PersistentMemoryAllocator* allocator);
+
   // Specifies whether a feature override enables or disables the feature.
   enum OverrideState {
     OVERRIDE_USE_DEFAULT,
@@ -124,6 +130,9 @@
                                   OverrideState override_state,
                                   FieldTrial* field_trial);
 
+  // Loops through feature overrides and serializes them all into |allocator|.
+  void AddFeaturesToAllocator(PersistentMemoryAllocator* allocator);
+
   // Returns comma-separated lists of feature names (in the same format that is
   // accepted by InitializeFromCommandLine()) corresponding to features that
   // have been overridden - either through command-line or via FieldTrials. For
@@ -180,6 +189,10 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity);
+  FRIEND_TEST_ALL_PREFIXES(FeatureListTest,
+                           StoreAndRetrieveFeaturesFromSharedMemory);
+  FRIEND_TEST_ALL_PREFIXES(FeatureListTest,
+                           StoreAndRetrieveAssociatedFeaturesFromSharedMemory);
 
   struct OverrideEntry {
     // The overridden enable (on/off) state of the feature.
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index 9d1dcb7..189e974 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/persistent_memory_allocator.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -468,4 +469,68 @@
   EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
 }
 
+TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) {
+  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+
+  // Create some overrides.
+  feature_list->RegisterOverride(kFeatureOffByDefaultName,
+                                 FeatureList::OVERRIDE_ENABLE_FEATURE, nullptr);
+  feature_list->RegisterOverride(
+      kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, nullptr);
+  feature_list->FinalizeInitialization();
+
+  // Create an allocator and store the overrides.
+  std::unique_ptr<SharedMemory> shm(new SharedMemory());
+  shm->CreateAndMapAnonymous(4 << 10);
+  SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false);
+  feature_list->AddFeaturesToAllocator(&allocator);
+
+  std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
+
+  // Check that the new feature list is empty.
+  EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
+      kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
+  EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
+      kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
+
+  feature_list2->InitializeFromSharedMemory(&allocator);
+  // Check that the new feature list now has 2 overrides.
+  EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
+      kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
+  EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
+      kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
+}
+
+TEST_F(FeatureListTest, StoreAndRetrieveAssociatedFeaturesFromSharedMemory) {
+  FieldTrialList field_trial_list(nullptr);
+  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+
+  // Create some overrides.
+  FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
+  FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
+  feature_list->RegisterFieldTrialOverride(
+      kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
+  feature_list->RegisterFieldTrialOverride(
+      kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
+  feature_list->FinalizeInitialization();
+
+  // Create an allocator and store the overrides.
+  std::unique_ptr<SharedMemory> shm(new SharedMemory());
+  shm->CreateAndMapAnonymous(4 << 10);
+  SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false);
+  feature_list->AddFeaturesToAllocator(&allocator);
+
+  std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
+  feature_list2->InitializeFromSharedMemory(&allocator);
+  feature_list2->FinalizeInitialization();
+
+  // Check that the field trials are still associated.
+  FieldTrial* associated_trial1 =
+      feature_list2->GetAssociatedFieldTrial(kFeatureOnByDefault);
+  FieldTrial* associated_trial2 =
+      feature_list2->GetAssociatedFieldTrial(kFeatureOffByDefault);
+  EXPECT_EQ(associated_trial1, trial1);
+  EXPECT_EQ(associated_trial2, trial2);
+}
+
 }  // namespace base
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index f11ef35..505bba24 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -10,7 +10,6 @@
 #include "base/base_switches.h"
 #include "base/build_time.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/pickle.h"
@@ -49,6 +48,10 @@
 // for now while the implementation is fleshed out (e.g. data format, single
 // shared memory segment). See https://codereview.chromium.org/2365273004/ and
 // crbug.com/653874
+// The browser is the only process that has write access to the shared memory.
+// This is safe from race conditions because MakeIterable is a release operation
+// and GetNextOfType is an acquire operation, so memory writes before
+// MakeIterable happen before memory reads after GetNextOfType.
 const bool kUseSharedMemoryForFieldTrials = true;
 
 // Constants for the field trial allocator.
@@ -248,7 +251,19 @@
   return true;
 }
 
-void AddForceFieldTrialsFlag(CommandLine* cmd_line) {
+void AddFeatureAndFieldTrialFlags(const char* enable_features_switch,
+                                  const char* disable_features_switch,
+                                  CommandLine* cmd_line) {
+  std::string enabled_features;
+  std::string disabled_features;
+  FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features,
+                                                  &disabled_features);
+
+  if (!enabled_features.empty())
+    cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features);
+  if (!disabled_features.empty())
+    cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features);
+
   std::string field_trial_states;
   FieldTrialList::AllStatesToString(&field_trial_states);
   if (!field_trial_states.empty()) {
@@ -805,6 +820,24 @@
   }
 }
 
+// static
+void FieldTrialList::CreateFeaturesFromCommandLine(
+    const base::CommandLine& command_line,
+    const char* enable_features_switch,
+    const char* disable_features_switch,
+    FeatureList* feature_list) {
+  // Fallback to command line if not using shared memory.
+  if (!kUseSharedMemoryForFieldTrials ||
+      !global_->field_trial_allocator_.get()) {
+    return feature_list->InitializeFromCommandLine(
+        command_line.GetSwitchValueASCII(enable_features_switch),
+        command_line.GetSwitchValueASCII(disable_features_switch));
+  }
+
+  feature_list->InitializeFromSharedMemory(
+      global_->field_trial_allocator_.get());
+}
+
 #if defined(POSIX_WITH_ZYGOTE)
 // static
 bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) {
@@ -854,12 +887,19 @@
 // static
 void FieldTrialList::CopyFieldTrialStateToFlags(
     const char* field_trial_handle_switch,
+    const char* enable_features_switch,
+    const char* disable_features_switch,
     CommandLine* cmd_line) {
   // TODO(lawrencewu): Ideally, having the global would be guaranteed. However,
   // content browser tests currently don't create a FieldTrialList because they
   // don't run ChromeBrowserMainParts code where it's done for Chrome.
-  if (!global_)
+  // Some tests depend on the enable and disable features flag switch, though,
+  // so we can still add those even though AllStatesToString() will be a no-op.
+  if (!global_) {
+    AddFeatureAndFieldTrialFlags(enable_features_switch,
+                                 disable_features_switch, cmd_line);
     return;
+  }
 
 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE)
   // Use shared memory to pass the state if the feature is enabled, otherwise
@@ -869,7 +909,8 @@
     // If the readonly handle didn't get duplicated properly, then fallback to
     // original behavior.
     if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) {
-      AddForceFieldTrialsFlag(cmd_line);
+      AddFeatureAndFieldTrialFlags(enable_features_switch,
+                                   disable_features_switch, cmd_line);
       return;
     }
 
@@ -895,7 +936,8 @@
   }
 #endif
 
-  AddForceFieldTrialsFlag(cmd_line);
+  AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch,
+                               cmd_line);
 }
 
 // static
@@ -1171,6 +1213,10 @@
     AddToAllocatorWhileLocked(registered.second);
   }
 
+  // Add all existing features.
+  FeatureList::GetInstance()->AddFeaturesToAllocator(
+      global_->field_trial_allocator_.get());
+
 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE)
   // Set |readonly_allocator_handle_| so we can pass it to be inherited and
   // via the command line.
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 01bb35c..7fedb37 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -65,6 +65,7 @@
 
 #include "base/base_export.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/files/file.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -508,6 +509,14 @@
                                           const char* field_trial_handle_switch,
                                           int fd_key);
 
+  // Creates base::Feature overrides from the command line by first trying to
+  // use shared memory and then falling back to the command line if it fails.
+  static void CreateFeaturesFromCommandLine(
+      const base::CommandLine& command_line,
+      const char* enable_features_switch,
+      const char* disable_features_switch,
+      FeatureList* feature_list);
+
 #if defined(OS_WIN)
   // On Windows, we need to explicitly pass down any handles to be inherited.
   // This function adds the shared memory handle to field trial state to the
@@ -527,6 +536,8 @@
   // Needs the |field_trial_handle_switch| argument to be passed in since base/
   // can't depend on content/.
   static void CopyFieldTrialStateToFlags(const char* field_trial_handle_switch,
+                                         const char* enable_features_switch,
+                                         const char* disable_features_switch,
                                          base::CommandLine* cmd_line);
 
   // Create a FieldTrial with the given |name| and using 100% probability for
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 6c07026..05776a11 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/gtest_util.h"
 #include "base/test/mock_entropy_provider.h"
+#include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -1145,15 +1146,21 @@
   base::FilePath test_file_path = base::FilePath(FILE_PATH_LITERAL("Program"));
   base::CommandLine cmd_line = base::CommandLine(test_file_path);
   const char field_trial_handle[] = "test-field-trial-handle";
+  const char enable_features_switch[] = "test-enable-features";
+  const char disable_features_switch[] = "test-disable-features";
 
-  base::FieldTrialList::CopyFieldTrialStateToFlags(field_trial_handle,
-                                                   &cmd_line);
+  base::FieldTrialList::CopyFieldTrialStateToFlags(
+      field_trial_handle, enable_features_switch, disable_features_switch,
+      &cmd_line);
   EXPECT_TRUE(cmd_line.HasSwitch(field_trial_handle) ||
               cmd_line.HasSwitch(switches::kForceFieldTrials));
 }
 #endif
 
 TEST(FieldTrialListTest, InstantiateAllocator) {
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.Init();
+
   FieldTrialList field_trial_list(nullptr);
   FieldTrialList::CreateFieldTrial("Trial1", "Group1");
 
@@ -1176,6 +1183,9 @@
   // Scoping the first FieldTrialList, as we need another one to test that it
   // matches.
   {
+    test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.Init();
+
     FieldTrialList field_trial_list(nullptr);
     FieldTrialList::CreateFieldTrial("Trial1", "Group1");
     FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
@@ -1198,6 +1208,9 @@
   constexpr char kTrialName[] = "trial";
   base::SharedMemoryHandle handle;
   {
+    test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.Init();
+
     // Create a simulated trial and a real trial and call group() on them, which
     // should only add the real trial to the field trial allocator.
     FieldTrialList field_trial_list(nullptr);
@@ -1230,6 +1243,9 @@
 }
 
 TEST(FieldTrialListTest, AssociateFieldTrialParams) {
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.Init();
+
   std::string trial_name("Trial1");
   std::string group_name("Group1");
 
@@ -1266,6 +1282,9 @@
 
   base::SharedMemoryHandle handle;
   {
+    test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.Init();
+
     // Create a field trial with some params.
     FieldTrialList field_trial_list(nullptr);
     FieldTrial* trial =
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 21fe3a80..98086039 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -55,19 +55,17 @@
 
 // Convenience function for determining if a numeric value is negative without
 // throwing compiler warnings on: unsigned(value) < 0.
-template <typename T>
-constexpr typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type
-IsValueNegative(T value) {
-  static_assert(std::numeric_limits<T>::is_specialized,
-                "Argument must be numeric.");
+template <typename T,
+          typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
+constexpr bool IsValueNegative(T value) {
+  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
   return value < 0;
 }
 
-template <typename T>
-constexpr typename std::enable_if<!std::numeric_limits<T>::is_signed,
-                                  bool>::type IsValueNegative(T) {
-  static_assert(std::numeric_limits<T>::is_specialized,
-                "Argument must be numeric.");
+template <typename T,
+          typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
+constexpr bool IsValueNegative(T) {
+  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
   return false;
 }
 
@@ -117,7 +115,7 @@
   return constraint == RANGE_VALID
              ? static_cast<Dst>(value)
              : (constraint == RANGE_UNDERFLOW
-                    ? std::numeric_limits<Dst>::min()
+                    ? std::numeric_limits<Dst>::lowest()
                     : (constraint == RANGE_OVERFLOW
                            ? std::numeric_limits<Dst>::max()
                            : NaNHandler::template HandleFailure<Dst>()));
@@ -132,7 +130,7 @@
           typename Src>
 constexpr Dst saturated_cast(Src value) {
   using SrcType = typename UnderlyingType<Src>::type;
-  return std::numeric_limits<Dst>::is_iec559
+  return std::is_floating_point<Dst>::value
              ? static_cast<Dst>(
                    static_cast<SrcType>(value))  // Floating point optimization.
              : internal::saturated_cast_impl<Dst, NaNHandler>(
@@ -147,8 +145,7 @@
 constexpr Dst strict_cast(Src value) {
   using SrcType = typename UnderlyingType<Src>::type;
   static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
-  static_assert(std::numeric_limits<Dst>::is_specialized,
-                "Result must be numeric.");
+  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
 
   // If you got here from a compiler error, it's because you tried to assign
   // from a source type to a destination type that has insufficient range.
@@ -193,7 +190,7 @@
 template <typename T>
 class StrictNumeric {
  public:
-  typedef T type;
+  using type = T;
 
   constexpr StrictNumeric() : value_(0) {}
 
@@ -269,8 +266,8 @@
 using internal::StrictNumeric;
 using internal::MakeStrictNum;
 
-// Explicitly make a shorter size_t typedef for convenience.
-typedef StrictNumeric<size_t> SizeT;
+// Explicitly make a shorter size_t alias for convenience.
+using SizeT = StrictNumeric<size_t>;
 
 }  // namespace base
 
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h
index be9eaa1..ef5adf0 100644
--- a/base/numerics/safe_conversions_impl.h
+++ b/base/numerics/safe_conversions_impl.h
@@ -16,14 +16,20 @@
 namespace internal {
 
 // The std library doesn't provide a binary max_exponent for integers, however
-// we can compute one by adding one to the number of non-sign bits. This allows
-// for accurate range comparisons between floating point and integer types.
+// we can compute an analog using std::numeric_limits<>::digits.
 template <typename NumericType>
 struct MaxExponent {
-  static const int value = std::numeric_limits<NumericType>::is_iec559
+  static const int value = std::is_floating_point<NumericType>::value
                                ? std::numeric_limits<NumericType>::max_exponent
-                               : (sizeof(NumericType) * CHAR_BIT + 1 -
-                                  std::numeric_limits<NumericType>::is_signed);
+                               : std::numeric_limits<NumericType>::digits + 1;
+};
+
+// The number of bits (including the sign) in an integer. Eliminates sizeof
+// hacks.
+template <typename NumericType>
+struct IntegerBitsPlusSign {
+  static const int value = std::numeric_limits<NumericType>::digits +
+                           std::is_signed<NumericType>::value;
 };
 
 enum IntegerRepresentation {
@@ -33,7 +39,7 @@
 
 // A range for a given nunmeric Src type is contained for a given numeric Dst
 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
-// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
+// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
 // We implement this as template specializations rather than simple static
 // comparisons to ensure type correctness in our comparisons.
 enum NumericRangeRepresentation {
@@ -44,16 +50,14 @@
 // Helper templates to statically determine if our destination type can contain
 // maximum and minimum values represented by the source type.
 
-template <
-    typename Dst,
-    typename Src,
-    IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
-                                            ? INTEGER_REPRESENTATION_SIGNED
-                                            : INTEGER_REPRESENTATION_UNSIGNED,
-    IntegerRepresentation SrcSign =
-        std::numeric_limits<Src>::is_signed
-            ? INTEGER_REPRESENTATION_SIGNED
-            : INTEGER_REPRESENTATION_UNSIGNED >
+template <typename Dst,
+          typename Src,
+          IntegerRepresentation DstSign = std::is_signed<Dst>::value
+                                              ? INTEGER_REPRESENTATION_SIGNED
+                                              : INTEGER_REPRESENTATION_UNSIGNED,
+          IntegerRepresentation SrcSign = std::is_signed<Src>::value
+                                              ? INTEGER_REPRESENTATION_SIGNED
+                                              : INTEGER_REPRESENTATION_UNSIGNED>
 struct StaticDstRangeRelationToSrcRange;
 
 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
@@ -135,8 +139,8 @@
 // such that the resulting maximum is represented exactly as a floating point.
 template <typename Dst, typename Src>
 struct NarrowingRange {
-  typedef typename std::numeric_limits<Src> SrcLimits;
-  typedef typename std::numeric_limits<Dst> DstLimits;
+  using SrcLimits = typename std::numeric_limits<Src>;
+  using DstLimits = typename std::numeric_limits<Dst>;
   // The following logic avoids warnings where the max function is
   // instantiated with invalid values for a bit shift (even though
   // such a function can never be called).
@@ -154,23 +158,19 @@
     return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1);
   }
 
-  static constexpr Dst min() {
-    return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max()
-                                               : DstLimits::min();
-  }
+  static constexpr Dst lowest() { return DstLimits::lowest(); }
 };
 
-template <
-    typename Dst,
-    typename Src,
-    IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
-                                            ? INTEGER_REPRESENTATION_SIGNED
-                                            : INTEGER_REPRESENTATION_UNSIGNED,
-    IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
-                                            ? INTEGER_REPRESENTATION_SIGNED
-                                            : INTEGER_REPRESENTATION_UNSIGNED,
-    NumericRangeRepresentation DstRange =
-        StaticDstRangeRelationToSrcRange<Dst, Src>::value >
+template <typename Dst,
+          typename Src,
+          IntegerRepresentation DstSign = std::is_signed<Dst>::value
+                                              ? INTEGER_REPRESENTATION_SIGNED
+                                              : INTEGER_REPRESENTATION_UNSIGNED,
+          IntegerRepresentation SrcSign = std::is_signed<Src>::value
+                                              ? INTEGER_REPRESENTATION_SIGNED
+                                              : INTEGER_REPRESENTATION_UNSIGNED,
+          NumericRangeRepresentation DstRange =
+              StaticDstRangeRelationToSrcRange<Dst, Src>::value>
 struct DstRangeRelationToSrcRangeImpl;
 
 // The following templates are for ranges that must be verified at runtime. We
@@ -200,7 +200,7 @@
                                       NUMERIC_RANGE_NOT_CONTAINED> {
   static constexpr RangeConstraint Check(Src value) {
     return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()),
-                              (value >= NarrowingRange<Dst, Src>::min()));
+                              (value >= NarrowingRange<Dst, Src>::lowest()));
   }
 };
 
@@ -224,7 +224,7 @@
                                       INTEGER_REPRESENTATION_UNSIGNED,
                                       NUMERIC_RANGE_NOT_CONTAINED> {
   static constexpr RangeConstraint Check(Src value) {
-    return sizeof(Dst) > sizeof(Src)
+    return IntegerBitsPlusSign<Dst>::value > IntegerBitsPlusSign<Src>::value
                ? RANGE_VALID
                : GetRangeConstraint(
                      value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
@@ -251,83 +251,48 @@
 
 template <typename Dst, typename Src>
 constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) {
-  static_assert(std::numeric_limits<Src>::is_specialized,
-                "Argument must be numeric.");
-  static_assert(std::numeric_limits<Dst>::is_specialized,
-                "Result must be numeric.");
+  static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
   return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
 }
 
 // Integer promotion templates used by the portable checked integer arithmetic.
 template <size_t Size, bool IsSigned>
-struct IntegerForSizeAndSign;
-template <>
-struct IntegerForSizeAndSign<1, true> {
-  typedef int8_t type;
-};
-template <>
-struct IntegerForSizeAndSign<1, false> {
-  typedef uint8_t type;
-};
-template <>
-struct IntegerForSizeAndSign<2, true> {
-  typedef int16_t type;
-};
-template <>
-struct IntegerForSizeAndSign<2, false> {
-  typedef uint16_t type;
-};
-template <>
-struct IntegerForSizeAndSign<4, true> {
-  typedef int32_t type;
-};
-template <>
-struct IntegerForSizeAndSign<4, false> {
-  typedef uint32_t type;
-};
-template <>
-struct IntegerForSizeAndSign<8, true> {
-  typedef int64_t type;
-};
-template <>
-struct IntegerForSizeAndSign<8, false> {
-  typedef uint64_t type;
-  static_assert(sizeof(uintmax_t) == 8,
-                "Max integer size not supported for this toolchain.");
-};
+struct IntegerForDigitsAndSign;
+
+#define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
+  template <>                                                   \
+  struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
+                                 std::is_signed<I>::value> {    \
+    using type = I;                                             \
+  }
+
+INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
+#undef INTEGER_FOR_DIGITS_AND_SIGN
 
 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
 // support 128-bit math, then the ArithmeticPromotion template below will need
 // to be updated (or more likely replaced with a decltype expression).
+static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
+              "Max integer size not supported for this toolchain.");
 
-template <typename Integer>
-struct UnsignedIntegerForSize {
-  typedef typename std::enable_if<
-      std::numeric_limits<Integer>::is_integer,
-      typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
-};
-
-template <typename Integer>
-struct SignedIntegerForSize {
-  typedef typename std::enable_if<
-      std::numeric_limits<Integer>::is_integer,
-      typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
-};
-
-template <typename Integer>
+template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
 struct TwiceWiderInteger {
-  typedef typename std::enable_if<
-      std::numeric_limits<Integer>::is_integer,
-      typename IntegerForSizeAndSign<
-          sizeof(Integer) * 2,
-          std::numeric_limits<Integer>::is_signed>::type>::type type;
+  using type =
+      typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
+                                       IsSigned>::type;
 };
 
 template <typename Integer>
 struct PositionOfSignBit {
-  static const typename std::enable_if<std::numeric_limits<Integer>::is_integer,
-                                       size_t>::type value =
-      CHAR_BIT * sizeof(Integer) - 1;
+  static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
 };
 
 enum ArithmeticPromotionCategory {
@@ -382,20 +347,20 @@
 };
 
 // Determines the type that is best able to represent an arithmetic result.
-template <typename Lhs,
-          typename Rhs = Lhs,
-          bool is_intmax_type =
-              std::is_integral<
-                  typename MaxExponentPromotion<Lhs, Rhs>::type>::value &&
-              sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) ==
-                  sizeof(intmax_t),
-          bool is_max_exponent =
-              StaticDstRangeRelationToSrcRange<
-                  typename MaxExponentPromotion<Lhs, Rhs>::type,
-                  Lhs>::value ==
-              NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
-                  typename MaxExponentPromotion<Lhs, Rhs>::type,
-                  Rhs>::value == NUMERIC_RANGE_CONTAINED>
+template <
+    typename Lhs,
+    typename Rhs = Lhs,
+    bool is_intmax_type =
+        std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
+            IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
+                value == IntegerBitsPlusSign<intmax_t>::value,
+    bool is_max_exponent =
+        StaticDstRangeRelationToSrcRange<
+            typename MaxExponentPromotion<Lhs, Rhs>::type,
+            Lhs>::value ==
+        NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
+            typename MaxExponentPromotion<Lhs, Rhs>::type,
+            Rhs>::value == NUMERIC_RANGE_CONTAINED>
 struct BigEnoughPromotion;
 
 // The side with the max exponent is big enough.
@@ -408,9 +373,10 @@
 // We can use a twice wider type to fit.
 template <typename Lhs, typename Rhs>
 struct BigEnoughPromotion<Lhs, Rhs, false, false> {
-  using type = typename IntegerForSizeAndSign<
-      sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) * 2,
-      std::is_signed<Lhs>::value || std::is_signed<Rhs>::value>::type;
+  using type =
+      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
+                                 std::is_signed<Lhs>::value ||
+                                     std::is_signed<Rhs>::value>::type;
   static const bool is_contained = true;
 };
 
@@ -427,13 +393,14 @@
 // the source.
 template <typename T, typename Lhs, typename Rhs>
 struct IsIntegerArithmeticSafe {
-  static const bool value = !std::numeric_limits<T>::is_iec559 &&
-                            StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
-                                NUMERIC_RANGE_CONTAINED &&
-                            sizeof(T) >= (2 * sizeof(Lhs)) &&
-                            StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
-                                NUMERIC_RANGE_CONTAINED &&
-                            sizeof(T) >= (2 * sizeof(Rhs));
+  static const bool value =
+      !std::is_floating_point<T>::value &&
+      StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
+          NUMERIC_RANGE_CONTAINED &&
+      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
+      StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
+          NUMERIC_RANGE_CONTAINED &&
+      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
 };
 
 // This hacks around libstdc++ 4.6 missing stuff in type_traits.
diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h
index 9b2177f..32f0dfdd 100644
--- a/base/numerics/safe_math.h
+++ b/base/numerics/safe_math.h
@@ -97,7 +97,7 @@
                 "CheckedNumeric<T>: T must be a numeric type.");
 
  public:
-  typedef T type;
+  using type = T;
 
   constexpr CheckedNumeric() {}
 
@@ -114,8 +114,7 @@
   template <typename Src>
   constexpr CheckedNumeric(Src value)  // NOLINT(runtime/explicit)
       : state_(value) {
-    static_assert(std::numeric_limits<Src>::is_specialized,
-                  "Argument must be numeric.");
+    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
   }
 
   // This is not an explicit constructor because we want a seamless conversion
@@ -208,7 +207,7 @@
   CheckedNumeric operator-() const {
     // Negation is always valid for floating point.
     T value = 0;
-    bool is_valid = (std::numeric_limits<T>::is_iec559 || IsValid()) &&
+    bool is_valid = (std::is_floating_point<T>::value || IsValid()) &&
                     CheckedNeg(state_.value(), &value);
     return CheckedNumeric<T>(value, is_valid);
   }
@@ -223,7 +222,7 @@
   CheckedNumeric Abs() const {
     // Absolute value is always valid for floating point.
     T value = 0;
-    bool is_valid = (std::numeric_limits<T>::is_iec559 || IsValid()) &&
+    bool is_valid = (std::is_floating_point<T>::value || IsValid()) &&
                     CheckedAbs(state_.value(), &value);
     return CheckedNumeric<T>(value, is_valid);
   }
@@ -387,10 +386,8 @@
           typename R,
           typename... Args>
 struct ResultType {
-  // The typedef was required here because MSVC fails to compile with "using".
-  typedef
-      typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type
-          type;
+  using type =
+      typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
 };
 
 // Convience wrapper to return a new CheckedNumeric from the provided arithmetic
diff --git a/base/numerics/safe_math_impl.h b/base/numerics/safe_math_impl.h
index 4b9877a..679cfa4 100644
--- a/base/numerics/safe_math_impl.h
+++ b/base/numerics/safe_math_impl.h
@@ -28,18 +28,18 @@
 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
 // so the float versions will not compile.
 template <typename Numeric,
-          bool IsInteger = std::numeric_limits<Numeric>::is_integer,
-          bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
+          bool IsInteger = std::is_integral<Numeric>::value,
+          bool IsFloat = std::is_floating_point<Numeric>::value>
 struct UnsignedOrFloatForSize;
 
 template <typename Numeric>
 struct UnsignedOrFloatForSize<Numeric, true, false> {
-  typedef typename UnsignedIntegerForSize<Numeric>::type type;
+  using type = typename std::make_unsigned<Numeric>::type;
 };
 
 template <typename Numeric>
 struct UnsignedOrFloatForSize<Numeric, false, true> {
-  typedef Numeric type;
+  using type = Numeric;
 };
 
 // Helper templates for integer manipulations.
@@ -47,7 +47,7 @@
 template <typename T>
 constexpr bool HasSignBit(T x) {
   // Cast to unsigned since right shift on signed is undefined.
-  return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
+  return !!(static_cast<typename std::make_unsigned<T>::type>(x) >>
             PositionOfSignBit<T>::value);
 }
 
@@ -66,23 +66,19 @@
 #define USE_OVERFLOW_BUILTINS (0)
 #endif
 
-// Here are the actual portable checked integer math implementations.
-// TODO(jschuh): Break this code out from the enable_if pattern and find a clean
-// way to coalesce things into the CheckedNumericState specializations below.
-
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
-CheckedAddImpl(T x, T y, T* result) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+bool CheckedAddImpl(T x, T y, T* result) {
   // Since the value of x+y is undefined if we have a signed type, we compute
   // it using the unsigned type of the same size.
-  typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
+  using UnsignedDst = typename std::make_unsigned<T>::type;
   UnsignedDst ux = static_cast<UnsignedDst>(x);
   UnsignedDst uy = static_cast<UnsignedDst>(y);
   UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
   *result = static_cast<T>(uresult);
   // Addition is valid if the sign of (x + y) is equal to either that of x or
   // that of y.
-  return (std::numeric_limits<T>::is_signed)
+  return (std::is_signed<T>::value)
              ? HasSignBit(BinaryComplement(
                    static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))
              : (BinaryComplement(x) >=
@@ -93,11 +89,10 @@
 struct CheckedAddOp {};
 
 template <typename T, typename U>
-struct CheckedAddOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedAddOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static bool Do(T x, U y, V* result) {
@@ -123,19 +118,19 @@
   }
 };
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
-CheckedSubImpl(T x, T y, T* result) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+bool CheckedSubImpl(T x, T y, T* result) {
   // Since the value of x+y is undefined if we have a signed type, we compute
   // it using the unsigned type of the same size.
-  typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
+  using UnsignedDst = typename std::make_unsigned<T>::type;
   UnsignedDst ux = static_cast<UnsignedDst>(x);
   UnsignedDst uy = static_cast<UnsignedDst>(y);
   UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
   *result = static_cast<T>(uresult);
   // Subtraction is valid if either x and y have same sign, or (x-y) and x have
   // the same sign.
-  return (std::numeric_limits<T>::is_signed)
+  return (std::is_signed<T>::value)
              ? HasSignBit(BinaryComplement(
                    static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
              : (x >= y);
@@ -145,11 +140,10 @@
 struct CheckedSubOp {};
 
 template <typename T, typename U>
-struct CheckedSubOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedSubOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static bool Do(T x, U y, V* result) {
@@ -179,36 +173,37 @@
 // we just promote to a twice wider type, and range check the result. In the
 // slow case we need to manually check that the result won't be truncated by
 // checking with division against the appropriate bound.
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            sizeof(T) * 2 <= sizeof(uintmax_t),
-                        bool>::type
-CheckedMulImpl(T x, T y, T* result) {
-  typedef typename TwiceWiderInteger<T>::type IntermediateType;
+template <typename T,
+          typename std::enable_if<
+              std::is_integral<T>::value &&
+              ((IntegerBitsPlusSign<T>::value * 2) <=
+               IntegerBitsPlusSign<intmax_t>::value)>::type* = nullptr>
+bool CheckedMulImpl(T x, T y, T* result) {
+  using IntermediateType = typename TwiceWiderInteger<T>::type;
   IntermediateType tmp =
       static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
   *result = static_cast<T>(tmp);
   return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<T>::is_signed &&
-                            (sizeof(T) * 2 > sizeof(uintmax_t)),
-                        bool>::type
-CheckedMulImpl(T x, T y, T* result) {
+template <typename T,
+          typename std::enable_if<
+              std::is_integral<T>::value && std::is_signed<T>::value &&
+              ((IntegerBitsPlusSign<T>::value * 2) >
+               IntegerBitsPlusSign<intmax_t>::value)>::type* = nullptr>
+bool CheckedMulImpl(T x, T y, T* result) {
   if (x && y) {
     if (x > 0) {
       if (y > 0) {
         if (x > std::numeric_limits<T>::max() / y)
           return false;
       } else {
-        if (y < std::numeric_limits<T>::min() / x)
+        if (y < std::numeric_limits<T>::lowest() / x)
           return false;
       }
     } else {
       if (y > 0) {
-        if (x < std::numeric_limits<T>::min() / y)
+        if (x < std::numeric_limits<T>::lowest() / y)
           return false;
       } else {
         if (y < std::numeric_limits<T>::max() / x)
@@ -220,12 +215,12 @@
   return true;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            !std::numeric_limits<T>::is_signed &&
-                            (sizeof(T) * 2 > sizeof(uintmax_t)),
-                        bool>::type
-CheckedMulImpl(T x, T y, T* result) {
+template <typename T,
+          typename std::enable_if<
+              std::is_integral<T>::value && !std::is_signed<T>::value &&
+              ((IntegerBitsPlusSign<T>::value * 2) >
+               IntegerBitsPlusSign<uintmax_t>::value)>::type* = nullptr>
+bool CheckedMulImpl(T x, T y, T* result) {
   *result = x * y;
   return (y == 0 || x <= std::numeric_limits<T>::max() / y);
 }
@@ -234,11 +229,10 @@
 struct CheckedMulOp {};
 
 template <typename T, typename U>
-struct CheckedMulOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedMulOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static bool Do(T x, U y, V* result) {
@@ -248,8 +242,12 @@
     // support full-width, mixed-sign multiply builtins.
     // https://crbug.com/613003
     static const bool kUseMaxInt =
-        sizeof(__typeof__(x * y)) < sizeof(intptr_t) ||
-        (sizeof(__typeof__(x * y)) == sizeof(intptr_t) &&
+        // Narrower type than uintptr_t is always safe.
+        std::numeric_limits<__typeof__(x * y)>::digits <
+            std::numeric_limits<intptr_t>::digits ||
+        // Safe for intptr_t and uintptr_t if the sign matches.
+        (IntegerBitsPlusSign<__typeof__(x * y)>::value ==
+             IntegerBitsPlusSign<intptr_t>::value &&
          std::is_signed<T>::value == std::is_signed<U>::value);
 #else
     static const bool kUseMaxInt = true;
@@ -280,11 +278,11 @@
 
 // Division just requires a check for a zero denominator or an invalid negation
 // on signed min/-1.
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
-CheckedDivImpl(T x, T y, T* result) {
-  if (y && (!std::numeric_limits<T>::is_signed ||
-            x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+bool CheckedDivImpl(T x, T y, T* result) {
+  if (y && (!std::is_signed<T>::value ||
+            x != std::numeric_limits<T>::lowest() || y != static_cast<T>(-1))) {
     *result = x / y;
     return true;
   }
@@ -295,11 +293,10 @@
 struct CheckedDivOp {};
 
 template <typename T, typename U>
-struct CheckedDivOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedDivOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static bool Do(T x, U y, V* result) {
@@ -316,11 +313,9 @@
   }
 };
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedModImpl(T x, T y, T* result) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+bool CheckedModImpl(T x, T y, T* result) {
   if (y > 0) {
     *result = static_cast<T>(x % y);
     return true;
@@ -328,27 +323,14 @@
   return false;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            !std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedModImpl(T x, T y, T* result) {
-  if (y != 0) {
-    *result = static_cast<T>(x % y);
-    return true;
-  }
-  return false;
-}
-
 template <typename T, typename U, class Enable = void>
 struct CheckedModOp {};
 
 template <typename T, typename U>
-struct CheckedModOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedModOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static bool Do(T x, U y, V* result) {
@@ -368,16 +350,15 @@
 // of bits in the promoted type are undefined. Shifts of negative values
 // are undefined. Otherwise it is defined when the result fits.
 template <typename T, typename U>
-struct CheckedLshOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedLshOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = T;
   template <typename V>
   static bool Do(T x, U shift, V* result) {
-    using ShiftType = typename UnsignedIntegerForSize<T>::type;
-    static const ShiftType kBitWidth = CHAR_BIT * sizeof(T);
+    using ShiftType = typename std::make_unsigned<T>::type;
+    static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value;
     const ShiftType real_shift = static_cast<ShiftType>(shift);
     // Signed shift is not legal on negative values.
     if (!IsValueNegative(x) && real_shift < kBitWidth) {
@@ -398,17 +379,16 @@
 // of bits in the promoted type are undefined. Otherwise, it is always defined,
 // but a right shift of a negative value is implementation-dependent.
 template <typename T, typename U>
-struct CheckedRshOp<
-    T,
-    U,
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<U>::is_integer>::type> {
+struct CheckedRshOp<T,
+                    U,
+                    typename std::enable_if<std::is_integral<T>::value &&
+                                            std::is_integral<U>::value>::type> {
   using result_type = T;
   template <typename V = result_type>
   static bool Do(T x, U shift, V* result) {
     // Use the type conversion push negative values out of range.
-    using ShiftType = typename UnsignedIntegerForSize<T>::type;
-    if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) {
+    using ShiftType = typename std::make_unsigned<T>::type;
+    if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) {
       T tmp = x >> shift;
       *result = static_cast<V>(tmp);
       return IsValueInRangeForNumericType<V>(tmp);
@@ -426,7 +406,7 @@
                     U,
                     typename std::enable_if<std::is_integral<T>::value &&
                                             std::is_integral<U>::value>::type> {
-  using result_type = typename UnsignedIntegerForSize<
+  using result_type = typename std::make_unsigned<
       typename MaxExponentPromotion<T, U>::type>::type;
   template <typename V = result_type>
   static bool Do(T x, U y, V* result) {
@@ -445,7 +425,7 @@
                    U,
                    typename std::enable_if<std::is_integral<T>::value &&
                                            std::is_integral<U>::value>::type> {
-  using result_type = typename UnsignedIntegerForSize<
+  using result_type = typename std::make_unsigned<
       typename MaxExponentPromotion<T, U>::type>::type;
   template <typename V = result_type>
   static bool Do(T x, U y, V* result) {
@@ -464,7 +444,7 @@
                     U,
                     typename std::enable_if<std::is_integral<T>::value &&
                                             std::is_integral<U>::value>::type> {
-  using result_type = typename UnsignedIntegerForSize<
+  using result_type = typename std::make_unsigned<
       typename MaxExponentPromotion<T, U>::type>::type;
   template <typename V = result_type>
   static bool Do(T x, U y, V* result) {
@@ -514,99 +494,90 @@
   }
 };
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedNeg(T value, T* result) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  std::is_signed<T>::value>::type* = nullptr>
+bool CheckedNeg(T value, T* result) {
   // The negation of signed min is min, so catch that one.
-  if (value != std::numeric_limits<T>::min()) {
+  if (value != std::numeric_limits<T>::lowest()) {
     *result = static_cast<T>(-value);
     return true;
   }
   return false;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            !std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedNeg(T value, T* result) {
-  if (!value) {  // The only legal unsigned negation is zero.
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  !std::is_signed<T>::value>::type* = nullptr>
+bool CheckedNeg(T value, T* result) {
+  if (!value) {
     *result = static_cast<T>(0);
     return true;
   }
   return false;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            !std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedInv(T value, T* result) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  !std::is_signed<T>::value>::type* = nullptr>
+bool CheckedInv(T value, T* result) {
   *result = ~value;
   return true;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedAbs(T value, T* result) {
-  if (value != std::numeric_limits<T>::min()) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  std::is_signed<T>::value>::type* = nullptr>
+bool CheckedAbs(T value, T* result) {
+  if (value != std::numeric_limits<T>::lowest()) {
     *result = std::abs(value);
     return true;
   }
   return false;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                            !std::numeric_limits<T>::is_signed,
-                        bool>::type
-CheckedAbs(T value, T* result) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  !std::is_signed<T>::value>::type* = nullptr>
+bool CheckedAbs(T value, T* result) {
   // T is unsigned, so |value| must already be positive.
   *result = value;
   return true;
 }
 
-template <typename T>
-constexpr
-    typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                                std::numeric_limits<T>::is_signed,
-                            typename UnsignedIntegerForSize<T>::type>::type
-    SafeUnsignedAbs(T value) {
-  typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
-  return value == std::numeric_limits<T>::min()
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  std::is_signed<T>::value>::type* = nullptr>
+constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
+  using UnsignedT = typename std::make_unsigned<T>::type;
+  return value == std::numeric_limits<T>::lowest()
              ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
              : static_cast<UnsignedT>(std::abs(value));
 }
 
-template <typename T>
-constexpr typename std::enable_if<std::numeric_limits<T>::is_integer &&
-                                      !std::numeric_limits<T>::is_signed,
-                                  T>::type
-SafeUnsignedAbs(T value) {
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value &&
+                                  !std::is_signed<T>::value>::type* = nullptr>
+constexpr T SafeUnsignedAbs(T value) {
   // T is unsigned, so |value| must already be positive.
   return static_cast<T>(value);
 }
 
 // This is just boilerplate that wraps the standard floating point arithmetic.
 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
-#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                               \
-  template <typename T, typename U>                                       \
-  struct Checked##NAME##Op<                                               \
-      T, U,                                                               \
-      typename std::enable_if<std::numeric_limits<T>::is_iec559 ||        \
-                              std::numeric_limits<U>::is_iec559>::type> { \
-    using result_type = typename MaxExponentPromotion<T, U>::type;        \
-    template <typename V>                                                 \
-    static bool Do(T x, U y, V* result) {                                 \
-      using Promotion = typename MaxExponentPromotion<T, U>::type;        \
-      Promotion presult = x OP y;                                         \
-      *result = static_cast<V>(presult);                                  \
-      return IsValueInRangeForNumericType<V>(presult);                    \
-    }                                                                     \
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                                    \
+  template <typename T, typename U>                                            \
+  struct Checked##NAME##Op<                                                    \
+      T, U, typename std::enable_if<std::is_floating_point<T>::value ||        \
+                                    std::is_floating_point<U>::value>::type> { \
+    using result_type = typename MaxExponentPromotion<T, U>::type;             \
+    template <typename V>                                                      \
+    static bool Do(T x, U y, V* result) {                                      \
+      using Promotion = typename MaxExponentPromotion<T, U>::type;             \
+      Promotion presult = x OP y;                                              \
+      *result = static_cast<V>(presult);                                       \
+      return IsValueInRangeForNumericType<V>(presult);                         \
+    }                                                                          \
   };
 
 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
@@ -616,16 +587,18 @@
 
 #undef BASE_FLOAT_ARITHMETIC_OPS
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type
-CheckedNeg(T value, T* result) {
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+bool CheckedNeg(T value, T* result) {
   *result = static_cast<T>(-value);
   return true;
 }
 
-template <typename T>
-typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type
-CheckedAbs(T value, T* result) {
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+bool CheckedAbs(T value, T* result) {
   *result = static_cast<T>(std::abs(value));
   return true;
 }
@@ -642,10 +615,10 @@
 template <typename NumericType>
 struct GetNumericRepresentation {
   static const NumericRepresentation value =
-      std::numeric_limits<NumericType>::is_integer
+      std::is_integral<NumericType>::value
           ? NUMERIC_INTEGER
-          : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING
-                                                         : NUMERIC_UNKNOWN);
+          : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
+                                                        : NUMERIC_UNKNOWN);
 };
 
 template <typename T, NumericRepresentation type =
@@ -672,8 +645,7 @@
   constexpr CheckedNumericState(Src value, bool is_valid)
       : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
         value_(is_valid_ ? static_cast<T>(value) : 0) {
-    static_assert(std::numeric_limits<Src>::is_specialized,
-                  "Argument must be numeric.");
+    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
   }
 
   // Copy constructor.
@@ -683,10 +655,7 @@
         value_(is_valid_ ? static_cast<T>(rhs.value()) : 0) {}
 
   template <typename Src>
-  constexpr explicit CheckedNumericState(
-      Src value,
-      typename std::enable_if<std::numeric_limits<Src>::is_specialized,
-                              int>::type = 0)
+  constexpr explicit CheckedNumericState(Src value)
       : is_valid_(IsValueInRangeForNumericType<T>(value)),
         value_(is_valid_ ? static_cast<T>(value) : 0) {}
 
@@ -707,20 +676,13 @@
   constexpr CheckedNumericState() : value_(0.0) {}
 
   template <typename Src>
-  constexpr CheckedNumericState(
-      Src value,
-      bool is_valid,
-      typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type =
-          0)
+  constexpr CheckedNumericState(Src value, bool is_valid)
       : value_((is_valid && IsValueInRangeForNumericType<T>(value))
                    ? static_cast<T>(value)
                    : std::numeric_limits<T>::quiet_NaN()) {}
 
   template <typename Src>
-  constexpr explicit CheckedNumericState(
-      Src value,
-      typename std::enable_if<std::numeric_limits<Src>::is_specialized,
-                              int>::type = 0)
+  constexpr explicit CheckedNumericState(Src value)
       : value_(static_cast<T>(value)) {}
 
   // Copy constructor.
diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc
index 56b07427..985a3082 100644
--- a/base/numerics/safe_numerics_unittest.cc
+++ b/base/numerics/safe_numerics_unittest.cc
@@ -44,11 +44,11 @@
 using base::saturated_cast;
 using base::strict_cast;
 using base::internal::MaxExponent;
+using base::internal::IntegerBitsPlusSign;
 using base::internal::RANGE_VALID;
 using base::internal::RANGE_INVALID;
 using base::internal::RANGE_OVERFLOW;
 using base::internal::RANGE_UNDERFLOW;
-using base::internal::SignedIntegerForSize;
 
 // These tests deliberately cause arithmetic boundary errors. If the compiler is
 // aggressive enough, it can const detect these errors, so we disable warnings.
@@ -61,8 +61,8 @@
 // wholy represented as the destination floating-point type.
 template <typename Dst, typename Src>
 Dst GetMaxConvertibleToFloat() {
-  typedef numeric_limits<Dst> DstLimits;
-  typedef numeric_limits<Src> SrcLimits;
+  using DstLimits = numeric_limits<Dst>;
+  using SrcLimits = numeric_limits<Src>;
   static_assert(SrcLimits::is_specialized, "Source must be numeric.");
   static_assert(DstLimits::is_specialized, "Destination must be numeric.");
   CHECK(DstLimits::is_iec559);
@@ -139,35 +139,35 @@
     typename std::enable_if<numeric_limits<Dst>::is_integer &&
                                 numeric_limits<Dst>::is_signed,
                             int>::type = 0) {
-  typedef numeric_limits<Dst> DstLimits;
-  TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::min()));
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()).Abs());
+  using DstLimits = numeric_limits<Dst>;
+  TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::lowest()));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
 
   TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + -1);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) +
-                        -DstLimits::max());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
+                        DstLimits::lowest());
 
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1);
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) - -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) - -1);
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
-                        -DstLimits::max());
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) -
+                        DstLimits::lowest());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
                         DstLimits::max());
 
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) * 2);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
 
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) / -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) / -1);
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) * -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * -1);
 
   // Modulus is legal only for integers.
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
   TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2);
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-1) % -2);
-  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
   // Test all the different modulus combinations.
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
@@ -180,18 +180,22 @@
   // Test bit shifts.
   volatile Dst negative_one = -1;
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << negative_one);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << (sizeof(Dst) * CHAR_BIT - 1));
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0) << (sizeof(Dst) * CHAR_BIT));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
+                        << (IntegerBitsPlusSign<Dst>::value - 1));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0)
+                        << IntegerBitsPlusSign<Dst>::value);
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) << 1);
-  TEST_EXPECTED_VALUE(static_cast<Dst>(1) << (sizeof(Dst) * CHAR_BIT - 2),
-                      CheckedNumeric<Dst>(1) << (sizeof(Dst) * CHAR_BIT - 2));
+  TEST_EXPECTED_VALUE(
+      static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2),
+      CheckedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2));
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0)
-                             << (sizeof(Dst) * CHAR_BIT - 1));
+                             << (IntegerBitsPlusSign<Dst>::value - 1));
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) << 0);
   TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) << 1);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> (sizeof(Dst) * CHAR_BIT));
-  TEST_EXPECTED_VALUE(0,
-                      CheckedNumeric<Dst>(1) >> (sizeof(Dst) * CHAR_BIT - 1));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >>
+                        IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_VALUE(
+      0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
 
   TestStrictPointerMath<Dst>();
@@ -205,24 +209,24 @@
     typename std::enable_if<numeric_limits<Dst>::is_integer &&
                                 !numeric_limits<Dst>::is_signed,
                             int>::type = 0) {
-  typedef numeric_limits<Dst> DstLimits;
-  TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min()));
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs());
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1);
-  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2);
+  using DstLimits = numeric_limits<Dst>;
+  TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2);
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).UnsignedAbs());
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).UnsignedAbs());
   TEST_EXPECTED_SUCCESS(
-      CheckedNumeric<typename SignedIntegerForSize<Dst>::type>(
-          std::numeric_limits<typename SignedIntegerForSize<Dst>::type>::min())
+      CheckedNumeric<typename std::make_signed<Dst>::type>(
+          std::numeric_limits<typename std::make_signed<Dst>::type>::lowest())
           .UnsignedAbs());
 
   // Modulus is legal only for integers.
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2);
-  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
   // Test all the different modulus combinations.
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
@@ -232,20 +236,25 @@
   TEST_EXPECTED_VALUE(0, checked_dst %= 1);
   // Test that div by 0 is avoided but returns invalid result.
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) % 0);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << (sizeof(Dst) * CHAR_BIT));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
+                        << IntegerBitsPlusSign<Dst>::value);
   // Test bit shifts.
   volatile int negative_one = -1;
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << negative_one);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << (sizeof(Dst) * CHAR_BIT));
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0) << (sizeof(Dst) * CHAR_BIT));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
+                        << IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0)
+                        << IntegerBitsPlusSign<Dst>::value);
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) << 1);
-  TEST_EXPECTED_VALUE(static_cast<Dst>(1) << (sizeof(Dst) * CHAR_BIT - 1),
-                      CheckedNumeric<Dst>(1) << (sizeof(Dst) * CHAR_BIT - 1));
+  TEST_EXPECTED_VALUE(
+      static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1),
+      CheckedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1));
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) << 0);
   TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) << 1);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> (sizeof(Dst) * CHAR_BIT));
-  TEST_EXPECTED_VALUE(0,
-                      CheckedNumeric<Dst>(1) >> (sizeof(Dst) * CHAR_BIT - 1));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >>
+                        IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_VALUE(
+      0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) & 1);
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) & 0);
@@ -276,23 +285,23 @@
     const char* dst,
     int line,
     typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) {
-  typedef numeric_limits<Dst> DstLimits;
-  TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min()));
+  using DstLimits = numeric_limits<Dst>;
+  TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
 
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs());
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
 
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + -1);
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
   TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + 1);
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) +
-                        -DstLimits::max());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
+                        DstLimits::lowest());
 
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
-                        -DstLimits::max());
-  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) -
+                        DstLimits::lowest());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
                         DstLimits::max());
 
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) * 2);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
 
   TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2);
 }
@@ -300,7 +309,7 @@
 // Generic arithmetic tests.
 template <typename Dst>
 static void TestArithmetic(const char* dst, int line) {
-  typedef numeric_limits<Dst> DstLimits;
+  using DstLimits = numeric_limits<Dst>;
 
   EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid());
   EXPECT_EQ(false,
@@ -354,7 +363,7 @@
   TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1));
   if (numeric_limits<Dst>::is_signed)
     TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1));
-  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + 1);
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) + 1);
   TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) +
                         DstLimits::max());
 
@@ -387,8 +396,8 @@
   // Generic division.
   TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1);
   TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
-  TEST_EXPECTED_VALUE(DstLimits::min() / 2,
-                      CheckedNumeric<Dst>(DstLimits::min()) / 2);
+  TEST_EXPECTED_VALUE(DstLimits::lowest() / 2,
+                      CheckedNumeric<Dst>(DstLimits::lowest()) / 2);
   TEST_EXPECTED_VALUE(DstLimits::max() / 2,
                       CheckedNumeric<Dst>(DstLimits::max()) / 2);
 
@@ -438,8 +447,8 @@
 
 template <typename Dst, typename Src>
 void TestStrictComparison() {
-  typedef numeric_limits<Dst> DstLimits;
-  typedef numeric_limits<Src> SrcLimits;
+  using DstLimits = numeric_limits<Dst>;
+  using SrcLimits = numeric_limits<Src>;
   static_assert(StrictNumeric<Src>(SrcLimits::lowest()) < DstLimits::max(), "");
   static_assert(StrictNumeric<Src>(SrcLimits::lowest()) < SrcLimits::max(), "");
   static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) >= DstLimits::max()),
@@ -506,17 +515,18 @@
 template <typename Dst, typename Src>
 struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
   static void Test(const char *dst, const char *src, int line) {
-    typedef numeric_limits<Src> SrcLimits;
-    typedef numeric_limits<Dst> DstLimits;
-                   // Integral to floating.
+    using SrcLimits = numeric_limits<Src>;
+    using DstLimits = numeric_limits<Dst>;
+    // Integral to floating.
     static_assert((DstLimits::is_iec559 && SrcLimits::is_integer) ||
-                  // Not floating to integral and...
-                  (!(DstLimits::is_integer && SrcLimits::is_iec559) &&
-                   // Same sign, same numeric, source is narrower or same.
-                   ((SrcLimits::is_signed == DstLimits::is_signed &&
-                    sizeof(Dst) >= sizeof(Src)) ||
-                   // Or signed destination and source is smaller
-                    (DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))),
+                      // Not floating to integral and...
+                      (!(DstLimits::is_integer && SrcLimits::is_iec559) &&
+                       // Same sign, same numeric, source is narrower or same.
+                       ((SrcLimits::is_signed == DstLimits::is_signed &&
+                         MaxExponent<Dst>::value >= MaxExponent<Src>::value) ||
+                        // Or signed destination and source is smaller
+                        (DstLimits::is_signed &&
+                         MaxExponent<Dst>::value >= MaxExponent<Src>::value))),
                   "Comparison must be sign preserving and value preserving");
 
     TestStrictComparison<Dst, Src>();
@@ -545,7 +555,7 @@
       TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
     } else if (numeric_limits<Src>::is_signed) {
       TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
-      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
+      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
     }
   }
 };
@@ -553,12 +563,11 @@
 template <typename Dst, typename Src>
 struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
   static void Test(const char *dst, const char *src, int line) {
-    typedef numeric_limits<Src> SrcLimits;
-    typedef numeric_limits<Dst> DstLimits;
+    using SrcLimits = numeric_limits<Src>;
+    using DstLimits = numeric_limits<Dst>;
     static_assert(SrcLimits::is_signed == DstLimits::is_signed,
                   "Destination and source sign must be the same");
-    static_assert(sizeof(Dst) < sizeof(Src) ||
-                   (DstLimits::is_integer && SrcLimits::is_iec559),
+    static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
                   "Destination must be narrower than source");
 
     TestStrictComparison<Dst, Src>();
@@ -586,15 +595,15 @@
         TEST_EXPECTED_RANGE(
             RANGE_VALID,
             static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
-        TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min()));
+        TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::lowest()));
       }
     } else if (SrcLimits::is_signed) {
       TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1));
-      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
       TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
     } else {
       TEST_EXPECTED_FAILURE(checked_dst - static_cast<Src>(1));
-      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
+      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
     }
   }
 };
@@ -602,9 +611,9 @@
 template <typename Dst, typename Src>
 struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> {
   static void Test(const char *dst, const char *src, int line) {
-    typedef numeric_limits<Src> SrcLimits;
-    typedef numeric_limits<Dst> DstLimits;
-    static_assert(sizeof(Dst) >= sizeof(Src),
+    using SrcLimits = numeric_limits<Src>;
+    using DstLimits = numeric_limits<Dst>;
+    static_assert(MaxExponent<Dst>::value >= MaxExponent<Src>::value,
                   "Destination must be equal or wider than source.");
     static_assert(SrcLimits::is_signed, "Source must be signed");
     static_assert(!DstLimits::is_signed, "Destination must be unsigned");
@@ -614,9 +623,9 @@
     const CheckedNumeric<Dst> checked_dst;
     TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max());
     TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
-    TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max());
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
 
-    TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
+    TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
     TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
     TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
     TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
@@ -626,10 +635,9 @@
 template <typename Dst, typename Src>
 struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
   static void Test(const char *dst, const char *src, int line) {
-    typedef numeric_limits<Src> SrcLimits;
-    typedef numeric_limits<Dst> DstLimits;
-    static_assert((DstLimits::is_integer && SrcLimits::is_iec559) ||
-                   (sizeof(Dst) < sizeof(Src)),
+    using SrcLimits = numeric_limits<Src>;
+    using DstLimits = numeric_limits<Dst>;
+    static_assert(MaxExponent<Dst>::value < MaxExponent<Src>::value,
                   "Destination must be narrower than source.");
     static_assert(SrcLimits::is_signed, "Source must be signed.");
     static_assert(!DstLimits::is_signed, "Destination must be unsigned.");
@@ -640,7 +648,7 @@
     TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
     TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
     TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
-    TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max());
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
 
     TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
     TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
@@ -660,10 +668,10 @@
         TEST_EXPECTED_RANGE(
             RANGE_VALID,
             static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
-        TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min()));
+        TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::lowest()));
       }
     } else {
-      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
     }
   }
 };
@@ -671,9 +679,9 @@
 template <typename Dst, typename Src>
 struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> {
   static void Test(const char *dst, const char *src, int line) {
-    typedef numeric_limits<Src> SrcLimits;
-    typedef numeric_limits<Dst> DstLimits;
-    static_assert(sizeof(Dst) <= sizeof(Src),
+    using SrcLimits = numeric_limits<Src>;
+    using DstLimits = numeric_limits<Dst>;
+    static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
                   "Destination must be narrower or equal to source.");
     static_assert(!SrcLimits::is_signed, "Source must be unsigned.");
     static_assert(DstLimits::is_signed, "Destination must be signed.");
@@ -683,9 +691,9 @@
     const CheckedNumeric<Dst> checked_dst;
     TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
     TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
-    TEST_EXPECTED_VALUE(SrcLimits::min(), checked_dst + SrcLimits::min());
+    TEST_EXPECTED_VALUE(SrcLimits::lowest(), checked_dst + SrcLimits::lowest());
 
-    TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
+    TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
     TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
     TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
   }
@@ -818,7 +826,7 @@
   double double_large = numeric_limits<double>::max();
   double double_infinity = numeric_limits<float>::infinity();
   double double_large_int = numeric_limits<int>::max();
-  double double_small_int = numeric_limits<int>::min();
+  double double_small_int = numeric_limits<int>::lowest();
 
   // Just test that the casts compile, since the other tests cover logic.
   EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0)));
@@ -834,9 +842,9 @@
   EXPECT_FALSE(CheckedNumeric<unsigned>(StrictNumeric<int>(-1)).IsValid());
 
   EXPECT_TRUE(IsValueNegative(-1));
-  EXPECT_TRUE(IsValueNegative(numeric_limits<int>::min()));
-  EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::min()));
-  EXPECT_TRUE(IsValueNegative(-numeric_limits<double>::max()));
+  EXPECT_TRUE(IsValueNegative(numeric_limits<int>::lowest()));
+  EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::lowest()));
+  EXPECT_TRUE(IsValueNegative(numeric_limits<double>::lowest()));
   EXPECT_FALSE(IsValueNegative(0));
   EXPECT_FALSE(IsValueNegative(1));
   EXPECT_FALSE(IsValueNegative(0u));
@@ -863,7 +871,8 @@
   EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max());
   EXPECT_EQ(saturated_cast<float>(double_large), double_infinity);
   EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity);
-  EXPECT_EQ(numeric_limits<int>::min(), saturated_cast<int>(double_small_int));
+  EXPECT_EQ(numeric_limits<int>::lowest(),
+            saturated_cast<int>(double_small_int));
   EXPECT_EQ(numeric_limits<int>::max(), saturated_cast<int>(double_large_int));
 
   float not_a_number = std::numeric_limits<float>::infinity() -
@@ -872,7 +881,7 @@
   EXPECT_EQ(0, saturated_cast<int>(not_a_number));
 
   // Test the CheckedNumeric value extractions functions.
-  auto int8_min = MakeCheckedNum(numeric_limits<int8_t>::min());
+  auto int8_min = MakeCheckedNum(numeric_limits<int8_t>::lowest());
   auto int8_max = MakeCheckedNum(numeric_limits<int8_t>::max());
   auto double_max = MakeCheckedNum(numeric_limits<double>::max());
   static_assert(
@@ -885,7 +894,7 @@
       "ValueOrDefault returning incorrect type.");
   EXPECT_FALSE(IsValidForType<uint8_t>(int8_min));
   EXPECT_TRUE(IsValidForType<uint8_t>(int8_max));
-  EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::min()),
+  EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::lowest()),
             ValueOrDieForType<int>(int8_min));
   EXPECT_TRUE(IsValidForType<uint32_t>(int8_max));
   EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::max()),
@@ -900,7 +909,7 @@
   EXPECT_TRUE(int8_max.AssignIfValid(&int16_dest));
   EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::max()), int16_dest);
   EXPECT_TRUE(int8_min.AssignIfValid(&int16_dest));
-  EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::min()), int16_dest);
+  EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::lowest()), int16_dest);
   EXPECT_FALSE(double_max.AssignIfValid(&uint8_dest));
   EXPECT_FALSE(double_max.AssignIfValid(&int16_dest));
   EXPECT_TRUE(double_max.AssignIfValid(&double_dest));
@@ -929,9 +938,9 @@
   EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000000)));
   EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000001)));
   EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(
-      std::numeric_limits<int32_t>::min()));
+      std::numeric_limits<int32_t>::lowest()));
   EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(
-      std::numeric_limits<int64_t>::min()));
+      std::numeric_limits<int64_t>::lowest()));
 
   EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0));
   EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(1));
@@ -945,13 +954,13 @@
   EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0xffffffff)));
   EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x100000000)));
   EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
-      std::numeric_limits<int32_t>::min()));
+      std::numeric_limits<int32_t>::lowest()));
   EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
-      static_cast<int64_t>(std::numeric_limits<int32_t>::min())));
+      static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
   EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
-      static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1));
+      static_cast<int64_t>(std::numeric_limits<int32_t>::lowest()) - 1));
   EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
-      std::numeric_limits<int64_t>::min()));
+      std::numeric_limits<int64_t>::lowest()));
 
   EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0));
   EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(1));
@@ -962,10 +971,10 @@
   EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000000)));
   EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000001)));
   EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(
-      std::numeric_limits<int32_t>::min()));
+      std::numeric_limits<int32_t>::lowest()));
   EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(INT64_C(-1)));
   EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(
-      std::numeric_limits<int64_t>::min()));
+      std::numeric_limits<int64_t>::lowest()));
 
   EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0));
   EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(1));
@@ -987,11 +996,11 @@
   EXPECT_FALSE(
       IsValueInRangeForNumericType<int64_t>(UINT64_C(0xffffffffffffffff)));
   EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
-      std::numeric_limits<int32_t>::min()));
+      std::numeric_limits<int32_t>::lowest()));
   EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
-      static_cast<int64_t>(std::numeric_limits<int32_t>::min())));
+      static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
   EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
-      std::numeric_limits<int64_t>::min()));
+      std::numeric_limits<int64_t>::lowest()));
 }
 
 TEST(SafeNumerics, CompoundNumericOperations) {
diff --git a/chrome/DEPS b/chrome/DEPS
index bdec65e..ee44536 100644
--- a/chrome/DEPS
+++ b/chrome/DEPS
@@ -33,8 +33,6 @@
   "-webkit",
   "-tools",
 
-  "-crypto/third_party",
-
   # Allow inclusion of WebKit API files.
   "+third_party/WebKit/public/platform",
   "+third_party/WebKit/public/public_features.h",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index ea42a47c..52ab333f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -329,9 +329,7 @@
      * @return The height of the Overlay Panel Content View in pixels.
      */
     public int getContentViewHeightPx() {
-        float barExpandedHeight = isFullWidthSizePanel()
-                ? getToolbarHeight() : mBarHeightPeeking;
-        return Math.round((mMaximumHeight - barExpandedHeight) / mPxToDp);
+        return Math.round((mMaximumHeight - getToolbarHeight()) / mPxToDp);
     }
 
     // ============================================================================================
@@ -686,11 +684,7 @@
      * @return The maximized height of the panel in dps.
      */
     protected float getMaximizedHeight() {
-        if (isFullWidthSizePanel()) {
-            return getTabHeight();
-        } else {
-            return getTabHeight() - mToolbarHeight;
-        }
+        return getTabHeight();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index b854b8f..aa2fab2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -32,7 +32,6 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -118,10 +117,10 @@
 
             // We use our own implementation of the dismissal animation, so we don't call the
             // parent implementation. (by default it changes the translation-X and elevation)
-            mRecyclerView.updateViewStateForDismiss(dX, viewHolder);
+            mRecyclerView.updateViewStateForDismiss(dX, (NewTabPageViewHolder) viewHolder);
 
             // If there is another item that should be animated at the same time, do the same to it.
-            ViewHolder siblingViewHolder = getDismissSibling(viewHolder);
+            NewTabPageViewHolder siblingViewHolder = getDismissSibling(viewHolder);
             if (siblingViewHolder != null) {
                 mRecyclerView.updateViewStateForDismiss(dX, siblingViewHolder);
             }
@@ -388,26 +387,14 @@
         return RecyclerView.NO_POSITION;
     }
 
-    public int getSignInPromoPosition() {
-        return getChildPositionOffset(mSigninPromo);
-    }
-
-    public int getBottomSpacerPosition() {
+    int getBottomSpacerPosition() {
         return getChildPositionOffset(mBottomSpacer);
     }
 
-    public int getLastContentItemPosition() {
+    int getLastContentItemPosition() {
         return getChildPositionOffset(hasAllBeenDismissed() ? mAllDismissed : mFooter);
     }
 
-    public int getSuggestionPosition(SnippetArticle article) {
-        for (int i = 0; i < mRoot.getItemCount(); i++) {
-            SnippetArticle articleToCheck = mRoot.getSuggestionAt(i);
-            if (articleToCheck != null && articleToCheck.equals(article)) return i;
-        }
-        return RecyclerView.NO_POSITION;
-    }
-
     private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions,
             @CategoryStatusEnum int status) {
         // Count the number of suggestions before this category.
@@ -560,24 +547,16 @@
     /**
      * Returns another view holder that should be dismissed at the same time as the provided one.
      */
-    public ViewHolder getDismissSibling(ViewHolder viewHolder) {
+    public NewTabPageViewHolder getDismissSibling(ViewHolder viewHolder) {
         int swipePos = viewHolder.getAdapterPosition();
         int siblingPosDelta = mRoot.getDismissSiblingPosDelta(swipePos);
         if (siblingPosDelta == 0) return null;
 
-        return mRecyclerView.findViewHolderForAdapterPosition(siblingPosDelta + swipePos);
+        return (NewTabPageViewHolder) mRecyclerView.findViewHolderForAdapterPosition(
+                siblingPosDelta + swipePos);
     }
 
-    /**
-     * @return The info associated to the provided category.
-     * @throws NullPointerException if {@code category} isn't currently registered with the adapter.
-     * */
-    public SuggestionsCategoryInfo getCategoryInfo(@CategoryInt int category) {
-        return mSections.get(category).getCategoryInfo();
-    }
-
-    @VisibleForTesting
-    public boolean hasAllBeenDismissed() {
+    private boolean hasAllBeenDismissed() {
         return mSections.isEmpty() && !mSigninPromo.isVisible();
     }
 
@@ -601,20 +580,13 @@
      * @return Returns the {@link SuggestionsSection} that contains the item at
      *     {@code itemPosition}, or null if the item is not part of one.
      */
-    @VisibleForTesting
-    SuggestionsSection getSuggestionsSection(int itemPosition) {
+    private SuggestionsSection getSuggestionsSection(int itemPosition) {
         TreeNode child = mRoot.getChildForPosition(itemPosition);
         if (!(child instanceof SuggestionsSection)) return null;
         return (SuggestionsSection) child;
     }
 
-    @VisibleForTesting
-    List<TreeNode> getChildren() {
-        return Collections.unmodifiableList(mChildren);
-    }
-
-    @VisibleForTesting
-    int getChildPositionOffset(TreeNode child) {
+    private int getChildPositionOffset(TreeNode child) {
         return mRoot.getStartingOffsetForChild(child);
     }
 
@@ -632,12 +604,20 @@
         return RecyclerView.NO_POSITION;
     }
 
-    private void announceItemRemoved(String suggestionTitle) {
+    SuggestionsSection getSectionForTesting(@CategoryInt int category) {
+        return mSections.get(category);
+    }
+
+    InnerNode getRootForTesting() {
+        return mRoot;
+    }
+
+    private void announceItemRemoved(String itemTitle) {
         // In tests the RecyclerView can be null.
         if (mRecyclerView == null) return;
 
         mRecyclerView.announceForAccessibility(mRecyclerView.getResources().getString(
-                R.string.ntp_accessibility_item_removed, suggestionTitle));
+                R.string.ntp_accessibility_item_removed, itemTitle));
     }
 
     private void announceItemRemoved(@StringRes int stringToAnnounce) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
index 7501092..6ebfa32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
@@ -185,7 +185,7 @@
      * Updates the space added at the end of the list to make sure the above/below the fold
      * distinction can be preserved.
      */
-    public void refreshBottomSpacing() {
+    private void refreshBottomSpacing() {
         ViewHolder bottomSpacingViewHolder = findBottomSpacer();
 
         // It might not be in the layout yet if it's not visible or ready to be displayed.
@@ -536,8 +536,8 @@
      * @param dX The amount of horizontal displacement caused by user's action.
      * @param viewHolder The view holder containing the view to be updated.
      */
-    public void updateViewStateForDismiss(float dX, ViewHolder viewHolder) {
-        if (!((NewTabPageViewHolder) viewHolder).isDismissable()) return;
+    public void updateViewStateForDismiss(float dX, NewTabPageViewHolder viewHolder) {
+        if (!viewHolder.isDismissable()) return;
 
         viewHolder.itemView.setTranslationX(dX);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index 55c26687..4e952c4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -50,7 +50,7 @@
         mOfflinePageBridge = offlinePageBridge;
 
         mHeader = new SectionHeader(info.getTitle());
-        mSuggestionsList = new SuggestionsList(this);
+        mSuggestionsList = new SuggestionsList(this, info);
         mStatus = StatusItem.createNoSuggestionsItem(this);
         mMoreButton = new ActionItem(this);
         mProgressIndicator = new ProgressItem(this);
@@ -61,9 +61,11 @@
 
     private static class SuggestionsList extends ChildNode implements Iterable<SnippetArticle> {
         private final List<SnippetArticle> mSuggestions = new ArrayList<>();
+        private final SuggestionsCategoryInfo mCategoryInfo;
 
-        public SuggestionsList(NodeParent parent) {
+        public SuggestionsList(NodeParent parent, SuggestionsCategoryInfo categoryInfo) {
             super(parent);
+            mCategoryInfo = categoryInfo;
         }
 
         @Override
@@ -80,7 +82,8 @@
         @Override
         public void onBindViewHolder(NewTabPageViewHolder holder, int position) {
             assert holder instanceof SnippetArticleViewHolder;
-            ((SnippetArticleViewHolder) holder).onBindViewHolder(getSuggestionAt(position));
+            ((SnippetArticleViewHolder) holder)
+                    .onBindViewHolder(getSuggestionAt(position), mCategoryInfo);
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
index 5109920..3eb3a43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -66,6 +66,7 @@
 
     private FetchImageCallback mImageCallback;
     private SnippetArticle mArticle;
+    private SuggestionsCategoryInfo mCategoryInfo;
     private int mPublisherFaviconSizePx;
 
     private final boolean mUseFaviconService;
@@ -149,10 +150,9 @@
      * Updates the layout taking into account screen dimensions and the type of snippet displayed.
      */
     private void updateLayout() {
-        SuggestionsCategoryInfo info =
-                mRecyclerView.getNewTabPageAdapter().getCategoryInfo(mArticle.mCategory);
         boolean narrow = mUiConfig.getCurrentDisplayStyle() == UiConfig.DISPLAY_STYLE_NARROW;
-        boolean minimal = info.getCardLayout() == ContentSuggestionsCardLayout.MINIMAL_CARD;
+        boolean minimal =
+                mCategoryInfo.getCardLayout() == ContentSuggestionsCardLayout.MINIMAL_CARD;
 
         // If the screen is narrow or we are using the minimal layout, hide the article snippet.
         boolean hideSnippet = narrow || minimal;
@@ -185,13 +185,14 @@
         mPublisherBar.setLayoutParams(params);
     }
 
-    public void onBindViewHolder(SnippetArticle article) {
+    public void onBindViewHolder(SnippetArticle article, SuggestionsCategoryInfo categoryInfo) {
         super.onBindViewHolder();
 
         // No longer listen for offline status changes to the old article.
         if (mArticle != null) mArticle.setOfflineStatusChangeRunnable(null);
 
         mArticle = article;
+        mCategoryInfo = categoryInfo;
         updateLayout();
 
         mHeadlineTextView.setText(mArticle.mTitle);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
index 2c5ecad..fba2acfc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -117,7 +117,7 @@
 
         // Scroll the last suggestion into view and click it.
         SnippetArticle suggestion = suggestions.get(suggestions.size() - 1);
-        int suggestionPosition = getAdapter().getSuggestionPosition(suggestion);
+        int suggestionPosition = getSuggestionPosition(suggestion);
         scrollToPosition(suggestionPosition);
         final View suggestionView = waitForView(suggestionPosition);
         ChromeTabUtils.waitForTabPageLoaded(mTab, new Runnable() {
@@ -135,12 +135,13 @@
     public void testAllDismissed() throws InterruptedException, TimeoutException {
         setSuggestionsAndWaitForUpdate(3);
         assertEquals(3, mSource.getSuggestionsForCategory(KnownCategories.ARTICLES).size());
-        assertFalse(getAdapter().hasAllBeenDismissed());
+        assertEquals(RecyclerView.NO_POSITION,
+                getAdapter().getFirstPositionForType(ItemViewType.ALL_DISMISSED));
         assertEquals(1, mSource.getCategories().length);
         assertEquals(KnownCategories.ARTICLES, mSource.getCategories()[0]);
 
         // Dismiss the sign in promo.
-        int signinPromoPosition = getAdapter().getSignInPromoPosition();
+        int signinPromoPosition = getAdapter().getFirstPositionForType(ItemViewType.PROMO);
         scrollToPosition(signinPromoPosition);
         View signinPromoView = waitForView(signinPromoPosition);
         getAdapter().dismissItem(signinPromoPosition);
@@ -155,11 +156,11 @@
             waitForViewToDetach(cardView);
             cardPosition = getAdapter().getFirstCardPosition();
         }
-        assertTrue(getAdapter().hasAllBeenDismissed());
         assertEquals(0, mSource.getCategories().length);
 
         // Click the refresh button on the all dismissed item.
-        int allDismissedPosition = getAdapter().getLastContentItemPosition();
+        int allDismissedPosition = getAdapter().getFirstPositionForType(ItemViewType.ALL_DISMISSED);
+        assertTrue(allDismissedPosition != RecyclerView.NO_POSITION);
         scrollToPosition(allDismissedPosition);
         View allDismissedView = waitForView(allDismissedPosition);
         singleClickView(allDismissedView.findViewById(R.id.action_button));
@@ -178,8 +179,7 @@
         assertEquals(10, suggestions.size());
 
         // Scroll a suggestion into view.
-        int suggestionPosition =
-                getAdapter().getSuggestionPosition(suggestions.get(suggestions.size() - 1));
+        int suggestionPosition = getSuggestionPosition(suggestions.get(suggestions.size() - 1));
         scrollToPosition(suggestionPosition);
         View suggestionView = waitForView(suggestionPosition);
 
@@ -246,6 +246,15 @@
         return getRecyclerView().getNewTabPageAdapter();
     }
 
+    private int getSuggestionPosition(SnippetArticle article) {
+        NewTabPageAdapter adapter = getAdapter();
+        for (int i = 0; i < adapter.getItemCount(); i++) {
+            SnippetArticle articleToCheck = adapter.getSuggestionAt(i);
+            if (articleToCheck != null && articleToCheck.equals(article)) return i;
+        }
+        return RecyclerView.NO_POSITION;
+    }
+
     private void scrollToPosition(final int position) {
         final NewTabPageRecyclerView recyclerView = getRecyclerView();
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index fc6b73b..6ada280c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -6,10 +6,10 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -74,10 +74,12 @@
 
     private FakeSuggestionsSource mSource;
     private NewTabPageAdapter mAdapter;
+    @Mock
     private SigninManager mMockSigninManager;
     @Mock
     private OfflinePageBridge mOfflinePageBridge;
-    @Mock private NewTabPageManager mNewTabPageManager;
+    @Mock
+    private NewTabPageManager mNewTabPageManager;
 
     /**
      * Stores information about a section that should be present in the adapter.
@@ -109,19 +111,19 @@
      * expressed as a sequence of calls to the {@link #expect} methods.
      */
     private static class ItemsMatcher { // TODO(pke): Find better name.
-        private final NewTabPageAdapter mAdapter;
+        private final TreeNode mTreeNode;
         private int mCurrentIndex;
 
-        public ItemsMatcher(NewTabPageAdapter adapter, int startingIndex) {
-            mAdapter = adapter;
-            mCurrentIndex = startingIndex;
+        public ItemsMatcher(TreeNode root) {
+            mTreeNode = root;
         }
 
         public void expect(@ItemViewType int expectedItemType) {
-            if (mCurrentIndex >= mAdapter.getItemCount()) {
+            if (mCurrentIndex >= mTreeNode.getItemCount()) {
                 fail("Expected item of type " + expectedItemType + " but encountered end of list");
             }
-            @ItemViewType int itemType = mAdapter.getItemViewType(mCurrentIndex);
+            @ItemViewType
+            int itemType = mTreeNode.getItemViewType(mCurrentIndex);
             assertEquals("Type mismatch at position " + mCurrentIndex, expectedItemType, itemType);
             mCurrentIndex++;
         }
@@ -146,65 +148,11 @@
             }
         }
 
-        public void expectPosition(int expectedPosition) {
-            assertEquals(expectedPosition, mCurrentIndex);
+        public void expectEnd() {
+            assertEquals(mTreeNode.getItemCount(), mCurrentIndex);
         }
     }
 
-    /**
-     * Asserts that the given {@link TreeNode} is a {@link SuggestionsSection} that matches the
-     * given {@link SectionDescriptor}.
-     * @param descriptor The section descriptor to match against.
-     * @param node The node from the adapter.
-     */
-    private void assertMatches(SectionDescriptor descriptor, TreeNode node) {
-        int offset = mAdapter.getChildPositionOffset(node);
-        ItemsMatcher matcher = new ItemsMatcher(mAdapter, offset);
-        matcher.expect(descriptor);
-        matcher.expectPosition(offset + node.getItemCount());
-    }
-
-    /**
-     * Asserts that {@link #mAdapter}.{@link NewTabPageAdapter#getItemCount()} corresponds to an
-     * NTP with the given sections in it.
-     * @param descriptors A list of descriptors, each describing a section that should be present on
-     *                    the UI.
-     */
-    private void assertItemsFor(SectionDescriptor... descriptors) {
-        ItemsMatcher matcher = new ItemsMatcher(mAdapter, 0);
-        matcher.expect(ItemViewType.ABOVE_THE_FOLD);
-        for (SectionDescriptor descriptor : descriptors) matcher.expect(descriptor);
-        if (descriptors.length == 0) {
-            matcher.expect(ItemViewType.ALL_DISMISSED);
-        } else {
-            matcher.expect(ItemViewType.FOOTER);
-        }
-        matcher.expect(ItemViewType.SPACING);
-        matcher.expectPosition(mAdapter.getItemCount());
-    }
-
-    /**
-     * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a section with
-     * {@code numSuggestions} cards in it.
-     * @param numSuggestions The number of suggestions in the section. If there are zero, use either
-     *                       no section at all (if it is not displayed) or
-     *                       {@link #sectionWithStatusCard()}.
-     * @return A descriptor for the section.
-     */
-    private SectionDescriptor section(int numSuggestions) {
-        assert numSuggestions > 0;
-        return new SectionDescriptor(numSuggestions);
-    }
-
-    /**
-     * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a section that has no
-     * suggestions, but a status card to be displayed.
-     * @return A descriptor for the section.
-     */
-    private SectionDescriptor sectionWithStatusCard() {
-        return new SectionDescriptor(0);
-    }
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -212,7 +160,6 @@
         // Initialise the sign in state. We will be signed in by default in the tests.
         assertFalse(ChromePreferenceManager.getInstance(RuntimeEnvironment.application)
                             .getNewTabPageSigninPromoDismissed());
-        mMockSigninManager = mock(SigninManager.class);
         SigninManager.setInstanceForTesting(mMockSigninManager);
         when(mMockSigninManager.isSignedInOnNative()).thenReturn(true);
         when(mMockSigninManager.isSignInAllowed()).thenReturn(true);
@@ -230,7 +177,7 @@
         when(mNewTabPageManager.getSuggestionsSource()).thenReturn(mSource);
         when(mNewTabPageManager.isCurrentPage()).thenReturn(true);
 
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
     }
 
     @After
@@ -361,8 +308,7 @@
     @Test
     @Feature({"Ntp"})
     public void testProgressIndicatorDisplay() {
-        int progressPos = mAdapter.getFirstPositionForType(ItemViewType.FOOTER) - 1;
-        SuggestionsSection section = mAdapter.getSuggestionsSection(progressPos);
+        SuggestionsSection section = mAdapter.getSectionForTesting(KnownCategories.ARTICLES);
         ProgressItem progress = section.getProgressItemForTesting();
 
         mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.INITIALIZING);
@@ -395,20 +341,19 @@
         assertItemsFor();
 
         // Same when loading a new NTP.
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor();
 
         // Same for CATEGORY_EXPLICITLY_DISABLED.
         mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, snippets);
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor(section(5));
         mSource.setStatusForCategory(
                 KnownCategories.ARTICLES, CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
         assertItemsFor();
 
-        // Same when loading a new NTP.
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor();
     }
 
@@ -428,8 +373,7 @@
         mSource.silentlyRemoveCategory(KnownCategories.ARTICLES);
         assertItemsFor(section(4));
 
-        // But it disappears when loading a new NTP.
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor();
     }
 
@@ -439,31 +383,27 @@
     public void testSectionVisibleIfEmpty() {
         @CategoryInt
         final int category = 42;
-        final int sectionIdx = 1; // section 0 is the above-the-fold item, we test the one after.
-        final List<SnippetArticle> articles =
-                Collections.unmodifiableList(createDummySuggestions(3));
-        FakeSuggestionsSource suggestionsSource;
 
         // Part 1: VisibleIfEmpty = true
-        suggestionsSource = new FakeSuggestionsSource();
+        FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource();
         suggestionsSource.setStatusForCategory(category, CategoryStatus.INITIALIZING);
         suggestionsSource.setInfoForCategory(category,
                 new CategoryInfoBuilder(category).showIfEmpty().build());
 
         // 1.1 - Initial state
         when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor(sectionWithStatusCard().withProgress());
 
         // 1.2 - With suggestions
+        List<SnippetArticle> articles = Collections.unmodifiableList(createDummySuggestions(3));
         suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABLE);
         suggestionsSource.setSuggestionsForCategory(category, articles);
         assertItemsFor(section(3));
 
         // 1.3 - When all suggestions are dismissed
-        assertEquals(SuggestionsSection.class, mAdapter.getChildren().get(sectionIdx).getClass());
-        SuggestionsSection section42 = (SuggestionsSection) mAdapter.getChildren().get(sectionIdx);
-        assertMatches(section(3), section42);
+        SuggestionsSection section42 = mAdapter.getSectionForTesting(category);
+        assertSectionMatches(section(3), section42);
         section42.removeSuggestion(articles.get(0));
         section42.removeSuggestion(articles.get(1));
         section42.removeSuggestion(articles.get(2));
@@ -476,7 +416,7 @@
 
         // 2.1 - Initial state
         when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor();
 
         // 2.2 - With suggestions
@@ -495,14 +435,9 @@
     public void testMoreButton() {
         @CategoryInt
         final int category = 42;
-        final int sectionIdx = 1; // section 0 is the above the fold, we test the one after.
-        final List<SnippetArticle> articles =
-                Collections.unmodifiableList(createDummySuggestions(3));
-        FakeSuggestionsSource suggestionsSource;
-        SuggestionsSection section42;
 
         // Part 1: With "View All" action
-        suggestionsSource = new FakeSuggestionsSource();
+        FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource();
         suggestionsSource.setStatusForCategory(category, CategoryStatus.INITIALIZING);
         suggestionsSource.setInfoForCategory(category, new CategoryInfoBuilder(category)
                                                                .withViewAllAction()
@@ -511,18 +446,18 @@
 
         // 1.1 - Initial state.
         when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor(sectionWithStatusCard().withActionButton().withProgress());
 
         // 1.2 - With suggestions.
+        List<SnippetArticle> articles = Collections.unmodifiableList(createDummySuggestions(3));
         suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABLE);
         suggestionsSource.setSuggestionsForCategory(category, articles);
         assertItemsFor(section(3).withActionButton());
 
         // 1.3 - When all suggestions are dismissed.
-        assertEquals(SuggestionsSection.class, mAdapter.getChildren().get(sectionIdx).getClass());
-        section42 = (SuggestionsSection) mAdapter.getChildren().get(sectionIdx);
-        assertMatches(section(3).withActionButton(), section42);
+        SuggestionsSection section42 = mAdapter.getSectionForTesting(category);
+        assertSectionMatches(section(3).withActionButton(), section42);
         section42.removeSuggestion(articles.get(0));
         section42.removeSuggestion(articles.get(1));
         section42.removeSuggestion(articles.get(2));
@@ -536,7 +471,7 @@
 
         // 2.1 - Initial state.
         when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor(sectionWithStatusCard().withProgress());
 
         // 2.2 - With suggestions.
@@ -545,22 +480,14 @@
         assertItemsFor(section(3));
 
         // 2.3 - When all suggestions are dismissed.
-        assertEquals(SuggestionsSection.class, mAdapter.getChildren().get(sectionIdx).getClass());
-        section42 = (SuggestionsSection) mAdapter.getChildren().get(sectionIdx);
-        assertMatches(section(3), section42);
+        section42 = mAdapter.getSectionForTesting(category);
+        assertSectionMatches(section(3), section42);
         section42.removeSuggestion(articles.get(0));
         section42.removeSuggestion(articles.get(1));
         section42.removeSuggestion(articles.get(2));
         assertItemsFor(sectionWithStatusCard());
     }
 
-    private void assertArticlesEqual(List<SnippetArticle> articles, int start, int end) {
-        assertThat(mAdapter.getItemCount(), greaterThanOrEqualTo(end));
-        for (int i = start; i < end; i++) {
-            assertEquals(articles.get(i - start), mAdapter.getSuggestionAt(i));
-        }
-    }
-
     /**
      * Tests that invalidated suggestions are immediately removed.
      */
@@ -596,8 +523,7 @@
                                                              .build());
         mSource.setStatusForCategory(dynamicCategory1, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(dynamicCategory1, dynamics1);
-        // Reload
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
 
         assertItemsFor(section(3), section(5).withActionButton());
 
@@ -607,8 +533,7 @@
                 new CategoryInfoBuilder(dynamicCategory1).build());
         mSource.setStatusForCategory(dynamicCategory2, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(dynamicCategory2, dynamics2);
-        // Reload
-        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
         assertItemsFor(section(3), section(5).withActionButton(), section(11));
     }
 
@@ -621,16 +546,14 @@
         // Above-the-fold, sign in promo, all-dismissed, footer, spacer.
         final int basicChildCount = 5;
         FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource();
+        when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
         registerCategory(suggestionsSource, KnownCategories.ARTICLES, 0);
         registerCategory(suggestionsSource, KnownCategories.BOOKMARKS, 0);
         registerCategory(suggestionsSource, KnownCategories.PHYSICAL_WEB_PAGES, 0);
         registerCategory(suggestionsSource, KnownCategories.DOWNLOADS, 0);
+        reloadNtp();
 
-        when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        NewTabPageAdapter ntpAdapter = new NewTabPageAdapter(
-                mNewTabPageManager, null, null, mOfflinePageBridge);
-        List<TreeNode> children = ntpAdapter.getChildren();
-
+        List<TreeNode> children = mAdapter.getRootForTesting().getChildren();
         assertEquals(basicChildCount + 4, children.size());
         assertEquals(AboveTheFoldItem.class, children.get(0).getClass());
         assertEquals(SuggestionsSection.class, children.get(1).getClass());
@@ -644,16 +567,14 @@
 
         // With a different order.
         suggestionsSource = new FakeSuggestionsSource();
+        when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
         registerCategory(suggestionsSource, KnownCategories.ARTICLES, 0);
         registerCategory(suggestionsSource, KnownCategories.PHYSICAL_WEB_PAGES, 0);
         registerCategory(suggestionsSource, KnownCategories.DOWNLOADS, 0);
         registerCategory(suggestionsSource, KnownCategories.BOOKMARKS, 0);
+        reloadNtp();
 
-        when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        ntpAdapter = new NewTabPageAdapter(
-                mNewTabPageManager, null, null, mOfflinePageBridge);
-        children = ntpAdapter.getChildren();
-
+        children = mAdapter.getRootForTesting().getChildren();
         assertEquals(basicChildCount + 4, children.size());
         assertEquals(AboveTheFoldItem.class, children.get(0).getClass());
         assertEquals(SuggestionsSection.class, children.get(1).getClass());
@@ -667,19 +588,17 @@
 
         // With unknown categories.
         suggestionsSource = new FakeSuggestionsSource();
+        when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
         registerCategory(suggestionsSource, KnownCategories.ARTICLES, 0);
         registerCategory(suggestionsSource, KnownCategories.PHYSICAL_WEB_PAGES, 0);
         registerCategory(suggestionsSource, KnownCategories.DOWNLOADS, 0);
-
-        when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        ntpAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+        reloadNtp();
 
         // The adapter is already initialised, it will not accept new categories anymore.
         registerCategory(suggestionsSource, 42, 1);
         registerCategory(suggestionsSource, KnownCategories.BOOKMARKS, 1);
 
-        children = ntpAdapter.getChildren();
-
+        children = mAdapter.getRootForTesting().getChildren();
         assertEquals(basicChildCount + 3, children.size());
         assertEquals(AboveTheFoldItem.class, children.get(0).getClass());
         assertEquals(SuggestionsSection.class, children.get(1).getClass());
@@ -694,16 +613,12 @@
     @Feature({"Ntp"})
     public void testChangeNotifications() {
         FakeSuggestionsSource suggestionsSource = spy(new FakeSuggestionsSource());
-        // Allow using dismissSuggestion() instead of throwing UnsupportedOperationException.
-        doNothing().when(suggestionsSource).dismissSuggestion(any(SnippetArticle.class));
-
         registerCategory(suggestionsSource, KnownCategories.ARTICLES, 3);
         when(mNewTabPageManager.getSuggestionsSource()).thenReturn(suggestionsSource);
-        NewTabPageAdapter adapter = new NewTabPageAdapter(
-                mNewTabPageManager, null, null, mOfflinePageBridge);
+
+        reloadNtp();
         AdapterDataObserver dataObserver = mock(AdapterDataObserver.class);
-        adapter.registerAdapterDataObserver(dataObserver);
-        reset(dataObserver); // reset notification changes from initialisation.
+        mAdapter.registerAdapterDataObserver(dataObserver);
 
         // Adapter content:
         // Idx | Item
@@ -714,24 +629,26 @@
         // 5   | Footer
         // 6   | Spacer
 
-        adapter.dismissItem(3); // Dismiss the second suggestion of the second section.
+        mAdapter.dismissItem(3); // Dismiss the second suggestion of the second section.
         verify(dataObserver).onItemRangeRemoved(3, 1);
         verify(dataObserver).onItemRangeChanged(5, 1, null);
 
         // Make sure the call with the updated position works properly.
-        adapter.dismissItem(3);
+        mAdapter.dismissItem(3);
         verify(dataObserver, times(2)).onItemRangeRemoved(3, 1);
         verify(dataObserver).onItemRangeChanged(4, 1, null);
-        reset(dataObserver);
+        verifyNoMoreInteractions(dataObserver);
 
         // Dismiss the last suggestion in the section. We should now show the status card.
-        adapter.dismissItem(2);
+        reset(dataObserver);
+        mAdapter.dismissItem(2);
         verify(dataObserver).onItemRangeRemoved(2, 1); // Suggestion removed
         verify(dataObserver).onItemRangeChanged(3, 1, null); // Spacer refresh
         verify(dataObserver).onItemRangeInserted(2, 1); // Status card added
         verify(dataObserver).onItemRangeChanged(4, 1, null); // Spacer refresh
         verify(dataObserver).onItemRangeInserted(3, 1); // Action item added
         verify(dataObserver).onItemRangeChanged(5, 1, null); // Spacer refresh
+        verifyNoMoreInteractions(dataObserver);
 
         // Adapter content:
         // Idx | Item
@@ -748,7 +665,7 @@
         reset(dataObserver);
         suggestionsSource.setSuggestionsForCategory(
                 KnownCategories.ARTICLES, createDummySuggestions(newSuggestionCount));
-        adapter.onNewSuggestions(KnownCategories.ARTICLES);
+        mAdapter.onNewSuggestions(KnownCategories.ARTICLES);
         verify(dataObserver).onItemRangeInserted(2, newSuggestionCount);
         verify(dataObserver).onItemRangeChanged(5 + newSuggestionCount, 1, null); // Spacer refresh
         verify(dataObserver, times(2)).onItemRangeRemoved(2 + newSuggestionCount, 1);
@@ -768,13 +685,14 @@
         reset(dataObserver);
         suggestionsSource.setSuggestionsForCategory(
                 KnownCategories.ARTICLES, createDummySuggestions(0));
-        adapter.onCategoryStatusChanged(KnownCategories.ARTICLES, CategoryStatus.SIGNED_OUT);
+        mAdapter.onCategoryStatusChanged(KnownCategories.ARTICLES, CategoryStatus.SIGNED_OUT);
         verify(dataObserver).onItemRangeRemoved(2, newSuggestionCount);
         verify(dataObserver).onItemRangeChanged(3, 1, null); // Spacer refresh
         verify(dataObserver).onItemRangeInserted(2, 1); // Status card added
         verify(dataObserver).onItemRangeChanged(4, 1, null); // Spacer refresh
         verify(dataObserver).onItemRangeInserted(3, 1); // Action item added
         verify(dataObserver).onItemRangeChanged(5, 1, null); // Spacer refresh
+        verifyNoMoreInteractions(dataObserver);
     }
 
     @Test
@@ -787,27 +705,8 @@
 
         doNothing().when(mNewTabPageManager).addDestructionObserver(observers.capture());
 
-        NewTabPageAdapter adapter =
-                new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
-
-        TreeNode signinPromo = adapter.getChildren().get(2);
-
-        // Adapter content:
-        // Idx | Item               | Item Index
-        // ----|--------------------|-------------
-        // 0   | Above-the-fold     | 0
-        // 1   | Header             | 1
-        // 2   | Status             | 1
-        // 3   | Action             | 1
-        // 4   | Progress Indicator | 1
-        // 5   | Sign in promo      | 2
-        // 6   | Footer             | 3
-        // 7   | Spacer             | 4
-
-        assertEquals(1, signinPromo.getItemCount());
-        assertEquals(ItemViewType.PROMO, signinPromo.getItemViewType(0));
-
-        // verify(mNewTabPageManager).addDestructionObserver(observers.capture());
+        reloadNtp();
+        assertTrue(isSignInPromoVisible());
 
         // Note: As currently implemented, these two variables should point to the same object, a
         // SignInPromo.SigninObserver
@@ -823,18 +722,18 @@
         }
 
         signInStateObserver.onSignedIn();
-        assertEquals(0, signinPromo.getItemCount());
+        assertFalse(isSignInPromoVisible());
 
         signInStateObserver.onSignedOut();
-        assertEquals(1, signinPromo.getItemCount());
+        assertTrue(isSignInPromoVisible());
 
         when(mMockSigninManager.isSignInAllowed()).thenReturn(false);
         signInAllowedObserver.onSignInAllowedChanged();
-        assertEquals(0, signinPromo.getItemCount());
+        assertFalse(isSignInPromoVisible());
 
         when(mMockSigninManager.isSignInAllowed()).thenReturn(true);
         signInAllowedObserver.onSignInAllowedChanged();
-        assertEquals(1, signinPromo.getItemCount());
+        assertTrue(isSignInPromoVisible());
     }
 
     @Test
@@ -844,38 +743,18 @@
         when(mMockSigninManager.isSignedInOnNative()).thenReturn(false);
         ChromePreferenceManager.getInstance(RuntimeEnvironment.application)
                 .setNewTabPageSigninPromoDismissed(false);
+        reloadNtp();
 
-        NewTabPageAdapter adapter =
-                new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
-        final int signInPromoIndex = adapter.getFirstPositionForType(ItemViewType.PROMO);
+        final int signInPromoPosition = mAdapter.getFirstPositionForType(ItemViewType.PROMO);
+        assertNotEquals(RecyclerView.NO_POSITION, signInPromoPosition);
+        mAdapter.dismissItem(signInPromoPosition);
 
-        assertEquals(6, adapter.getChildren().size());
-        TreeNode signinPromo = adapter.getChildren().get(2);
-
-        // Adapter content:
-        // Idx | Item               | Item Index
-        // ----|--------------------|-------------
-        // 0   | Above-the-fold     | 0
-        // 1   | Header             | 1
-        // 2   | Status             | 1
-        // 3   | Action             | 1
-        // 4   | Progress Indicator | 1
-        // 5   | Sign in promo      | 2
-        // 6   | All dismissed      | 3
-        // 7   | Footer             | 4
-        // 8   | Spacer             | 5
-
-        assertEquals(ItemViewType.PROMO, signinPromo.getItemViewType(0));
-
-        adapter.dismissItem(signInPromoIndex);
-        assertEquals(0, signinPromo.getItemCount());
+        assertFalse(isSignInPromoVisible());
         assertTrue(ChromePreferenceManager.getInstance(RuntimeEnvironment.application)
                            .getNewTabPageSigninPromoDismissed());
 
-        adapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
-        assertEquals(6, adapter.getChildren().size());
-        // The items below the signin promo move up, footer is now at the position of the promo.
-        assertEquals(ItemViewType.FOOTER, adapter.getItemViewType(signInPromoIndex));
+        reloadNtp();
+        assertFalse(isSignInPromoVisible());
     }
 
     @Test
@@ -987,6 +866,75 @@
                 mAdapter.getFirstPositionForType(ItemViewType.ALL_DISMISSED));
     }
 
+    /**
+     * Asserts that the given {@link TreeNode} is a {@link SuggestionsSection} that matches the
+     * given {@link SectionDescriptor}.
+     * @param descriptor The section descriptor to match against.
+     * @param section The section from the adapter.
+     */
+    private void assertSectionMatches(SectionDescriptor descriptor, SuggestionsSection section) {
+        ItemsMatcher matcher = new ItemsMatcher(section);
+        matcher.expect(descriptor);
+        matcher.expectEnd();
+    }
+
+    /**
+     * Asserts that {@link #mAdapter}.{@link NewTabPageAdapter#getItemCount()} corresponds to an NTP
+     * with the given sections in it.
+     *
+     * @param descriptors A list of descriptors, each describing a section that should be present on
+     *                    the UI.
+     */
+    private void assertItemsFor(SectionDescriptor... descriptors) {
+        ItemsMatcher matcher = new ItemsMatcher(mAdapter.getRootForTesting());
+        matcher.expect(ItemViewType.ABOVE_THE_FOLD);
+        for (SectionDescriptor descriptor : descriptors) matcher.expect(descriptor);
+        if (descriptors.length == 0) {
+            matcher.expect(ItemViewType.ALL_DISMISSED);
+        } else {
+            matcher.expect(ItemViewType.FOOTER);
+        }
+        matcher.expect(ItemViewType.SPACING);
+        matcher.expectEnd();
+    }
+
+    /**
+     * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a section with
+     * {@code numSuggestions} cards in it.
+     * @param numSuggestions The number of suggestions in the section. If there are zero, use either
+     *                       no section at all (if it is not displayed) or
+     *                       {@link #sectionWithStatusCard()}.
+     * @return A descriptor for the section.
+     */
+    private SectionDescriptor section(int numSuggestions) {
+        assert numSuggestions > 0;
+        return new SectionDescriptor(numSuggestions);
+    }
+
+    /**
+     * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a section that has no
+     * suggestions, but a status card to be displayed.
+     * @return A descriptor for the section.
+     */
+    private SectionDescriptor sectionWithStatusCard() {
+        return new SectionDescriptor(0);
+    }
+
+    private void reloadNtp() {
+        mAdapter = new NewTabPageAdapter(mNewTabPageManager, null, null, mOfflinePageBridge);
+    }
+
+    private void assertArticlesEqual(List<SnippetArticle> articles, int start, int end) {
+        assertThat(mAdapter.getItemCount(), greaterThanOrEqualTo(end));
+        for (int i = start; i < end; i++) {
+            assertEquals(articles.get(i - start), mAdapter.getSuggestionAt(i));
+        }
+    }
+
+    private boolean isSignInPromoVisible() {
+        return mAdapter.getFirstPositionForType(ItemViewType.PROMO) != RecyclerView.NO_POSITION;
+    }
+
     /** Registers the category with the reload action */
     private void registerCategory(FakeSuggestionsSource suggestionsSource,
             @CategoryInt int category, int suggestionCount) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetectorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetectorTest.java
index 2fc7f262..a580c472 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetectorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetectorTest.java
@@ -5,8 +5,10 @@
 package org.chromium.chrome.browser.webapps;
 
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.os.Bundle;
 import android.text.TextUtils;
 
 import org.junit.Assert;
@@ -24,6 +26,8 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.common.ScreenOrientationValues;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
+import org.chromium.webapk.test.WebApkTestHelper;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -41,8 +45,8 @@
     private static final String WEBAPK_SHORT_NAME = "Short Name";
     private static final String WEBAPK_BEST_ICON_URL = "/icon.png";
     private static final String WEBAPK_BEST_ICON_MURMUR2_HASH = "3";
-    private static final int WEBAPK_DISPLAY_MODE = WebDisplayMode.Standalone;
-    private static final int WEBAPK_ORIENTATION = ScreenOrientationValues.LANDSCAPE;
+    private static final int WEBAPK_DISPLAY_MODE = WebDisplayMode.Undefined;
+    private static final int WEBAPK_ORIENTATION = ScreenOrientationValues.DEFAULT;
     private static final long WEBAPK_THEME_COLOR = 1L;
     private static final long WEBAPK_BACKGROUND_COLOR = 2L;
     private static final String WEBAPK_MANIFEST_URL = "manifest.json";
@@ -175,43 +179,69 @@
                 WEBAPK_MANIFEST_URL, manifestData.startUrl, manifestData.iconUrlToMurmur2HashMap);
     }
 
-    private static TestManifestUpgradeDetector createDetectorWithFetchedData(
-            ManifestData fetchedData, TestCallback callback) {
-        return createDetector(defaultManifestData(), fetchedData, callback);
+    /**
+     * Checks whether the WebAPK is updated given data from the WebAPK's Android Manifest and data
+     * from the fetched Web Manifest. This function uses the intent version of
+     * {@link WebApkInfo#create()} in order to test default values set by the intent version of
+     * {@link WebApkInfo#create()} and how the defaults affect whether the WebAPK is updated.
+     */
+    private boolean checkUpdateNeededForFetchedManifest(
+            ManifestData androidManifestData, ManifestData fetchedManifestData) {
+        registerWebApk(androidManifestData);
+
+        Intent intent = new Intent();
+        intent.putExtra(ShortcutHelper.EXTRA_URL, "");
+        intent.putExtra(
+                ShortcutHelper.EXTRA_WEBAPK_PACKAGE_NAME, WebApkTestHelper.WEBAPK_PACKAGE_NAME);
+        WebApkInfo androidManifestInfo = WebApkInfo.create(intent);
+
+        TestCallback callback = new TestCallback();
+        TestManifestUpgradeDetector detector =
+                new TestManifestUpgradeDetector(androidManifestInfo, fetchedManifestData, callback);
+        detector.start();
+        Assert.assertTrue(callback.mWasCalled);
+        return callback.mIsUpgraded;
     }
 
     /**
-     * Creates ManifestUpgradeDetector.
-     * @param oldData Data used to create WebAPK. Potentially different from Web Manifest data at
-     *                time that the WebAPK was generated.
-     * @param fetchedData Data fetched by ManifestUpgradeDetector.
-     * @param callback Callback to call when the upgrade check is complete.
+     * Registers WebAPK with default package name. Overwrites previous registrations.
+     * @param manifestData <meta-data> values for WebAPK's Android Manifest.
      */
-    private static TestManifestUpgradeDetector createDetector(
-            ManifestData oldData, ManifestData fetchedData, TestCallback callback) {
-        return new TestManifestUpgradeDetector(
-                infoFromManifestData(oldData), fetchedData, callback);
+    private void registerWebApk(ManifestData manifestData) {
+        Bundle metaData = new Bundle();
+        metaData.putString(WebApkMetaDataKeys.START_URL, manifestData.startUrl);
+        metaData.putString(WebApkMetaDataKeys.SCOPE, manifestData.scopeUrl);
+        metaData.putString(WebApkMetaDataKeys.NAME, manifestData.name);
+        metaData.putString(WebApkMetaDataKeys.SHORT_NAME, manifestData.shortName);
+        metaData.putString(WebApkMetaDataKeys.THEME_COLOR, manifestData.themeColor + "L");
+        metaData.putString(WebApkMetaDataKeys.BACKGROUND_COLOR, manifestData.backgroundColor + "L");
+        metaData.putString(WebApkMetaDataKeys.WEB_MANIFEST_URL, WEBAPK_MANIFEST_URL);
+
+        String iconUrlsAndIconMurmur2Hashes = "";
+        for (String iconUrl : manifestData.iconUrlToMurmur2HashMap.keySet()) {
+            String murmur2Hash = manifestData.iconUrlToMurmur2HashMap.get(iconUrl);
+            if (murmur2Hash == null) {
+                murmur2Hash = "0";
+            }
+            iconUrlsAndIconMurmur2Hashes += " " + iconUrl + " " + murmur2Hash;
+        }
+        iconUrlsAndIconMurmur2Hashes = iconUrlsAndIconMurmur2Hashes.trim();
+        metaData.putString(WebApkMetaDataKeys.ICON_URLS_AND_ICON_MURMUR2_HASHES,
+                iconUrlsAndIconMurmur2Hashes);
+        WebApkTestHelper.registerWebApkWithMetaData(metaData);
     }
 
     @Test
     public void testManifestDoesNotUpgrade() {
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector =
-                createDetectorWithFetchedData(defaultManifestData(), callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertFalse(callback.mIsUpgraded);
+        Assert.assertFalse(
+                checkUpdateNeededForFetchedManifest(defaultManifestData(), defaultManifestData()));
     }
 
     @Test
     public void testStartUrlChangeShouldUpgrade() {
         ManifestData fetchedData = defaultManifestData();
         fetchedData.startUrl = "/changed.html";
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetectorWithFetchedData(fetchedData, callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertTrue(callback.mIsUpgraded);
+        Assert.assertTrue(checkUpdateNeededForFetchedManifest(defaultManifestData(), fetchedData));
     }
 
     /**
@@ -226,12 +256,7 @@
         ManifestData fetchedData = defaultManifestData();
         fetchedData.scopeUrl = "";
         Assert.assertTrue(!oldData.scopeUrl.equals(fetchedData.scopeUrl));
-
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetector(oldData, fetchedData, callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertFalse(callback.mIsUpgraded);
+        Assert.assertFalse(checkUpdateNeededForFetchedManifest(oldData, fetchedData));
     }
 
     /**
@@ -249,11 +274,7 @@
         fetchedData.startUrl = "/fancy/scope/special/snowflake.html";
         fetchedData.scopeUrl = "";
 
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetector(oldData, fetchedData, callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertTrue(callback.mIsUpgraded);
+        Assert.assertTrue(checkUpdateNeededForFetchedManifest(oldData, fetchedData));
     }
 
     /**
@@ -267,12 +288,7 @@
         fetchedData.iconUrlToMurmur2HashMap.put(fetchedData.bestIconUrl,
                 WEBAPK_BEST_ICON_MURMUR2_HASH + "1");
         fetchedData.bestIcon = createBitmap(Color.BLUE);
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetectorWithFetchedData(fetchedData, callback);
-
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertTrue(callback.mIsUpgraded);
+        Assert.assertTrue(checkUpdateNeededForFetchedManifest(defaultManifestData(), fetchedData));
     }
 
     /**
@@ -286,12 +302,7 @@
         fetchedData.iconUrlToMurmur2HashMap.clear();
         fetchedData.iconUrlToMurmur2HashMap.put("/icon2.png", "22");
         fetchedData.bestIconUrl = "/icon2.png";
-
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetectorWithFetchedData(fetchedData, callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertTrue(callback.mIsUpgraded);
+        Assert.assertTrue(checkUpdateNeededForFetchedManifest(defaultManifestData(), fetchedData));
     }
 
     /**
@@ -307,12 +318,7 @@
         fetchedData.iconUrlToMurmur2HashMap.put(
                 WEBAPK_BEST_ICON_URL, WEBAPK_BEST_ICON_MURMUR2_HASH);
         fetchedData.iconUrlToMurmur2HashMap.put("/icon2.png", null);
-
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetectorWithFetchedData(fetchedData, callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertFalse(callback.mIsUpgraded);
+        Assert.assertFalse(checkUpdateNeededForFetchedManifest(defaultManifestData(), fetchedData));
     }
 
     /**
@@ -342,10 +348,6 @@
         fetchedData.iconUrlToMurmur2HashMap.put(iconUrl1, null);
         fetchedData.iconUrlToMurmur2HashMap.put(iconUrl2, hash2);
 
-        TestCallback callback = new TestCallback();
-        TestManifestUpgradeDetector detector = createDetector(oldData, fetchedData, callback);
-        detector.start();
-        Assert.assertTrue(callback.mWasCalled);
-        Assert.assertFalse(callback.mIsUpgraded);
+        Assert.assertFalse(checkUpdateNeededForFetchedManifest(oldData, fetchedData));
     }
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f24005e..1862fa59 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -11249,6 +11249,7 @@
         You're now signed in to Chrome
       </message>
 
+      <!-- "Chrome sync" is the Google Cloud Based service used for sync. Thus this string resource is set to "Chrome sync" even for Chromium builds. -->
       <message name="IDS_SYNC_CONFIRMATION_CHROME_SYNC_TITLE" desc="Title of the chrome sync section of the sync confirmation dialog in the tab modal signin flow" formatter_data="android_java">
         Chrome Sync
       </message>
@@ -15550,6 +15551,14 @@
       Disable video tracks when the video is played in the background to optimize performance.
     </message>
 
+    <!-- Video fullscreen with orientation lock experiment strings. -->
+    <message name="IDS_FLAGS_VIDEO_FULLSCREEN_ORIENTATION_LOCK_NAME" desc="Name of the flag for enabling orientation lock for fullscreen video playback.">
+      Lock screen orientation when playing a video fullscreen.
+    </message>
+    <message name="IDS_FLAGS_VIDEO_FULLSCREEN_ORIENTATION_LOCK_DESCRIPTION" desc="Description of the flag for enabling orientation lock for fullscreen video playback.">
+      Lock the screen orientation of the device to match video orientation when a video goes fullscreen.
+    </message>
+
     <if expr="is_win">
       <message name="IDS_UTILITY_PROCESS_SHELL_HANDLER_NAME" desc="The name of the utility process used to handle shell operations.">
         Shell Handler
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9b90b53d..5ddcfae 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -384,6 +384,8 @@
     "favicon/favicon_utils.h",
     "favicon/large_icon_service_factory.cc",
     "favicon/large_icon_service_factory.h",
+    "features.cc",
+    "features.h",
     "file_select_helper.cc",
     "file_select_helper.h",
     "file_select_helper_mac.mm",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d7725b1..daeeff6 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2103,6 +2103,12 @@
      IDS_FLAGS_MEDIA_REMOTING_ENCRYPTED_DESCRIPTION, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kMediaRemotingEncrypted)},
 #endif
+#if defined(OS_ANDROID)
+    {"video-fullscreen-orientation-lock",
+     IDS_FLAGS_VIDEO_FULLSCREEN_ORIENTATION_LOCK_NAME,
+     IDS_FLAGS_VIDEO_FULLSCREEN_ORIENTATION_LOCK_DESCRIPTION, kOsAndroid,
+     FEATURE_VALUE_TYPE(media::kVideoFullscreenOrientationLock)},
+#endif
 
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
diff --git a/chrome/browser/chrome_browser_field_trials_desktop.cc b/chrome/browser/chrome_browser_field_trials_desktop.cc
index 1901825d0..6ebdc45f 100644
--- a/chrome/browser/chrome_browser_field_trials_desktop.cc
+++ b/chrome/browser/chrome_browser_field_trials_desktop.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
+#include "chrome/browser/features.h"
 #include "chrome/browser/prerender/prerender_field_trial.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -112,6 +113,12 @@
 #if defined(OS_WIN)
   SetupStabilityDebugging();
 #endif  // defined(OS_WIN)
+  // Activate the experiment as early as possible to increase its visibility
+  // (e.g. the likelihood of its presence in the serialized system profile).
+  // This also needs to happen before the browser rendez-vous attempt
+  // (NotifyOtherProcessOrCreate) in PreMainMessageLoopRun so the corresponding
+  // metrics are tagged.
+  base::FeatureList::IsEnabled(features::kDesktopFastShutdown);
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index a9aaea8..1bcc099 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -176,7 +176,9 @@
 #include "chrome/browser/metrics/thread_watcher_android.h"
 #include "ui/base/resource/resource_bundle_android.h"
 #else
+#include "chrome/browser/features.h"
 #include "chrome/browser/feedback/feedback_profile_observer.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -1982,6 +1984,12 @@
       g_browser_process->local_state());
   run_loop.Run();
 
+  if (base::FeatureList::IsEnabled(features::kDesktopFastShutdown)) {
+    // Experiment to determine the impact of always taking the quick exit path.
+    // There is no returning from this call as it terminates the process.
+    chrome::SessionEnding();
+  }
+
   return true;
 #endif  // defined(OS_ANDROID)
 }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 87584d65..aaf9540 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -207,8 +207,6 @@
     "app_mode/kiosk_session_plugin_handler_delegate.h",
     "app_mode/startup_app_launcher.cc",
     "app_mode/startup_app_launcher.h",
-    "arc/arc_auth_code_fetcher.cc",
-    "arc/arc_auth_code_fetcher.h",
     "arc/arc_auth_context.cc",
     "arc/arc_auth_context.h",
     "arc/arc_auth_service.cc",
@@ -221,8 +219,13 @@
     "arc/arc_session_manager.h",
     "arc/arc_support_host.cc",
     "arc/arc_support_host.h",
-    "arc/auth/arc_robot_auth.cc",
-    "arc/auth/arc_robot_auth.h",
+    "arc/auth/arc_auth_code_fetcher.h",
+    "arc/auth/arc_background_auth_code_fetcher.cc",
+    "arc/auth/arc_background_auth_code_fetcher.h",
+    "arc/auth/arc_manual_auth_code_fetcher.cc",
+    "arc/auth/arc_manual_auth_code_fetcher.h",
+    "arc/auth/arc_robot_auth_code_fetcher.cc",
+    "arc/auth/arc_robot_auth_code_fetcher.h",
     "arc/downloads_watcher/arc_downloads_watcher_service.cc",
     "arc/downloads_watcher/arc_downloads_watcher_service.h",
     "arc/enterprise/arc_enterprise_reporting_service.cc",
diff --git a/chrome/browser/chromeos/arc/arc_auth_service.cc b/chrome/browser/chromeos/arc/arc_auth_service.cc
index 3fc09a0..218497c0 100644
--- a/chrome/browser/chromeos/arc/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/arc_auth_service.cc
@@ -4,14 +4,17 @@
 
 #include "chrome/browser/chromeos/arc/arc_auth_service.h"
 
+#include <utility>
+
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/chromeos/arc/arc_auth_code_fetcher.h"
-#include "chrome/browser/chromeos/arc/arc_optin_uma.h"
 #include "chrome/browser/chromeos/arc/arc_optin_uma.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
-#include "chrome/browser/chromeos/arc/auth/arc_robot_auth.h"
+#include "chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h"
+#include "chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h"
+#include "chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.h"
+#include "chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chromeos/chromeos_switches.h"
@@ -148,12 +151,8 @@
 }
 
 void ArcAuthService::OnInstanceClosed() {
-  ArcSupportHost* support_host = ArcSessionManager::Get()->support_host();
-  if (support_host)
-    support_host->RemoveObserver(this);
+  fetcher_.reset();
   notifier_.reset();
-  arc_robot_auth_.reset();
-  auth_code_fetcher_.reset();
 }
 
 void ArcAuthService::OnSignInComplete() {
@@ -172,6 +171,14 @@
                      weak_ptr_factory_.GetWeakPtr())));
 }
 
+void ArcAuthService::OnAccountInfoReady(mojom::AccountInfoPtr account_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod(
+      "OnAccountInfoReady", kMinVersionForOnAccountInfoReady);
+  DCHECK(instance);
+  instance->OnAccountInfoReady(std::move(account_info));
+}
+
 void ArcAuthService::GetAuthCodeDeprecated0(
     const GetAuthCodeDeprecated0Callback& callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -205,6 +212,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // No other auth code-related operation may be in progress.
   DCHECK(!notifier_);
+  DCHECK(!fetcher_);
 
   if (ArcSessionManager::IsOptInVerificationDisabled()) {
     notifier->Notify(
@@ -216,80 +224,30 @@
   // Hereafter asynchronous operation. Remember the notifier.
   notifier_ = std::move(notifier);
 
-  // In Kiosk mode, use Robot auth code fetching.
   if (ArcSessionManager::IsArcKioskMode()) {
-    arc_robot_auth_.reset(new ArcRobotAuth());
-    arc_robot_auth_->FetchRobotAuthCode(
-        base::Bind(&ArcAuthService::OnRobotAuthCodeFetched,
-                   weak_ptr_factory_.GetWeakPtr()));
-    return;
-  }
-
-  // Optionally retrive auth code in silent mode.
-  if (base::FeatureList::IsEnabled(arc::kArcUseAuthEndpointFeature)) {
-    DCHECK(!auth_code_fetcher_);
-    auth_code_fetcher_ = base::MakeUnique<ArcAuthCodeFetcher>(
+    // In Kiosk mode, use Robot auth code fetching.
+    fetcher_ = base::MakeUnique<ArcRobotAuthCodeFetcher>();
+  } else if (base::FeatureList::IsEnabled(arc::kArcUseAuthEndpointFeature)) {
+    // Optionally retrieve auth code in silent mode.
+    fetcher_ = base::MakeUnique<ArcBackgroundAuthCodeFetcher>(
         ArcSessionManager::Get()->profile(),
         ArcSessionManager::Get()->auth_context());
-    auth_code_fetcher_->Fetch(base::Bind(&ArcAuthService::OnAuthCodeFetched,
-                                         weak_ptr_factory_.GetWeakPtr()));
-    return;
-  }
-
-  // Otherwise, show LSO page to user after HTTP context preparation, and let
-  // them click "Sign in" button.
-  ArcSessionManager::Get()->auth_context()->Prepare(base::Bind(
-      &ArcAuthService::OnContextPrepared, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ArcAuthService::OnContextPrepared(
-    net::URLRequestContextGetter* request_context_getter) {
-  ArcSupportHost* support_host = ArcSessionManager::Get()->support_host();
-  // Here, support_host should be available always. The case support_host is
-  // not created is when 1) IsOptInVerificationDisabled() is true or 2)
-  // IsArcKioskMode() is true. Both cases are handled above.
-  DCHECK(support_host);
-  if (!support_host->HasObserver(this))
-    support_host->AddObserver(this);
-
-  if (request_context_getter) {
-    support_host->ShowLso();
   } else {
-    support_host->ShowError(ArcSupportHost::Error::SIGN_IN_NETWORK_ERROR,
-                            false);
+    // Otherwise, show LSO page and let user click "Sign in" button.
+    // Here, support_host should be available always. The case support_host is
+    // not created is when 1) IsOptInVerificationDisabled() is true or 2)
+    // IsArcKioskMode() is true. Both cases are handled above.
+    fetcher_ = base::MakeUnique<ArcManualAuthCodeFetcher>(
+        ArcSessionManager::Get()->auth_context(),
+        ArcSessionManager::Get()->support_host());
   }
-}
-
-void ArcAuthService::OnAccountInfoReady(mojom::AccountInfoPtr account_info) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod(
-      "OnAccountInfoReady", kMinVersionForOnAccountInfoReady);
-  DCHECK(instance);
-  instance->OnAccountInfoReady(std::move(account_info));
-}
-
-void ArcAuthService::OnRobotAuthCodeFetched(
-    const std::string& robot_auth_code) {
-  // We fetching robot auth code for ARC kiosk only.
-  DCHECK(ArcSessionManager::IsArcKioskMode());
-
-  // Current instance of ArcRobotAuth became useless.
-  arc_robot_auth_.reset();
-
-  if (robot_auth_code.empty()) {
-    VLOG(1) << "Robot account auth code fetching error";
-    // Log out the user. All the cleanup will be done in Shutdown() method.
-    // The callback is not called because auth code is empty.
-    chrome::AttemptUserExit();
-    return;
-  }
-
-  OnAuthCodeObtained(robot_auth_code);
+  fetcher_->Fetch(base::Bind(&ArcAuthService::OnAuthCodeFetched,
+                             weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcAuthService::OnAuthCodeFetched(const std::string& auth_code) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  auth_code_fetcher_.reset();
+  fetcher_.reset();
 
   if (auth_code.empty()) {
     ArcSessionManager::Get()->OnProvisioningFinished(
@@ -297,13 +255,6 @@
     return;
   }
 
-  OnAuthCodeObtained(auth_code);
-}
-
-void ArcAuthService::OnAuthCodeObtained(const std::string& auth_code) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!auth_code.empty());
-
   notifier_->Notify(
       !ArcSessionManager::IsOptInVerificationDisabled(), auth_code,
       GetAccountType(),
@@ -311,22 +262,4 @@
   notifier_.reset();
 }
 
-void ArcAuthService::OnAuthSucceeded(const std::string& auth_code) {
-  OnAuthCodeObtained(auth_code);
-}
-
-void ArcAuthService::OnRetryClicked() {
-  ArcSupportHost* support_host = ArcSessionManager::Get()->support_host();
-  // This is the callback for the UI event, so support_host should be always
-  // available here.
-  DCHECK(support_host);
-  if (support_host->ui_page() == ArcSupportHost::UIPage::ERROR) {
-    // This case is handled by ArcSessionManager::OnRetryClicked().
-    return;
-  }
-
-  ArcSessionManager::Get()->auth_context()->Prepare(base::Bind(
-      &ArcAuthService::OnContextPrepared, weak_ptr_factory_.GetWeakPtr()));
-}
-
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/arc_auth_service.h b/chrome/browser/chromeos/arc/arc_auth_service.h
index d008f1f..13080e1 100644
--- a/chrome/browser/chromeos/arc/arc_auth_service.h
+++ b/chrome/browser/chromeos/arc/arc_auth_service.h
@@ -10,27 +10,20 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/arc/arc_support_host.h"
 #include "components/arc/arc_service.h"
 #include "components/arc/common/auth.mojom.h"
 #include "components/arc/instance_holder.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
-namespace net {
-class URLRequestContextGetter;
-}
-
 namespace arc {
 
 class ArcAuthCodeFetcher;
-class ArcRobotAuth;
 
 // Implementation of ARC authorization.
 // TODO(hidehiko): Move to c/b/c/arc/auth with adding tests.
 class ArcAuthService : public ArcService,
                        public mojom::AuthHost,
-                       public InstanceHolder<mojom::AuthInstance>::Observer,
-                       public ArcSupportHost::Observer {
+                       public InstanceHolder<mojom::AuthInstance>::Observer {
  public:
   explicit ArcAuthService(ArcBridgeService* bridge_service);
   ~ArcAuthService() override;
@@ -60,10 +53,6 @@
   void GetIsAccountManagedDeprecated(
       const GetIsAccountManagedDeprecatedCallback& callback) override;
 
-  // ArcSupportHost::Observer:
-  void OnAuthSucceeded(const std::string& auth_code) override;
-  void OnRetryClicked() override;
-
  private:
   using AccountInfoCallback = base::Callback<void(mojom::AccountInfoPtr)>;
   class AccountInfoNotifier;
@@ -72,28 +61,16 @@
   void RequestAccountInfoInternal(
       std::unique_ptr<AccountInfoNotifier> account_info_notifier);
 
-  // Called when HTTP context is prepared.
-  void OnContextPrepared(net::URLRequestContextGetter* request_context_getter);
-
-  void OnAccountInfoReady(mojom::AccountInfoPtr account_info);
-
-  // Callback for Robot auth in Kiosk mode.
-  void OnRobotAuthCodeFetched(const std::string& auth_code);
-
-  // Callback for automatic auth code fetching when --arc-user-auth-endpoint
-  // flag is set.
+  // Callback on auth_code fetched.
   void OnAuthCodeFetched(const std::string& auth_code);
 
-  // Common procedure across LSO auth code fetching, automatic auth code
-  // fetching, and Robot auth.
-  void OnAuthCodeObtained(const std::string& auth_code);
+  // Called to let ARC container know the account info.
+  void OnAccountInfoReady(mojom::AccountInfoPtr account_info);
 
   mojo::Binding<mojom::AuthHost> binding_;
 
-  std::unique_ptr<ArcAuthCodeFetcher> auth_code_fetcher_;
-  std::unique_ptr<ArcRobotAuth> arc_robot_auth_;
-
   std::unique_ptr<AccountInfoNotifier> notifier_;
+  std::unique_ptr<ArcAuthCodeFetcher> fetcher_;
 
   base::WeakPtrFactory<ArcAuthService> weak_ptr_factory_;
 
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 2be4bdc2..3159554 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -14,14 +14,14 @@
 #include "base/logging.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/arc/arc_auth_code_fetcher.h"
 #include "chrome/browser/chromeos/arc/arc_auth_context.h"
 #include "chrome/browser/chromeos/arc/arc_optin_uma.h"
 #include "chrome/browser/chromeos/arc/arc_support_host.h"
-#include "chrome/browser/chromeos/arc/auth/arc_robot_auth.h"
 #include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h"
 #include "chrome/browser/chromeos/arc/policy/arc_android_management_checker.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
+#include "chrome/browser/chromeos/login/user_flow.h"
+#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
@@ -168,6 +168,13 @@
     return false;
   }
 
+  chromeos::UserFlow* user_flow =
+      chromeos::ChromeUserManager::Get()->GetUserFlow(user->GetAccountId());
+  if (!user_flow || !user_flow->CanStartArc()) {
+    VLOG(1) << "ARC is not allowed in the current user flow.";
+    return false;
+  }
+
   if (user_manager::UserManager::Get()
           ->IsCurrentUserCryptohomeDataEphemeral()) {
     VLOG(2) << "Users with ephemeral data are not supported in Arc.";
@@ -242,6 +249,14 @@
   DCHECK_EQ(state_, State::ACTIVE);
 
   if (result == ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR) {
+    if (IsArcKioskMode()) {
+      VLOG(1) << "Robot account auth code fetching error";
+      // Log out the user. All the cleanup will be done in Shutdown() method.
+      // The callback is not called because auth code is empty.
+      chrome::AttemptUserExit();
+      return;
+    }
+
     // For backwards compatibility, use NETWORK_ERROR for
     // CHROME_SERVER_COMMUNICATION_ERROR case.
     UpdateOptInCancelUMA(OptInCancelReason::NETWORK_ERROR);
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h b/chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h
new file mode 100644
index 0000000..7f9770e
--- /dev/null
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h
@@ -0,0 +1,30 @@
+// 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_CHROMEOS_ARC_AUTH_ARC_AUTH_CODE_FETCHER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_AUTH_CODE_FETCHER_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace arc {
+
+// Interface to implement auth_code fetching.
+class ArcAuthCodeFetcher {
+ public:
+  virtual ~ArcAuthCodeFetcher() = default;
+
+  // Fetches the |auth_code|. On success, |callback| is called with the
+  // fetched |auth_code|. Otherwise, |callback| is called with empty string.
+  // Fetch() should be called once par instance, and it is expected that
+  // the inflight operation is cancelled without calling the |callback|
+  // when the instance is deleted.
+  using FetchCallback = base::Callback<void(const std::string& auth_code)>;
+  virtual void Fetch(const FetchCallback& callback) = 0;
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_AUTH_CODE_FETCHER_H_
diff --git a/chrome/browser/chromeos/arc/arc_auth_code_fetcher.cc b/chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.cc
similarity index 87%
rename from chrome/browser/chromeos/arc/arc_auth_code_fetcher.cc
rename to chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.cc
index 54e334f..d21207d7 100644
--- a/chrome/browser/chromeos/arc/arc_auth_code_fetcher.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.cc
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/arc/arc_auth_code_fetcher.h"
+#include "chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h"
+
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -45,24 +47,25 @@
 
 }  // namespace
 
-ArcAuthCodeFetcher::ArcAuthCodeFetcher(Profile* profile,
-                                       ArcAuthContext* context)
+ArcBackgroundAuthCodeFetcher::ArcBackgroundAuthCodeFetcher(
+    Profile* profile,
+    ArcAuthContext* context)
     : OAuth2TokenService::Consumer(kConsumerName),
       profile_(profile),
       context_(context),
       weak_ptr_factory_(this) {}
 
-ArcAuthCodeFetcher::~ArcAuthCodeFetcher() = default;
+ArcBackgroundAuthCodeFetcher::~ArcBackgroundAuthCodeFetcher() = default;
 
-void ArcAuthCodeFetcher::Fetch(const FetchCallback& callback) {
+void ArcBackgroundAuthCodeFetcher::Fetch(const FetchCallback& callback) {
   DCHECK(callback_.is_null());
   callback_ = callback;
 
-  context_->Prepare(base::Bind(&ArcAuthCodeFetcher::OnPrepared,
+  context_->Prepare(base::Bind(&ArcBackgroundAuthCodeFetcher::OnPrepared,
                                weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ArcAuthCodeFetcher::OnPrepared(
+void ArcBackgroundAuthCodeFetcher::OnPrepared(
     net::URLRequestContextGetter* request_context_getter) {
   if (!request_context_getter) {
     base::ResetAndReturn(&callback_).Run(std::string());
@@ -82,7 +85,7 @@
   login_token_request_ = token_service->StartRequest(account_id, scopes, this);
 }
 
-void ArcAuthCodeFetcher::OnGetTokenSuccess(
+void ArcBackgroundAuthCodeFetcher::OnGetTokenSuccess(
     const OAuth2TokenService::Request* request,
     const std::string& access_token,
     const base::Time& expiration_time) {
@@ -111,7 +114,7 @@
   auth_code_fetcher_->Start();
 }
 
-void ArcAuthCodeFetcher::OnGetTokenFailure(
+void ArcBackgroundAuthCodeFetcher::OnGetTokenFailure(
     const OAuth2TokenService::Request* request,
     const GoogleServiceAuthError& error) {
   VLOG(2) << "Failed to get LST " << error.ToString() << ".";
@@ -119,7 +122,8 @@
   base::ResetAndReturn(&callback_).Run(std::string());
 }
 
-void ArcAuthCodeFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
+void ArcBackgroundAuthCodeFetcher::OnURLFetchComplete(
+    const net::URLFetcher* source) {
   const int response_code = source->GetResponseCode();
   std::string json_string;
   source->GetResponseAsString(&json_string);
@@ -162,7 +166,7 @@
   base::ResetAndReturn(&callback_).Run(auth_code);
 }
 
-void ArcAuthCodeFetcher::ResetFetchers() {
+void ArcBackgroundAuthCodeFetcher::ResetFetchers() {
   login_token_request_.reset();
   auth_code_fetcher_.reset();
 }
diff --git a/chrome/browser/chromeos/arc/arc_auth_code_fetcher.h b/chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h
similarity index 65%
rename from chrome/browser/chromeos/arc/arc_auth_code_fetcher.h
rename to chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h
index 19a2c18f..b7d41a8 100644
--- a/chrome/browser/chromeos/arc/arc_auth_code_fetcher.h
+++ b/chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.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_CHROMEOS_ARC_ARC_AUTH_CODE_FETCHER_H_
-#define CHROME_BROWSER_CHROMEOS_ARC_ARC_AUTH_CODE_FETCHER_H_
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_BACKGROUND_AUTH_CODE_FETCHER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_BACKGROUND_AUTH_CODE_FETCHER_H_
 
 #include <memory>
 #include <string>
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/arc/arc_auth_context.h"
+#include "chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_fetcher_delegate.h"
 
@@ -26,16 +27,19 @@
 
 // The instance is not reusable, so for each Fetch(), the instance must be
 // re-created. Deleting the instance cancels inflight operation.
-class ArcAuthCodeFetcher : public OAuth2TokenService::Consumer,
-                           public net::URLFetcherDelegate {
+class ArcBackgroundAuthCodeFetcher : public ArcAuthCodeFetcher,
+                                     public OAuth2TokenService::Consumer,
+                                     public net::URLFetcherDelegate {
  public:
-  ArcAuthCodeFetcher(Profile* profile, ArcAuthContext* context);
-  ~ArcAuthCodeFetcher() override;
+  ArcBackgroundAuthCodeFetcher(Profile* profile, ArcAuthContext* context);
+  ~ArcBackgroundAuthCodeFetcher() override;
 
-  // Starts to fetch the token. On success fetched |auth_token| is passed.
-  // On error, auth_token is empty.
-  using FetchCallback = base::Callback<void(const std::string& auth_token)>;
-  void Fetch(const FetchCallback& callback);
+  // ArcAuthCodeFetcher:
+  void Fetch(const FetchCallback& callback) override;
+
+ private:
+  void ResetFetchers();
+  void OnPrepared(net::URLRequestContextGetter* request_context_getter);
 
   // OAuth2TokenService::Consumer:
   void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
@@ -47,10 +51,6 @@
   // net::URLFetcherDelegate:
   void OnURLFetchComplete(const net::URLFetcher* source) override;
 
- private:
-  void ResetFetchers();
-  void OnPrepared(net::URLRequestContextGetter* request_context_getter);
-
   // Unowned pointers.
   Profile* const profile_;
   ArcAuthContext* const context_;
@@ -61,11 +61,11 @@
   std::unique_ptr<OAuth2TokenService::Request> login_token_request_;
   std::unique_ptr<net::URLFetcher> auth_code_fetcher_;
 
-  base::WeakPtrFactory<ArcAuthCodeFetcher> weak_ptr_factory_;
+  base::WeakPtrFactory<ArcBackgroundAuthCodeFetcher> weak_ptr_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(ArcAuthCodeFetcher);
+  DISALLOW_COPY_AND_ASSIGN(ArcBackgroundAuthCodeFetcher);
 };
 
 }  // namespace arc
 
-#endif  // CHROME_BROWSER_CHROMEOS_ARC_ARC_AUTH_CODE_FETCHER_H_
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_BACKGROUND_AUTH_CODE_FETCHER_H_
diff --git a/chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.cc b/chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.cc
new file mode 100644
index 0000000..28fe5ef52
--- /dev/null
+++ b/chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.cc
@@ -0,0 +1,61 @@
+// 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/chromeos/arc/auth/arc_manual_auth_code_fetcher.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "chrome/browser/chromeos/arc/arc_auth_context.h"
+
+namespace arc {
+
+ArcManualAuthCodeFetcher::ArcManualAuthCodeFetcher(ArcAuthContext* context,
+                                                   ArcSupportHost* support_host)
+    : context_(context), support_host_(support_host), weak_ptr_factory_(this) {
+  DCHECK(context_);
+  DCHECK(support_host_);
+  support_host_->AddObserver(this);
+}
+
+ArcManualAuthCodeFetcher::~ArcManualAuthCodeFetcher() {
+  support_host_->RemoveObserver(this);
+}
+
+void ArcManualAuthCodeFetcher::Fetch(const FetchCallback& callback) {
+  DCHECK(pending_callback_.is_null());
+  pending_callback_ = callback;
+
+  FetchInternal();
+}
+
+void ArcManualAuthCodeFetcher::FetchInternal() {
+  DCHECK(!pending_callback_.is_null());
+  context_->Prepare(base::Bind(&ArcManualAuthCodeFetcher::OnContextPrepared,
+                               weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ArcManualAuthCodeFetcher::OnContextPrepared(
+    net::URLRequestContextGetter* request_context_getter) {
+  DCHECK(!pending_callback_.is_null());
+  if (!request_context_getter) {
+    support_host_->ShowError(ArcSupportHost::Error::SIGN_IN_NETWORK_ERROR,
+                             false);
+    return;
+  }
+
+  support_host_->ShowLso();
+}
+
+void ArcManualAuthCodeFetcher::OnAuthSucceeded(const std::string& auth_code) {
+  DCHECK(!pending_callback_.is_null());
+  base::ResetAndReturn(&pending_callback_).Run(auth_code);
+}
+
+void ArcManualAuthCodeFetcher::OnRetryClicked() {
+  DCHECK(!pending_callback_.is_null());
+  FetchInternal();
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.h b/chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.h
new file mode 100644
index 0000000..471d365
--- /dev/null
+++ b/chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.h
@@ -0,0 +1,57 @@
+// 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_CHROMEOS_ARC_AUTH_ARC_MANUAL_AUTH_CODE_FETCHER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_MANUAL_AUTH_CODE_FETCHER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/arc/arc_support_host.h"
+#include "chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace arc {
+
+class ArcAuthContext;
+
+// Implements the auth token fetching procedure with user's "SIGN IN" button
+// click.
+class ArcManualAuthCodeFetcher : public ArcAuthCodeFetcher,
+                                 public ArcSupportHost::Observer {
+ public:
+  ArcManualAuthCodeFetcher(ArcAuthContext* context,
+                           ArcSupportHost* support_host);
+  ~ArcManualAuthCodeFetcher() override;
+
+  // ArcAuthCodeFetcher:
+  void Fetch(const FetchCallback& callback) override;
+
+ private:
+  void FetchInternal();
+
+  // Called when HTTP request gets ready.
+  void OnContextPrepared(net::URLRequestContextGetter* request_context_getter);
+
+  // ArcSupportHost::Observer:
+  void OnAuthSucceeded(const std::string& auth_code) override;
+  void OnRetryClicked() override;
+
+  ArcAuthContext* const context_;
+  ArcSupportHost* const support_host_;
+
+  FetchCallback pending_callback_;
+
+  base::WeakPtrFactory<ArcManualAuthCodeFetcher> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcManualAuthCodeFetcher);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_MANUAL_AUTH_CODE_FETCHER_H_
diff --git a/chrome/browser/chromeos/arc/auth/arc_robot_auth.h b/chrome/browser/chromeos/arc/auth/arc_robot_auth.h
deleted file mode 100644
index dbcdbe1..0000000
--- a/chrome/browser/chromeos/arc/auth/arc_robot_auth.h
+++ /dev/null
@@ -1,45 +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_CHROMEOS_ARC_AUTH_ARC_ROBOT_AUTH_H_
-#define CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_ROBOT_AUTH_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "components/policy/core/common/cloud/device_management_service.h"
-
-namespace arc {
-
-// This class is responsible to fetch auth code for robot account. Robot auth
-// code is used for creation an account on Android side in ARC kiosk mode.
-class ArcRobotAuth {
- public:
-  using RobotAuthCodeCallback = base::Callback<void(const std::string&)>;
-  ArcRobotAuth();
-  ~ArcRobotAuth();
-
-  // Fetches robot auth code. When auth code is fetched, the callback is
-  // invoked. Invoking callback with empty string means a fetch error.
-  // FetchRobotAuthCode() should not be called while another inflight operation
-  // is running.
-  void FetchRobotAuthCode(const RobotAuthCodeCallback& callback);
-
- private:
-  void OnFetchRobotAuthCodeCompleted(
-      RobotAuthCodeCallback callback,
-      policy::DeviceManagementStatus status,
-      int net_error,
-      const enterprise_management::DeviceManagementResponse& response);
-
-  std::unique_ptr<policy::DeviceManagementRequestJob> fetch_request_job_;
-  base::WeakPtrFactory<ArcRobotAuth> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ArcRobotAuth);
-};
-
-}  // namespace arc
-
-#endif  // CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_ROBOT_AUTH_H_
diff --git a/chrome/browser/chromeos/arc/auth/arc_robot_auth.cc b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc
similarity index 85%
rename from chrome/browser/chromeos/arc/auth/arc_robot_auth.cc
rename to chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc
index b8c0a24..58bedb7 100644
--- a/chrome/browser/chromeos/arc/auth/arc_robot_auth.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/arc/auth/arc_robot_auth.h"
+#include "chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h"
+
+#include <string>
 
 #include "base/bind.h"
 #include "chrome/browser/browser_process.h"
@@ -36,11 +38,11 @@
 
 namespace arc {
 
-ArcRobotAuth::ArcRobotAuth() : weak_ptr_factory_(this) {}
+ArcRobotAuthCodeFetcher::ArcRobotAuthCodeFetcher() : weak_ptr_factory_(this) {}
 
-ArcRobotAuth::~ArcRobotAuth() = default;
+ArcRobotAuthCodeFetcher::~ArcRobotAuthCodeFetcher() = default;
 
-void ArcRobotAuth::FetchRobotAuthCode(const RobotAuthCodeCallback& callback) {
+void ArcRobotAuthCodeFetcher::Fetch(const FetchCallback& callback) {
   DCHECK(!fetch_request_job_);
   const policy::CloudPolicyClient* client = GetCloudPolicyClient();
 
@@ -58,12 +60,12 @@
   request->add_auth_scope(GaiaConstants::kAnyApiOAuth2Scope);
 
   fetch_request_job_->Start(
-      base::Bind(&ArcRobotAuth::OnFetchRobotAuthCodeCompleted,
+      base::Bind(&ArcRobotAuthCodeFetcher::OnFetchRobotAuthCodeCompleted,
                  weak_ptr_factory_.GetWeakPtr(), callback));
 }
 
-void ArcRobotAuth::OnFetchRobotAuthCodeCompleted(
-    RobotAuthCodeCallback callback,
+void ArcRobotAuthCodeFetcher::OnFetchRobotAuthCodeCompleted(
+    FetchCallback callback,
     policy::DeviceManagementStatus status,
     int net_error,
     const enterprise_management::DeviceManagementResponse& response) {
diff --git a/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h
new file mode 100644
index 0000000..fdf02d7
--- /dev/null
+++ b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h
@@ -0,0 +1,51 @@
+// 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_CHROMEOS_ARC_AUTH_ARC_ROBOT_AUTH_CODE_FETCHER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_ROBOT_AUTH_CODE_FETCHER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+
+namespace enterprise_management {
+class DeviceManagementResponse;
+}
+
+namespace policy {
+class DeviceManagementRequestJob;
+}
+
+namespace arc {
+
+// This class is responsible to fetch auth code for robot account. Robot auth
+// code is used for creation an account on Android side in ARC kiosk mode.
+class ArcRobotAuthCodeFetcher : public ArcAuthCodeFetcher {
+ public:
+  ArcRobotAuthCodeFetcher();
+  ~ArcRobotAuthCodeFetcher() override;
+
+  // ArcAuthCodeFetcher:
+  void Fetch(const FetchCallback& callback) override;
+
+ private:
+  void OnFetchRobotAuthCodeCompleted(
+      FetchCallback callback,
+      policy::DeviceManagementStatus status,
+      int net_error,
+      const enterprise_management::DeviceManagementResponse& response);
+
+  std::unique_ptr<policy::DeviceManagementRequestJob> fetch_request_job_;
+  base::WeakPtrFactory<ArcRobotAuthCodeFetcher> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcRobotAuthCodeFetcher);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_AUTH_ARC_ROBOT_AUTH_CODE_FETCHER_H_
diff --git a/chrome/browser/chromeos/arc/auth/arc_robot_auth_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc
similarity index 92%
rename from chrome/browser/chromeos/arc/auth/arc_robot_auth_browsertest.cc
rename to chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc
index ba6ab84..1ec0c7c6 100644
--- a/chrome/browser/chromeos/arc/auth/arc_robot_auth_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc
@@ -78,12 +78,12 @@
 
 }  // namespace
 
-class ArcRobotAuthBrowserTest : public InProcessBrowserTest {
+class ArcRobotAuthCodeFetcherBrowserTest : public InProcessBrowserTest {
  protected:
-  ArcRobotAuthBrowserTest() = default;
+  ArcRobotAuthCodeFetcherBrowserTest() = default;
 
   // InProcessBrowserTest:
-  ~ArcRobotAuthBrowserTest() override = default;
+  ~ArcRobotAuthCodeFetcherBrowserTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     InProcessBrowserTest::SetUpCommandLine(command_line);
@@ -144,10 +144,11 @@
  private:
   std::unique_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_;
 
-  DISALLOW_COPY_AND_ASSIGN(ArcRobotAuthBrowserTest);
+  DISALLOW_COPY_AND_ASSIGN(ArcRobotAuthCodeFetcherBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ArcRobotAuthBrowserTest, RequestAccountInfoSuccess) {
+IN_PROC_BROWSER_TEST_F(ArcRobotAuthCodeFetcherBrowserTest,
+                       RequestAccountInfoSuccess) {
   interceptor_->PushJobCallback(base::Bind(&ResponseJob));
 
   auth_instance_.callback =
@@ -162,13 +163,15 @@
   base::RunLoop().RunUntilIdle();
 }
 
-IN_PROC_BROWSER_TEST_F(ArcRobotAuthBrowserTest, RequestAccountInfoError) {
+IN_PROC_BROWSER_TEST_F(ArcRobotAuthCodeFetcherBrowserTest,
+                       RequestAccountInfoError) {
   interceptor_->PushJobCallback(
       policy::TestRequestInterceptor::BadRequestJob());
 
   auth_instance_.callback =
       base::Bind([](const mojom::AccountInfoPtr&) { FAIL(); });
 
+  ArcSessionManager::Get()->StartArc();
   ArcAuthService::GetForTest()->RequestAccountInfo();
   // This MessageLoop will be stopped by AttemptUserExit(), that is called as
   // a result of error of auth code fetching.
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.cc
index 856922b0..30b1eb0 100644
--- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.cc
@@ -122,6 +122,10 @@
   return false;
 }
 
+bool BootstrapUserFlow::CanStartArc() {
+  return false;
+}
+
 bool BootstrapUserFlow::ShouldLaunchBrowser() {
   return finished_;
 }
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.h b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.h
index 9b6926b..4f39b2a 100644
--- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.h
+++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_flow.h
@@ -42,6 +42,7 @@
 
   // chromeos::ExtendedUserFlow
   bool CanLockScreen() override;
+  bool CanStartArc() override;
   bool ShouldLaunchBrowser() override;
   bool ShouldSkipPostLoginScreens() override;
   bool HandleLoginFailure(const chromeos::AuthFailure& failure) override;
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc
index 2ba28ad9..fea8f7f 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc
@@ -16,6 +16,10 @@
   return true;
 }
 
+bool EasyUnlockUserLoginFlow::CanStartArc() {
+  return true;
+}
+
 bool EasyUnlockUserLoginFlow::ShouldLaunchBrowser() {
   return true;
 }
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.h b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.h
index 34b1919f..65465d4 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.h
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.h
@@ -23,6 +23,7 @@
  private:
   // chromeos::ExtendedUserFlow implementation.
   bool CanLockScreen() override;
+  bool CanStartArc() override;
   bool ShouldLaunchBrowser() override;
   bool ShouldSkipPostLoginScreens() override;
   bool HandleLoginFailure(const chromeos::AuthFailure& failure) override;
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.cc
index a6101e6ea..394046d 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.cc
@@ -42,6 +42,10 @@
   return false;
 }
 
+bool SupervisedUserCreationFlow::CanStartArc() {
+  return false;
+}
+
 bool SupervisedUserCreationFlow::ShouldShowSettings() {
   return false;
 }
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.h b/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.h
index 5e5da704..2c88141e 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.h
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.h
@@ -23,6 +23,7 @@
   ~SupervisedUserCreationFlow() override;
 
   bool CanLockScreen() override;
+  bool CanStartArc() override;
   bool ShouldShowSettings() override;
   bool ShouldShowNotificationTray() override;
   bool ShouldLaunchBrowser() override;
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc b/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc
index 08e387c..4a1ae73 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc
@@ -46,6 +46,10 @@
   return true;
 }
 
+bool SupervisedUserLoginFlow::CanStartArc() {
+  return false;
+}
+
 bool SupervisedUserLoginFlow::ShouldLaunchBrowser() {
   return data_loaded_;
 }
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.h b/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.h
index 9ecae8e..4f25bd0 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.h
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.h
@@ -26,6 +26,7 @@
   // ExtendedUserFlow overrides.
   void AppendAdditionalCommandLineSwitches() override;
   bool CanLockScreen() override;
+  bool CanStartArc() override;
   bool ShouldLaunchBrowser() override;
   bool ShouldSkipPostLoginScreens() override;
   bool SupportsEarlyRestartToApplyFlags() override;
diff --git a/chrome/browser/chromeos/login/user_flow.cc b/chrome/browser/chromeos/login/user_flow.cc
index 6e1a6a7..f0871e8 100644
--- a/chrome/browser/chromeos/login/user_flow.cc
+++ b/chrome/browser/chromeos/login/user_flow.cc
@@ -30,6 +30,10 @@
   return true;
 }
 
+bool DefaultUserFlow::CanStartArc() {
+  return true;
+}
+
 bool DefaultUserFlow::ShouldShowSettings() {
   return true;
 }
diff --git a/chrome/browser/chromeos/login/user_flow.h b/chrome/browser/chromeos/login/user_flow.h
index 91578aa..23c9094ac 100644
--- a/chrome/browser/chromeos/login/user_flow.h
+++ b/chrome/browser/chromeos/login/user_flow.h
@@ -28,6 +28,7 @@
 
   // Indicates if screen locking should be enabled or disabled for a flow.
   virtual bool CanLockScreen() = 0;
+  virtual bool CanStartArc() = 0;
   virtual bool ShouldShowSettings() = 0;
   virtual bool ShouldShowNotificationTray() = 0;
   virtual bool ShouldLaunchBrowser() = 0;
@@ -56,6 +57,7 @@
 
   void AppendAdditionalCommandLineSwitches() override;
   bool CanLockScreen() override;
+  bool CanStartArc() override;
   bool ShouldShowSettings() override;
   bool ShouldShowNotificationTray() override;
   bool ShouldLaunchBrowser() override;
diff --git a/chrome/browser/features.cc b/chrome/browser/features.cc
new file mode 100644
index 0000000..5b6c841
--- /dev/null
+++ b/chrome/browser/features.cc
@@ -0,0 +1,12 @@
+// 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/features.h"
+
+namespace features {
+
+const base::Feature kDesktopFastShutdown{"DesktopFastShutdown",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
diff --git a/chrome/browser/features.h b/chrome/browser/features.h
new file mode 100644
index 0000000..edf8951
--- /dev/null
+++ b/chrome/browser/features.h
@@ -0,0 +1,19 @@
+// 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.
+
+// Contains features that need inclusion from multiple files. Features used from
+// a single .cc file should be declared in the file's anonymous namespace.
+
+#ifndef CHROME_BROWSER_FEATURES_H_
+#define CHROME_BROWSER_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace features {
+
+extern const base::Feature kDesktopFastShutdown;
+
+}  // namespace features
+
+#endif  // CHROME_BROWSER_FEATURES_H_
diff --git a/chrome/browser/permissions/permission_request_impl.cc b/chrome/browser/permissions/permission_request_impl.cc
index 0abc8aa..a9bbd496 100644
--- a/chrome/browser/permissions/permission_request_impl.cc
+++ b/chrome/browser/permissions/permission_request_impl.cc
@@ -71,7 +71,7 @@
 #if defined(OS_CHROMEOS)
     // TODO(xhwang): fix this icon, see crrev.com/863263007
     case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
-      return gfx::VectorIconId::CHROME_PRODUCT;
+      return gfx::VectorIconId::PRODUCT;
 #endif
     case content::PermissionType::MIDI_SYSEX:
       return gfx::VectorIconId::MIDI;
diff --git a/chrome/browser/resources/history/other_devices.js b/chrome/browser/resources/history/other_devices.js
index 2ba6f76..0a10191 100644
--- a/chrome/browser/resources/history/other_devices.js
+++ b/chrome/browser/resources/history/other_devices.js
@@ -541,18 +541,12 @@
 /**
  * Sets the menu model data. An empty list means that either there are no
  * foreign sessions, or tab sync is disabled for this profile.
- * |isTabSyncEnabled| makes it possible to distinguish between the cases.
  *
  * @param {Array} sessionList Array of objects describing the sessions
  *     from other devices.
- * @param {boolean} isTabSyncEnabled Is tab sync enabled for this profile?
  */
-function setForeignSessions(sessionList, isTabSyncEnabled) {
-  // The other devices is shown iff tab sync is enabled.
-  if (isTabSyncEnabled)
-    devicesView.setSessionList(sessionList);
-  else
-    devicesView.clearDOM();
+function setForeignSessions(sessionList) {
+  devicesView.setSessionList(sessionList);
 }
 
 /**
diff --git a/chrome/browser/resources/md_history/app.crisper.js b/chrome/browser/resources/md_history/app.crisper.js
index 04ed24c..eb0c494b 100644
--- a/chrome/browser/resources/md_history/app.crisper.js
+++ b/chrome/browser/resources/md_history/app.crisper.js
@@ -83,4 +83,4 @@
 // 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.
-cr.define("md_history",function(){var lazyLoadPromise=null;function ensureLazyLoaded(){if(!lazyLoadPromise){lazyLoadPromise=new Promise(function(resolve,reject){Polymer.Base.importHref("chrome://history/lazy_load.html",resolve,reject,true)})}return lazyLoadPromise}return{ensureLazyLoaded:ensureLazyLoaded}});Polymer({is:"history-app",behaviors:[Polymer.IronScrollTargetBehavior],properties:{showSidebarFooter:Boolean,hasSyncedResults:Boolean,selectedPage_:{type:String,observer:"selectedPageChanged_"},grouped_:{type:Boolean,reflectToAttribute:true},queryState_:{type:Object,value:function(){return{incremental:false,querying:true,queryingDisabled:false,_range:HistoryRange.ALL_TIME,searchTerm:"",groupedOffset:0,set range(val){this._range=Number(val)},get range(){return this._range}}}},queryResult_:{type:Object,value:function(){return{info:null,results:null,sessionList:null}}},hasDrawer_:Boolean,isUserSignedIn_:{type:Boolean,value:loadTimeData.getBoolean("isUserSignedIn")},toolbarShadow_:{type:Boolean,reflectToAttribute:true,notify:true},showMenuPromo_:{type:Boolean,value:function(){return loadTimeData.getBoolean("showMenuPromo")}}},listeners:{"cr-toolbar-menu-promo-close":"onCrToolbarMenuPromoClose_","cr-toolbar-menu-promo-shown":"onCrToolbarMenuPromoShown_","cr-toolbar-menu-tap":"onCrToolbarMenuTap_","delete-selected":"deleteSelected","history-checkbox-select":"checkboxSelected","history-close-drawer":"closeDrawer_","history-view-changed":"historyViewChanged_","opened-changed":"onOpenedChanged_","unselect-all":"unselectAll"},boundOnCanExecute_:null,boundOnCommand_:null,attached:function(){this.grouped_=loadTimeData.getBoolean("groupByDomain");cr.ui.decorate("command",cr.ui.Command);this.boundOnCanExecute_=this.onCanExecute_.bind(this);this.boundOnCommand_=this.onCommand_.bind(this);document.addEventListener("canExecute",this.boundOnCanExecute_);document.addEventListener("command",this.boundOnCommand_)},detached:function(){document.removeEventListener("canExecute",this.boundOnCanExecute_);document.removeEventListener("command",this.boundOnCommand_)},onFirstRender:function(){setTimeout(function(){chrome.send("metricsHandler:recordTime",["History.ResultsRenderedTime",window.performance.now()])});var searchField=this.$.toolbar.searchField;if(!searchField.narrow){searchField.getSearchInput().focus()}md_history.ensureLazyLoaded().then(function(){window.requestIdleCallback(function(){document.fonts.load("bold 12px Roboto")})})},_scrollHandler:function(){if(this.scrollTarget)this.toolbarShadow_=this.scrollTarget.scrollTop!=0},onCrToolbarMenuPromoClose_:function(){this.showMenuPromo_=false},onCrToolbarMenuPromoShown_:function(){md_history.BrowserService.getInstance().menuPromoShown()},onCrToolbarMenuTap_:function(){var drawer=this.$$("#drawer");if(drawer)drawer.toggle()},onOpenedChanged_:function(e){if(e.detail.value)this.showMenuPromo_=false},checkboxSelected:function(e){var toolbar=this.$.toolbar;toolbar.count=this.$.history.getSelectedItemCount()},unselectAll:function(){var listContainer=this.$.history;var toolbar=this.$.toolbar;listContainer.unselectAllItems(toolbar.count);toolbar.count=0},deleteSelected:function(){this.$.history.deleteSelectedWithPrompt()},historyResult:function(info,results){this.set("queryState_.querying",false);this.set("queryResult_.info",info);this.set("queryResult_.results",results);var listContainer=this.$["history"];listContainer.historyResult(info,results)},focusToolbarSearchField:function(){this.$.toolbar.showSearchField()},onCanExecute_:function(e){e=e;switch(e.command.id){case"find-command":case"toggle-grouped":e.canExecute=true;break;case"slash-command":e.canExecute=!this.$.toolbar.searchField.isSearchFocused();break;case"delete-command":e.canExecute=this.$.toolbar.count>0;break}},onCommand_:function(e){if(e.command.id=="find-command"||e.command.id=="slash-command")this.focusToolbarSearchField();if(e.command.id=="delete-command")this.deleteSelected();if(e.command.id=="toggle-grouped")this.grouped_=!this.grouped_},setForeignSessions:function(sessionList,isTabSyncEnabled){if(!isTabSyncEnabled){var syncedDeviceManagerElem=this.$$("history-synced-device-manager");if(syncedDeviceManagerElem){md_history.ensureLazyLoaded().then(function(){syncedDeviceManagerElem.tabSyncDisabled()})}return}this.set("queryResult_.sessionList",sessionList)},historyDeleted:function(){this.$.history.historyDeleted()},updateSignInState:function(isUserSignedIn){this.isUserSignedIn_=isUserSignedIn},syncedTabsSelected_:function(selectedPage){return selectedPage=="syncedTabs"},shouldShowSpinner_:function(querying,incremental,searchTerm){return querying&&!incremental&&searchTerm!=""},showSyncNotice_:function(hasSyncedResults,selectedPage){return hasSyncedResults&&selectedPage!="syncedTabs"},selectedPageChanged_:function(){this.unselectAll();this.historyViewChanged_()},historyViewChanged_:function(){requestAnimationFrame(function(){md_history.ensureLazyLoaded().then(function(){if(!this.$.content.selectedItem)return;this.scrollTarget=this.$.content.selectedItem.getContentScrollTarget();this._scrollHandler()}.bind(this))}.bind(this));this.recordHistoryPageView_()},getSelectedPage_:function(selectedPage,items){return selectedPage},closeDrawer_:function(){var drawer=this.$$("#drawer");if(drawer)drawer.close()},recordHistoryPageView_:function(){var histogramValue=HistoryPageViewHistogram.END;switch(this.selectedPage_){case"syncedTabs":histogramValue=this.isUserSignedIn_?HistoryPageViewHistogram.SYNCED_TABS:HistoryPageViewHistogram.SIGNIN_PROMO;break;default:switch(this.queryState_.range){case HistoryRange.ALL_TIME:histogramValue=HistoryPageViewHistogram.HISTORY;break;case HistoryRange.WEEK:histogramValue=HistoryPageViewHistogram.GROUPED_WEEK;break;case HistoryRange.MONTH:histogramValue=HistoryPageViewHistogram.GROUPED_MONTH;break}break}md_history.BrowserService.getInstance().recordHistogram("History.HistoryPageView",histogramValue,HistoryPageViewHistogram.END)}});
\ No newline at end of file
+cr.define("md_history",function(){var lazyLoadPromise=null;function ensureLazyLoaded(){if(!lazyLoadPromise){lazyLoadPromise=new Promise(function(resolve,reject){Polymer.Base.importHref("chrome://history/lazy_load.html",resolve,reject,true)})}return lazyLoadPromise}return{ensureLazyLoaded:ensureLazyLoaded}});Polymer({is:"history-app",behaviors:[Polymer.IronScrollTargetBehavior],properties:{showSidebarFooter:Boolean,hasSyncedResults:Boolean,selectedPage_:{type:String,observer:"selectedPageChanged_"},grouped_:{type:Boolean,reflectToAttribute:true},queryState_:{type:Object,value:function(){return{incremental:false,querying:true,queryingDisabled:false,_range:HistoryRange.ALL_TIME,searchTerm:"",groupedOffset:0,set range(val){this._range=Number(val)},get range(){return this._range}}}},queryResult_:{type:Object,value:function(){return{info:null,results:null,sessionList:null}}},hasDrawer_:Boolean,isUserSignedIn_:{type:Boolean,value:loadTimeData.getBoolean("isUserSignedIn")},toolbarShadow_:{type:Boolean,reflectToAttribute:true,notify:true},showMenuPromo_:{type:Boolean,value:function(){return loadTimeData.getBoolean("showMenuPromo")}}},listeners:{"cr-toolbar-menu-promo-close":"onCrToolbarMenuPromoClose_","cr-toolbar-menu-promo-shown":"onCrToolbarMenuPromoShown_","cr-toolbar-menu-tap":"onCrToolbarMenuTap_","delete-selected":"deleteSelected","history-checkbox-select":"checkboxSelected","history-close-drawer":"closeDrawer_","history-view-changed":"historyViewChanged_","opened-changed":"onOpenedChanged_","unselect-all":"unselectAll"},boundOnCanExecute_:null,boundOnCommand_:null,attached:function(){this.grouped_=loadTimeData.getBoolean("groupByDomain");cr.ui.decorate("command",cr.ui.Command);this.boundOnCanExecute_=this.onCanExecute_.bind(this);this.boundOnCommand_=this.onCommand_.bind(this);document.addEventListener("canExecute",this.boundOnCanExecute_);document.addEventListener("command",this.boundOnCommand_)},detached:function(){document.removeEventListener("canExecute",this.boundOnCanExecute_);document.removeEventListener("command",this.boundOnCommand_)},onFirstRender:function(){setTimeout(function(){chrome.send("metricsHandler:recordTime",["History.ResultsRenderedTime",window.performance.now()])});var searchField=this.$.toolbar.searchField;if(!searchField.narrow){searchField.getSearchInput().focus()}md_history.ensureLazyLoaded().then(function(){window.requestIdleCallback(function(){document.fonts.load("bold 12px Roboto")})})},_scrollHandler:function(){if(this.scrollTarget)this.toolbarShadow_=this.scrollTarget.scrollTop!=0},onCrToolbarMenuPromoClose_:function(){this.showMenuPromo_=false},onCrToolbarMenuPromoShown_:function(){md_history.BrowserService.getInstance().menuPromoShown()},onCrToolbarMenuTap_:function(){var drawer=this.$$("#drawer");if(drawer)drawer.toggle()},onOpenedChanged_:function(e){if(e.detail.value)this.showMenuPromo_=false},checkboxSelected:function(e){var toolbar=this.$.toolbar;toolbar.count=this.$.history.getSelectedItemCount()},unselectAll:function(){var listContainer=this.$.history;var toolbar=this.$.toolbar;listContainer.unselectAllItems(toolbar.count);toolbar.count=0},deleteSelected:function(){this.$.history.deleteSelectedWithPrompt()},historyResult:function(info,results){this.set("queryState_.querying",false);this.set("queryResult_.info",info);this.set("queryResult_.results",results);var listContainer=this.$["history"];listContainer.historyResult(info,results)},focusToolbarSearchField:function(){this.$.toolbar.showSearchField()},onCanExecute_:function(e){e=e;switch(e.command.id){case"find-command":case"toggle-grouped":e.canExecute=true;break;case"slash-command":e.canExecute=!this.$.toolbar.searchField.isSearchFocused();break;case"delete-command":e.canExecute=this.$.toolbar.count>0;break}},onCommand_:function(e){if(e.command.id=="find-command"||e.command.id=="slash-command")this.focusToolbarSearchField();if(e.command.id=="delete-command")this.deleteSelected();if(e.command.id=="toggle-grouped")this.grouped_=!this.grouped_},setForeignSessions:function(sessionList){this.set("queryResult_.sessionList",sessionList)},historyDeleted:function(){this.$.history.historyDeleted()},updateSignInState:function(isUserSignedIn){this.isUserSignedIn_=isUserSignedIn},syncedTabsSelected_:function(selectedPage){return selectedPage=="syncedTabs"},shouldShowSpinner_:function(querying,incremental,searchTerm){return querying&&!incremental&&searchTerm!=""},showSyncNotice_:function(hasSyncedResults,selectedPage){return hasSyncedResults&&selectedPage!="syncedTabs"},selectedPageChanged_:function(){this.unselectAll();this.historyViewChanged_()},historyViewChanged_:function(){requestAnimationFrame(function(){md_history.ensureLazyLoaded().then(function(){if(!this.$.content.selectedItem)return;this.scrollTarget=this.$.content.selectedItem.getContentScrollTarget();this._scrollHandler()}.bind(this))}.bind(this));this.recordHistoryPageView_()},getSelectedPage_:function(selectedPage,items){return selectedPage},closeDrawer_:function(){var drawer=this.$$("#drawer");if(drawer)drawer.close()},recordHistoryPageView_:function(){var histogramValue=HistoryPageViewHistogram.END;switch(this.selectedPage_){case"syncedTabs":histogramValue=this.isUserSignedIn_?HistoryPageViewHistogram.SYNCED_TABS:HistoryPageViewHistogram.SIGNIN_PROMO;break;default:switch(this.queryState_.range){case HistoryRange.ALL_TIME:histogramValue=HistoryPageViewHistogram.HISTORY;break;case HistoryRange.WEEK:histogramValue=HistoryPageViewHistogram.GROUPED_WEEK;break;case HistoryRange.MONTH:histogramValue=HistoryPageViewHistogram.GROUPED_MONTH;break}break}md_history.BrowserService.getInstance().recordHistogram("History.HistoryPageView",histogramValue,HistoryPageViewHistogram.END)}});
\ No newline at end of file
diff --git a/chrome/browser/resources/md_history/app.js b/chrome/browser/resources/md_history/app.js
index 3c26c37..2a39a590 100644
--- a/chrome/browser/resources/md_history/app.js
+++ b/chrome/browser/resources/md_history/app.js
@@ -266,21 +266,8 @@
   /**
    * @param {!Array<!ForeignSession>} sessionList Array of objects describing
    *     the sessions from other devices.
-   * @param {boolean} isTabSyncEnabled Is tab sync enabled for this profile?
    */
-  setForeignSessions: function(sessionList, isTabSyncEnabled) {
-    if (!isTabSyncEnabled) {
-      var syncedDeviceManagerElem =
-      /** @type {HistorySyncedDeviceManagerElement} */this
-          .$$('history-synced-device-manager');
-      if (syncedDeviceManagerElem) {
-        md_history.ensureLazyLoaded().then(function() {
-          syncedDeviceManagerElem.tabSyncDisabled();
-        });
-      }
-      return;
-    }
-
+  setForeignSessions: function(sessionList) {
     this.set('queryResult_.sessionList', sessionList);
   },
 
diff --git a/chrome/browser/resources/md_history/history.js b/chrome/browser/resources/md_history/history.js
index 522e8f9..cecf808 100644
--- a/chrome/browser/resources/md_history/history.js
+++ b/chrome/browser/resources/md_history/history.js
@@ -68,16 +68,14 @@
 /**
  * Receives the synced history data. An empty list means that either there are
  * no foreign sessions, or tab sync is disabled for this profile.
- * |isTabSyncEnabled| makes it possible to distinguish between the cases.
  *
  * @param {!Array<!ForeignSession>} sessionList Array of objects describing the
  *     sessions from other devices.
- * @param {boolean} isTabSyncEnabled Is tab sync enabled for this profile?
  */
-function setForeignSessions(sessionList, isTabSyncEnabled) {
+function setForeignSessions(sessionList) {
   waitForAppUpgrade().then(function() {
     /** @type {HistoryAppElement} */($('history-app'))
-        .setForeignSessions(sessionList, isTabSyncEnabled);
+        .setForeignSessions(sessionList);
   });
 }
 
diff --git a/chrome/browser/resources/md_history/lazy_load.crisper.js b/chrome/browser/resources/md_history/lazy_load.crisper.js
index b3f8a172e..8fc66fd 100644
--- a/chrome/browser/resources/md_history/lazy_load.crisper.js
+++ b/chrome/browser/resources/md_history/lazy_load.crisper.js
@@ -19,7 +19,7 @@
 // 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.
-var ForeignDeviceInternal;Polymer({is:"history-synced-device-manager",properties:{sessionList:{type:Array,observer:"updateSyncedDevices"},searchTerm:{type:String,observer:"searchTermChanged"},syncedDevices_:{type:Array,value:function(){return[]}},signInState:{type:Boolean,observer:"signInStateChanged_"},guestSession_:{type:Boolean,value:loadTimeData.getBoolean("isGuestSession")},fetchingSyncedTabs_:{type:Boolean,value:false},hasSeenForeignData_:Boolean},listeners:{"toggle-menu":"onToggleMenu_",scroll:"onListScroll_","update-focus-grid":"updateFocusGrid_"},focusGrid_:null,attached:function(){this.focusGrid_=new cr.ui.FocusGrid;chrome.send("otherDevicesInitialized");md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.INITIALIZED,SyncedTabsHistogram.LIMIT)},detached:function(){this.focusGrid_.destroy()},getContentScrollTarget:function(){return this},createInternalDevice_:function(session){var tabs=[];var separatorIndexes=[];for(var i=0;i<session.windows.length;i++){var windowId=session.windows[i].sessionId;var newTabs=session.windows[i].tabs;if(newTabs.length==0)continue;newTabs.forEach(function(tab){tab.windowId=windowId});var windowAdded=false;if(!this.searchTerm){tabs=tabs.concat(newTabs);windowAdded=true}else{var searchText=this.searchTerm.toLowerCase();for(var j=0;j<newTabs.length;j++){var tab=newTabs[j];if(tab.title.toLowerCase().indexOf(searchText)!=-1){tabs.push(tab);windowAdded=true}}}if(windowAdded&&i!=session.windows.length-1)separatorIndexes.push(tabs.length-1)}return{device:session.name,lastUpdateTime:"– "+session.modifiedTime,opened:true,separatorIndexes:separatorIndexes,timestamp:session.timestamp,tabs:tabs,tag:session.tag}},onSignInTap_:function(){chrome.send("startSignInFlow")},onListScroll_:function(){var menu=this.$.menu.getIfExists();if(menu)menu.closeMenu()},onToggleMenu_:function(e){var menu=this.$.menu.get();menu.toggleMenu(e.detail.target,e.detail.tag);if(menu.menuOpen){md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.SHOW_SESSION_MENU,SyncedTabsHistogram.LIMIT)}},onOpenAllTap_:function(){var menu=assert(this.$.menu.getIfExists());var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.OPEN_ALL,SyncedTabsHistogram.LIMIT);browserService.openForeignSessionAllTabs(menu.itemData);menu.closeMenu()},updateFocusGrid_:function(){if(!this.focusGrid_)return;this.focusGrid_.destroy();this.debounce("updateFocusGrid",function(){Polymer.dom(this.root).querySelectorAll("history-synced-device-card").reduce(function(prev,cur){return prev.concat(cur.createFocusRows())},[]).forEach(function(row){this.focusGrid_.addRow(row)}.bind(this));this.focusGrid_.ensureRowActive()})},onDeleteSessionTap_:function(){var menu=assert(this.$.menu.getIfExists());var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.HIDE_FOR_NOW,SyncedTabsHistogram.LIMIT);browserService.deleteForeignSession(menu.itemData);menu.closeMenu()},clearDisplayedSyncedDevices_:function(){this.syncedDevices_=[]},showNoSyncedMessage:function(signInState,syncedDevicesLength,guestSession){if(guestSession)return true;return signInState&&syncedDevicesLength==0},showSignInGuide:function(signInState,guestSession){var show=!signInState&&!guestSession;if(show){md_history.BrowserService.getInstance().recordAction("Signin_Impression_FromRecentTabs")}return show},noSyncedTabsMessage:function(){var stringName=this.fetchingSyncedTabs_?"loading":"noSyncedResults";if(this.searchTerm!=="")stringName="noSearchResults";return loadTimeData.getString(stringName)},updateSyncedDevices:function(sessionList){this.fetchingSyncedTabs_=false;if(!sessionList)return;if(sessionList.length>0&&!this.hasSeenForeignData_){this.hasSeenForeignData_=true;md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.HAS_FOREIGN_DATA,SyncedTabsHistogram.LIMIT)}var devices=[];sessionList.forEach(function(session){var device=this.createInternalDevice_(session);if(device.tabs.length!=0)devices.push(device)}.bind(this));this.syncedDevices_=devices},tabSyncDisabled:function(){this.fetchingSyncedTabs_=false;this.clearDisplayedSyncedDevices_()},signInStateChanged_:function(){this.fire("history-view-changed");if(!this.signInState){this.clearDisplayedSyncedDevices_();return}this.fetchingSyncedTabs_=true},searchTermChanged:function(searchTerm){this.clearDisplayedSyncedDevices_();this.updateSyncedDevices(this.sessionList)}});
+var ForeignDeviceInternal;Polymer({is:"history-synced-device-manager",properties:{sessionList:{type:Array,observer:"updateSyncedDevices"},searchTerm:{type:String,observer:"searchTermChanged"},syncedDevices_:{type:Array,value:function(){return[]}},signInState:{type:Boolean,observer:"signInStateChanged_"},guestSession_:{type:Boolean,value:loadTimeData.getBoolean("isGuestSession")},fetchingSyncedTabs_:{type:Boolean,value:false},hasSeenForeignData_:Boolean},listeners:{"toggle-menu":"onToggleMenu_",scroll:"onListScroll_","update-focus-grid":"updateFocusGrid_"},focusGrid_:null,attached:function(){this.focusGrid_=new cr.ui.FocusGrid;chrome.send("otherDevicesInitialized");md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.INITIALIZED,SyncedTabsHistogram.LIMIT)},detached:function(){this.focusGrid_.destroy()},getContentScrollTarget:function(){return this},createInternalDevice_:function(session){var tabs=[];var separatorIndexes=[];for(var i=0;i<session.windows.length;i++){var windowId=session.windows[i].sessionId;var newTabs=session.windows[i].tabs;if(newTabs.length==0)continue;newTabs.forEach(function(tab){tab.windowId=windowId});var windowAdded=false;if(!this.searchTerm){tabs=tabs.concat(newTabs);windowAdded=true}else{var searchText=this.searchTerm.toLowerCase();for(var j=0;j<newTabs.length;j++){var tab=newTabs[j];if(tab.title.toLowerCase().indexOf(searchText)!=-1){tabs.push(tab);windowAdded=true}}}if(windowAdded&&i!=session.windows.length-1)separatorIndexes.push(tabs.length-1)}return{device:session.name,lastUpdateTime:"– "+session.modifiedTime,opened:true,separatorIndexes:separatorIndexes,timestamp:session.timestamp,tabs:tabs,tag:session.tag}},onSignInTap_:function(){chrome.send("startSignInFlow")},onListScroll_:function(){var menu=this.$.menu.getIfExists();if(menu)menu.closeMenu()},onToggleMenu_:function(e){var menu=this.$.menu.get();menu.toggleMenu(e.detail.target,e.detail.tag);if(menu.menuOpen){md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.SHOW_SESSION_MENU,SyncedTabsHistogram.LIMIT)}},onOpenAllTap_:function(){var menu=assert(this.$.menu.getIfExists());var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.OPEN_ALL,SyncedTabsHistogram.LIMIT);browserService.openForeignSessionAllTabs(menu.itemData);menu.closeMenu()},updateFocusGrid_:function(){if(!this.focusGrid_)return;this.focusGrid_.destroy();this.debounce("updateFocusGrid",function(){Polymer.dom(this.root).querySelectorAll("history-synced-device-card").reduce(function(prev,cur){return prev.concat(cur.createFocusRows())},[]).forEach(function(row){this.focusGrid_.addRow(row)}.bind(this));this.focusGrid_.ensureRowActive()})},onDeleteSessionTap_:function(){var menu=assert(this.$.menu.getIfExists());var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.HIDE_FOR_NOW,SyncedTabsHistogram.LIMIT);browserService.deleteForeignSession(menu.itemData);menu.closeMenu()},clearDisplayedSyncedDevices_:function(){this.syncedDevices_=[]},showNoSyncedMessage:function(signInState,syncedDevicesLength,guestSession){if(guestSession)return true;return signInState&&syncedDevicesLength==0},showSignInGuide:function(signInState,guestSession){var show=!signInState&&!guestSession;if(show){md_history.BrowserService.getInstance().recordAction("Signin_Impression_FromRecentTabs")}return show},noSyncedTabsMessage:function(){var stringName=this.fetchingSyncedTabs_?"loading":"noSyncedResults";if(this.searchTerm!=="")stringName="noSearchResults";return loadTimeData.getString(stringName)},updateSyncedDevices:function(sessionList){this.fetchingSyncedTabs_=false;if(!sessionList)return;if(sessionList.length>0&&!this.hasSeenForeignData_){this.hasSeenForeignData_=true;md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.HAS_FOREIGN_DATA,SyncedTabsHistogram.LIMIT)}var devices=[];sessionList.forEach(function(session){var device=this.createInternalDevice_(session);if(device.tabs.length!=0)devices.push(device)}.bind(this));this.syncedDevices_=devices},signInStateChanged_:function(){this.fire("history-view-changed");if(!this.signInState){this.clearDisplayedSyncedDevices_();return}this.fetchingSyncedTabs_=true},searchTermChanged:function(searchTerm){this.clearDisplayedSyncedDevices_();this.updateSyncedDevices(this.sessionList)}});
 // 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.
diff --git a/chrome/browser/resources/md_history/synced_device_manager.js b/chrome/browser/resources/md_history/synced_device_manager.js
index 8ad6a70..6dd7f78 100644
--- a/chrome/browser/resources/md_history/synced_device_manager.js
+++ b/chrome/browser/resources/md_history/synced_device_manager.js
@@ -267,14 +267,6 @@
   },
 
   /**
-   * End fetching synced tabs when sync is disabled.
-   */
-  tabSyncDisabled: function() {
-    this.fetchingSyncedTabs_ = false;
-    this.clearDisplayedSyncedDevices_();
-  },
-
-  /**
    * Get called when user's sign in state changes, this will affect UI of synced
    * tabs page. Sign in promo gets displayed when user is signed out, and
    * different messages are shown when there are no synced tabs.
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html
index ab992e5..3856ef2 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html
@@ -91,7 +91,7 @@
                 scrollable>
               <iron-list items="[[getUnpaired_(deviceList.*)]]"
                   selection-enabled selected-item="{{selectedItem}}"
-                  scroll-target="container" tabindex="0">
+                  scroll-target="container">
                 <template>
                   <bluetooth-device-list-item device="[[item]]"
                       tabindex$="[[tabIndex]]">
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
index 4d5c64a8..5560b673e 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
@@ -39,17 +39,18 @@
     </style>
     <settings-animated-pages id="pages" section="bluetooth">
       <neon-animatable route-path="default">
-        <div class="settings-box first">
+        <div class="settings-box first"
+            on-tap="toggleDeviceListExpanded_">
           <iron-icon icon="settings:bluetooth"></iron-icon>
           <span class="middle">$i18n{bluetoothEnable}</span>
           <cr-expand-button id="expandListButton"
               alt="$i18n{bluetoothExpandA11yLabel}"
-              hidden$="[[!bluetoothEnabled]]"
-              expanded="{{deviceListExpanded}}">
+              hidden$="[[!bluetoothEnabled_]]"
+              expanded="{{deviceListExpanded_}}">
           </cr-expand-button>
           <paper-toggle-button id="enableBluetooth"
-              checked="{{bluetoothEnabled}}"
-              disabled="[[!adapterState.available]]"
+              checked="{{bluetoothEnabled_}}"
+              disabled="[[!adapterState_.available]]"
               on-change="onBluetoothEnabledChange_">
           </paper-toggle-button>
           <cr-policy-pref-indicator
@@ -57,13 +58,13 @@
               hidden="[[prefs.cros.device.allow_bluetooth.value]]">
           </cr-policy-pref-indicator>
         </div>
-        <iron-collapse opened="[[canShowDeviceList_(bluetoothEnabled,
-                                                    deviceListExpanded)]]">
+        <iron-collapse opened="[[canShowDeviceList_(bluetoothEnabled_,
+                                                    deviceListExpanded_)]]">
           <div id="deviceList" class="list-frame vertical-list"
               on-device-event="onDeviceEvent_">
             <div id="container" class="layout vertical" scrollable>
-              <iron-list items="[[getPairedOrConnecting_(deviceList.*)]]"
-                  selection-enabled selected-item="{{selectedItem}}"
+              <iron-list items="[[getPairedOrConnecting_(deviceList_.*)]]"
+                  selection-enabled selected-item="{{selectedItem_}}"
                   scroll-target="container">
                 <template>
                   <bluetooth-device-list-item device="[[item]]"
@@ -73,11 +74,11 @@
               </iron-list>
             </div>
             <div class="no-devices layout horizontal center"
-                hidden$="[[haveDevices_(deviceList.*)]]">
+                hidden$="[[haveDevices_(deviceList_.*)]]">
               $i18n{bluetoothNoDevices}
             </div>
           </div>
-          <div class="settings-box" hidden$="[[!bluetoothEnabled]]">
+          <div class="settings-box" hidden$="[[!bluetoothEnabled_]]">
             <paper-button class="primary-button" on-tap="onAddDeviceTap_">
               $i18n{bluetoothAddDevice}
             </paper-button>
@@ -86,17 +87,17 @@
       </neon-animatable>
     </settings-animated-pages>
 
-    <template is="dom-if" if="[[deviceListExpanded]]">
+    <template is="dom-if" if="[[deviceListExpanded_]]">
       <bluetooth-device-dialog id="deviceDialog"
-          adapter-state="[[adapterState]]"
-          device-list="[[deviceList]]"
-          dialog-id="[[dialogId]]"
+          adapter-state="[[adapterState_]]"
+          device-list="[[deviceList_]]"
+          dialog-id="[[dialogId_]]"
           on-close="onDialogClosed_"
-          error-message="[[errorMessage]]"
+          error-message="[[errorMessage_]]"
           on-device-event="onDeviceEvent_"
           on-response="onResponse_"
-          pairing-device="[[pairingDevice]]"
-          pairing-event="[[pairingEvent]]">
+          pairing-device="[[pairingDevice_]]"
+          pairing-event="[[pairingEvent_]]">
       </bluetooth-device-dialog>
     </template>
 
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
index e6a6e6d..3c17c62 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
@@ -35,15 +35,21 @@
   behaviors: [I18nBehavior, CrScrollableBehavior],
 
   properties: {
-    /** Whether bluetooth is enabled. */
-    bluetoothEnabled: {
+    /** Preferences state. */
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+
+    /** @private */
+    bluetoothEnabled_: {
       type: Boolean,
       value: false,
       observer: 'bluetoothEnabledChanged_',
     },
 
-    /** Whether the device list is expanded. */
-    deviceListExpanded: {
+    /** @private */
+    deviceListExpanded_: {
       type: Boolean,
       value: false,
     },
@@ -51,14 +57,16 @@
     /**
      * The cached bluetooth adapter state.
      * @type {!chrome.bluetooth.AdapterState|undefined}
+     * @private
      */
-    adapterState: Object,
+    adapterState_: Object,
 
     /**
      * The ordered list of bluetooth devices.
      * @type {!Array<!chrome.bluetooth.Device>}
+     * @private
      */
-    deviceList: {
+    deviceList_: {
       type: Array,
       value: function() {
         return [];
@@ -68,8 +76,9 @@
     /**
      * Reflects the iron-list selecteditem property.
      * @type {!chrome.bluetooth.Device}
+     * @private
      */
-    selectedItem: {
+    selectedItem_: {
       type: Object,
       observer: 'selectedItemChanged_',
     },
@@ -80,33 +89,34 @@
      * 'pairDevice', or 'connectError'. This allows a seamless transition
      * between dialogs. Note: This property should be set before opening the
      * dialog and setting the property will not itself cause the dialog to open.
+     * @private
      */
-    dialogId: String,
+    dialogId_: String,
 
     /**
      * Current Pairing device.
      * @type {?chrome.bluetooth.Device|undefined}
+     * @private
      */
-    pairingDevice: Object,
+    pairingDevice_: Object,
 
     /**
      * Current Pairing event.
      * @type {?chrome.bluetoothPrivate.PairingEvent|undefined}
+     * @private
      */
-    pairingEvent: Object,
+    pairingEvent_: Object,
 
-    /** The translated error message to show when a connect error occurs. */
-    errorMessage: String,
-
-    /** Preferences state. */
-    prefs: {
-      type: Object,
-      notify: true,
-    },
+    /**
+     * The translated error message to show when a connect error occurs.
+     * @private
+     */
+    errorMessage_: String,
 
     /**
      * Interface for bluetooth calls. May be overriden by tests.
      * @type {Bluetooth}
+     * @private
      */
     bluetooth: {
       type: Object,
@@ -116,6 +126,7 @@
     /**
      * Interface for bluetoothPrivate calls. May be overriden by tests.
      * @type {BluetoothPrivate}
+     * @private
      */
     bluetoothPrivate: {
       type: Object,
@@ -203,38 +214,41 @@
   /** @private */
   bluetoothEnabledChanged_: function() {
     // When bluetooth is enabled, auto-expand the device list.
-    if (this.bluetoothEnabled)
-      this.deviceListExpanded = true;
+    if (this.bluetoothEnabled_)
+      this.deviceListExpanded_ = true;
   },
 
   /** @private */
   selectedItemChanged_: function() {
-    if (this.selectedItem)
-      this.connectDevice_(this.selectedItem);
+    if (this.selectedItem_)
+      this.connectDevice_(this.selectedItem_);
+  },
+
+  /** @private */
+  toggleDeviceListExpanded_: function() {
+    this.deviceListExpanded_ = !this.deviceListExpanded_;
   },
 
   /**
-   * @param {boolean} bluetoothEnabled
-   * @param {boolean} deviceListExpanded
    * @return {boolean} Whether the <iron-collapse> can be shown.
    * @private
    */
-  canShowDeviceList_: function(bluetoothEnabled, deviceListExpanded) {
-    return bluetoothEnabled && deviceListExpanded;
+  canShowDeviceList_: function() {
+    return this.bluetoothEnabled_ && this.deviceListExpanded_;
   },
 
   /**
    * If bluetooth is enabled, request the complete list of devices and update
-   * |deviceList|.
+   * this.deviceList_.
    * @private
    */
   updateDeviceList_: function() {
-    if (!this.bluetoothEnabled) {
-      this.deviceList = [];
+    if (!this.bluetoothEnabled_) {
+      this.deviceList_ = [];
       return;
     }
     this.bluetooth.getDevices(function(devices) {
-      this.deviceList = devices;
+      this.deviceList_ = devices;
       this.updateScrollableContents();
     }.bind(this));
   },
@@ -245,7 +259,7 @@
    */
   onBluetoothEnabledChange_: function() {
     this.bluetoothPrivate.setAdapterState(
-        {powered: this.bluetoothEnabled}, function() {
+        {powered: this.bluetoothEnabled_}, function() {
           if (chrome.runtime.lastError) {
             console.error(
                 'Error enabling bluetooth: ' +
@@ -260,8 +274,8 @@
    * @private
    */
   onBluetoothAdapterStateChanged_: function(state) {
-    this.adapterState = state;
-    this.bluetoothEnabled = state.powered;
+    this.adapterState_ = state;
+    this.bluetoothEnabled_ = state.powered;
     this.updateDeviceList_();
   },
 
@@ -272,16 +286,16 @@
    */
   onBluetoothDeviceUpdated_: function(device) {
     var address = device.address;
-    if (this.dialogId && this.pairingDevice &&
-        this.pairingDevice.address == address) {
-      this.pairingDevice = device;
+    if (this.dialogId_ && this.pairingDevice_ &&
+        this.pairingDevice_.address == address) {
+      this.pairingDevice_ = device;
     }
     var index = this.getDeviceIndex_(address);
     if (index >= 0) {
-      this.set('deviceList.' + index, device);
+      this.set('deviceList_.' + index, device);
       return;
     }
-    this.push('deviceList', device);
+    this.push('deviceList_', device);
   },
 
   /**
@@ -294,12 +308,12 @@
     var index = this.getDeviceIndex_(address);
     if (index < 0)
       return;
-    this.splice('deviceList', index, 1);
+    this.splice('deviceList_', index, 1);
   },
 
   /** @private */
   startDiscovery_: function() {
-    if (!this.adapterState || this.adapterState.discovering)
+    if (!this.adapterState_ || this.adapterState_.discovering)
       return;
 
     if (!this.bluetoothPrivateOnPairingListener_) {
@@ -323,7 +337,7 @@
 
   /** @private */
   stopDiscovery_: function() {
-    if (!this.get('adapterState.discovering'))
+    if (!this.get('adapterState_.discovering'))
       return;
 
     if (this.bluetoothPrivateOnPairingListener_) {
@@ -347,17 +361,17 @@
    * @private
    */
   onBluetoothPrivateOnPairing_: function(e) {
-    if (!this.dialogId || !this.pairingDevice ||
-        e.device.address != this.pairingDevice.address) {
+    if (!this.dialogId_ || !this.pairingDevice_ ||
+        e.device.address != this.pairingDevice_.address) {
       return;
     }
     if (e.pairing == chrome.bluetoothPrivate.PairingEventType.KEYS_ENTERED &&
-        e.passkey === undefined && this.pairingEvent) {
+        e.passkey === undefined && this.pairingEvent_) {
       // 'keysEntered' event might not include the updated passkey so preserve
       // the current one.
-      e.passkey = this.pairingEvent.passkey;
+      e.passkey = this.pairingEvent_.passkey;
     }
-    this.pairingEvent = e;
+    this.pairingEvent_ = e;
   },
 
   /** @private */
@@ -410,9 +424,9 @@
    * @private
    */
   getDeviceIndex_: function(address) {
-    var len = this.deviceList.length;
+    var len = this.deviceList_.length;
     for (var i = 0; i < len; ++i) {
-      if (this.deviceList[i].address == address)
+      if (this.deviceList_[i].address == address)
         return i;
     }
     return -1;
@@ -432,18 +446,17 @@
    * @private
    */
   getPairedOrConnecting_: function() {
-    return this.deviceList.filter(function(device) {
+    return this.deviceList_.filter(function(device) {
       return !!device.paired || !!device.connecting;
     });
   },
 
   /**
-   * @param {Object} deviceListChanges Changes to the deviceList Array.
    * @return {boolean} True if deviceList contains any paired devices.
    * @private
    */
-  haveDevices_: function(deviceListChanges) {
-    return this.deviceList.findIndex(function(d) {
+  haveDevices_: function() {
+    return this.deviceList_.findIndex(function(d) {
       return !!d.paired;
     }) != -1;
   },
@@ -456,8 +469,8 @@
     // If the device is not paired, show the pairing dialog.
     if (!device.paired) {
       // Set the pairing device and clear any pairing event.
-      this.pairingDevice = device;
-      this.pairingEvent = null;
+      this.pairingDevice_ = device;
+      this.pairingEvent_ = null;
 
       this.openDialog_('pairDevice');
     }
@@ -486,9 +499,9 @@
       var name = this.getDeviceName_(device);
       var id = 'bluetooth_connect_' + error;
       if (this.i18nExists(id)) {
-        this.errorMessage = this.i18n(id, name);
+        this.errorMessage_ = this.i18n(id, name);
       } else {
-        this.errorMessage = error;
+        this.errorMessage_ = error;
         console.error('Unexpected error connecting to: ' + name + ': ' + error);
       }
       this.openDialog_('connectError');
@@ -539,12 +552,12 @@
    * @private
    */
   openDialog_: function(dialogId) {
-    if (this.dialogId) {
+    if (this.dialogId_) {
       // Dialog already opened, just update the contents.
-      this.dialogId = dialogId;
+      this.dialogId_ = dialogId;
       return;
     }
-    this.dialogId = dialogId;
+    this.dialogId_ = dialogId;
     // Call flush so that the dialog gets sized correctly before it is opened.
     Polymer.dom.flush();
     var dialog = this.$$('#deviceDialog');
@@ -555,8 +568,8 @@
   /** @private */
   onDialogClosed_: function() {
     this.stopDiscovery_();
-    this.dialogId = '';
-    this.pairingDevice = null;
-    this.pairingEvent = null;
+    this.dialogId_ = '';
+    this.pairingDevice_ = null;
+    this.pairingEvent_ = null;
   },
 });
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js b/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js
index 8e243d3..5d3a865 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js
@@ -81,9 +81,11 @@
 
     /**
      * Handles tapping on the "Add address" button.
+     * @param {!Event} e The polymer event.
      * @private
      */
-    onAddAddressTap_: function() {
+    onAddAddressTap_: function(e) {
+      e.preventDefault();
       this.activeAddress = {};
       this.showAddressDialog_ = true;
     },
@@ -95,9 +97,11 @@
 
     /**
      * Handles tapping on the "Edit" address button.
+     * @param {!Event} e The polymer event.
      * @private
      */
-    onMenuEditAddressTap_: function() {
+    onMenuEditAddressTap_: function(e) {
+      e.preventDefault();
       if (this.activeAddress.metadata.isLocal)
         this.showAddressDialog_ = true;
       else
@@ -137,6 +141,7 @@
      * @private
      */
     onAddCreditCardTap_: function(e) {
+      e.preventDefault();
       var date = new Date();  // Default to current month/year.
       var expirationMonth = date.getMonth() + 1;  // Months are 0 based.
       this.activeCreditCard = {
@@ -153,9 +158,11 @@
 
     /**
      * Handles tapping on the "Edit" credit card button.
+     * @param {!Event} e The polymer event.
      * @private
      */
-    onMenuEditCreditCardTap_: function() {
+    onMenuEditCreditCardTap_: function(e) {
+      e.preventDefault();
       if (this.activeCreditCard.metadata.isLocal)
         this.showCreditCardDialog_ = true;
       else
diff --git a/chrome/browser/resources/settings/people_page/change_picture.html b/chrome/browser/resources/settings/people_page/change_picture.html
index b9f4508..cc0ee2f 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.html
+++ b/chrome/browser/resources/settings/people_page/change_picture.html
@@ -18,6 +18,7 @@
       #container {
         -webkit-margin-start: 16px;
         align-items: flex-start;
+        outline: none;
         padding-top: 16px;
       }
 
diff --git a/chrome/browser/resources/settings/people_page/change_picture.js b/chrome/browser/resources/settings/people_page/change_picture.js
index bb3f0489..0e18c64e 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.js
+++ b/chrome/browser/resources/settings/people_page/change_picture.js
@@ -240,7 +240,7 @@
 
   /**
    * Handler for when accessibility-specific keys are pressed.
-   * @param {!{detail: !{key: string}}} e
+   * @param {!{detail: !{key: string, keyboardEvent: Object}}} e
    */
   onKeysPress_: function(e) {
     if (!this.selectedItem_)
@@ -268,6 +268,7 @@
         } while (this.selectedItem_.hidden);
 
         this.lastSelectedImageType_ = this.selectedItem_.dataset.type;
+        e.detail.keyboardEvent.preventDefault();
         break;
 
       case 'down':
@@ -279,6 +280,7 @@
         } while (this.selectedItem_.hidden);
 
         this.lastSelectedImageType_ = this.selectedItem_.dataset.type;
+        e.detail.keyboardEvent.preventDefault();
         break;
 
       case 'enter':
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
index 32517dc..ca7b87e 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
@@ -49,6 +49,10 @@
           </div>
         </div>
         <div class="message-container">
+          <!--
+            "Chrome sync" is the Google Cloud Based services used for sync. Thus
+            this section uses the Chrome logo even for Chromium builds.
+          -->
           <div id="chrome-logo" class="logo"></div>
           <div>
             <div class="title">$i18n{syncConfirmationChromeSyncTitle}</div>
@@ -57,6 +61,10 @@
           </div>
         </div>
         <div class="message-container">
+          <!--
+            This section uses the Google logo even for Chromium builds as the
+            user can personalize their Google services from this screen.
+          -->
           <div id="googleg-logo" class="logo"></div>
           <div>
             <div class="title">
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index 593aed7..f57f188 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -216,7 +216,9 @@
 }
 
 void SearchIPCRouter::OnLogMostVisitedImpression(
-    int page_seq_no, int position, NTPLoggingTileSource tile_source) const {
+    int page_seq_no,
+    int position,
+    ntp_tiles::NTPTileSource tile_source) const {
   if (page_seq_no != commit_counter_)
     return;
 
@@ -229,7 +231,9 @@
 }
 
 void SearchIPCRouter::OnLogMostVisitedNavigation(
-    int page_seq_no, int position, NTPLoggingTileSource tile_source) const {
+    int page_seq_no,
+    int position,
+    ntp_tiles::NTPTileSource tile_source) const {
   if (page_seq_no != commit_counter_)
     return;
 
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index a097dbd0..b878018 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "chrome/common/search/instant_types.h"
 #include "chrome/common/search/ntp_logging_events.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/base/window_open_disposition.h"
@@ -58,12 +59,12 @@
     // Called to log an impression from a given provider on the New Tab Page.
     virtual void OnLogMostVisitedImpression(
         int position,
-        NTPLoggingTileSource tile_source) = 0;
+        ntp_tiles::NTPTileSource tile_source) = 0;
 
     // Called to log a navigation from a given provider on the New Tab Page.
     virtual void OnLogMostVisitedNavigation(
         int position,
-        NTPLoggingTileSource tile_source) = 0;
+        ntp_tiles::NTPTileSource tile_source) = 0;
 
     // Called when the page wants to paste the |text| (or the clipboard contents
     // if the |text| is empty) into the omnibox.
@@ -180,10 +181,10 @@
                   base::TimeDelta time) const;
   void OnLogMostVisitedImpression(int page_seq_no,
                                   int position,
-                                  NTPLoggingTileSource tile_source) const;
+                                  ntp_tiles::NTPTileSource tile_source) const;
   void OnLogMostVisitedNavigation(int page_seq_no,
                                   int position,
-                                  NTPLoggingTileSource tile_source) const;
+                                  ntp_tiles::NTPTileSource tile_source) const;
   void OnPasteAndOpenDropDown(int page_seq_no,
                               const base::string16& text) const;
   void OnChromeIdentityCheck(int page_seq_no,
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index 55cd978..6b183c0d 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -56,9 +56,9 @@
   MOCK_METHOD2(OnLogEvent, void(NTPLoggingEventType event,
                                 base::TimeDelta time));
   MOCK_METHOD2(OnLogMostVisitedImpression,
-               void(int position, NTPLoggingTileSource tile_source));
+               void(int position, ntp_tiles::NTPTileSource tile_source));
   MOCK_METHOD2(OnLogMostVisitedNavigation,
-               void(int position, NTPLoggingTileSource tile_source));
+               void(int position, ntp_tiles::NTPTileSource tile_source));
   MOCK_METHOD1(PasteIntoOmnibox, void(const base::string16&));
   MOCK_METHOD1(OnChromeIdentityCheck, void(const base::string16& identity));
   MOCK_METHOD0(OnHistorySyncCheck, void());
@@ -291,14 +291,16 @@
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
   EXPECT_CALL(*mock_delegate(),
-      OnLogMostVisitedImpression(3, NTPLoggingTileSource::SERVER)).Times(1);
+              OnLogMostVisitedImpression(
+                  3, ntp_tiles::NTPTileSource::SUGGESTIONS_SERVICE))
+      .Times(1);
   EXPECT_CALL(*policy, ShouldProcessLogEvent()).Times(1)
       .WillOnce(testing::Return(true));
 
   content::WebContents* contents = web_contents();
   OnMessageReceived(ChromeViewHostMsg_LogMostVisitedImpression(
       contents->GetRenderViewHost()->GetRoutingID(), GetSearchIPCRouterSeqNo(),
-      3, NTPLoggingTileSource::SERVER));
+      3, ntp_tiles::NTPTileSource::SUGGESTIONS_SERVICE));
 }
 
 TEST_F(SearchIPCRouterTest, ProcessLogMostVisitedNavigationMsg) {
@@ -306,14 +308,16 @@
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
   EXPECT_CALL(*mock_delegate(),
-      OnLogMostVisitedNavigation(3, NTPLoggingTileSource::SERVER)).Times(1);
+              OnLogMostVisitedNavigation(
+                  3, ntp_tiles::NTPTileSource::SUGGESTIONS_SERVICE))
+      .Times(1);
   EXPECT_CALL(*policy, ShouldProcessLogEvent()).Times(1)
       .WillOnce(testing::Return(true));
 
   content::WebContents* contents = web_contents();
   OnMessageReceived(ChromeViewHostMsg_LogMostVisitedNavigation(
       contents->GetRenderViewHost()->GetRoutingID(), GetSearchIPCRouterSeqNo(),
-      3, NTPLoggingTileSource::SERVER));
+      3, ntp_tiles::NTPTileSource::SUGGESTIONS_SERVICE));
 }
 
 TEST_F(SearchIPCRouterTest, ProcessChromeIdentityCheckMsg) {
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index fde20f8..4264cbd 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -387,7 +387,8 @@
 }
 
 void SearchTabHelper::OnLogMostVisitedImpression(
-    int position, NTPLoggingTileSource tile_source) {
+    int position,
+    ntp_tiles::NTPTileSource tile_source) {
 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
 #if !defined(OS_ANDROID)
   NTPUserDataLogger::GetOrCreateFromWebContents(
@@ -396,7 +397,8 @@
 }
 
 void SearchTabHelper::OnLogMostVisitedNavigation(
-    int position, NTPLoggingTileSource tile_source) {
+    int position,
+    ntp_tiles::NTPTileSource tile_source) {
 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
 #if !defined(OS_ANDROID)
   NTPUserDataLogger::GetOrCreateFromWebContents(
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 31b363c..d1a0bede 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/search/search_model.h"
 #include "chrome/common/search/instant_types.h"
 #include "chrome/common/search/ntp_logging_events.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/browser/reload_type.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -146,10 +147,12 @@
   void OnUndoMostVisitedDeletion(const GURL& url) override;
   void OnUndoAllMostVisitedDeletions() override;
   void OnLogEvent(NTPLoggingEventType event, base::TimeDelta time) override;
-  void OnLogMostVisitedImpression(int position,
-                                  NTPLoggingTileSource tile_source) override;
-  void OnLogMostVisitedNavigation(int position,
-                                  NTPLoggingTileSource tile_source) override;
+  void OnLogMostVisitedImpression(
+      int position,
+      ntp_tiles::NTPTileSource tile_source) override;
+  void OnLogMostVisitedNavigation(
+      int position,
+      ntp_tiles::NTPTileSource tile_source) override;
   void PasteIntoOmnibox(const base::string16& text) override;
   void OnChromeIdentityCheck(const base::string16& identity) override;
   void OnHistorySyncCheck() override;
diff --git a/chrome/browser/ui/search/search_tab_helper_unittest.cc b/chrome/browser/ui/search/search_tab_helper_unittest.cc
index bacf1d5..63dccd3 100644
--- a/chrome/browser/ui/search/search_tab_helper_unittest.cc
+++ b/chrome/browser/ui/search/search_tab_helper_unittest.cc
@@ -58,9 +58,9 @@
   MOCK_METHOD2(OnLogEvent, void(NTPLoggingEventType event,
                                 base::TimeDelta time));
   MOCK_METHOD2(OnLogMostVisitedImpression,
-               void(int position, NTPLoggingTileSource tile_source));
+               void(int position, ntp_tiles::NTPTileSource tile_source));
   MOCK_METHOD2(OnLogMostVisitedNavigation,
-               void(int position, NTPLoggingTileSource tile_source));
+               void(int position, ntp_tiles::NTPTileSource tile_source));
   MOCK_METHOD1(PasteIntoOmnibox, void(const base::string16&));
   MOCK_METHOD1(OnChromeIdentityCheck, void(const base::string16& identity));
   MOCK_METHOD0(OnHistorySyncCheck, void());
diff --git a/chrome/browser/ui/startup/default_browser_infobar_delegate.cc b/chrome/browser/ui/startup/default_browser_infobar_delegate.cc
index 5bc0e8c..e2b9f575 100644
--- a/chrome/browser/ui/startup/default_browser_infobar_delegate.cc
+++ b/chrome/browser/ui/startup/default_browser_infobar_delegate.cc
@@ -4,12 +4,13 @@
 
 #include "chrome/browser/ui/startup/default_browser_infobar_delegate.h"
 
+#include <memory>
+
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ui/startup/default_browser_prompt.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
 #include "components/infobars/core/infobar.h"
 #include "content/public/browser/user_metrics.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -17,24 +18,10 @@
 
 #if defined(OS_WIN)
 #include "base/win/windows_version.h"
-#include "components/variations/variations_associated_data.h"
-#include "ui/base/material_design/material_design_controller.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/paint_vector_icon.h"
 #endif
 
 namespace chrome {
 
-#if defined(OS_WIN)
-namespace {
-
-// Experiment where different styles for the default browser prompt are tested.
-constexpr char kDefaultBrowserPromptStyle[] = "DefaultBrowserPromptStyle";
-
-}  // namespace
-#endif
-
 bool IsStickyDefaultBrowserPromptEnabled() {
 #if defined(OS_WIN)
   // The flow to set the default browser is only asynchronous on Windows 10+.
@@ -83,13 +70,7 @@
 
 infobars::InfoBarDelegate::Type DefaultBrowserInfoBarDelegate::GetInfoBarType()
     const {
-#if defined(OS_WIN)
-  std::string infobar_type = variations::GetVariationParamValue(
-      kDefaultBrowserPromptStyle, "InfoBarType");
-  return (infobar_type == "PageAction") ? PAGE_ACTION_TYPE : WARNING_TYPE;
-#else
   return PAGE_ACTION_TYPE;
-#endif
 }
 
 infobars::InfoBarDelegate::InfoBarIdentifier
@@ -97,29 +78,10 @@
   return DEFAULT_BROWSER_INFOBAR_DELEGATE;
 }
 
-int DefaultBrowserInfoBarDelegate::GetIconId() const {
-  return IDR_PRODUCT_LOGO_32;
+gfx::VectorIconId DefaultBrowserInfoBarDelegate::GetVectorIconId() const {
+  return gfx::VectorIconId::PRODUCT;
 }
 
-#if defined(OS_WIN)
-gfx::Image DefaultBrowserInfoBarDelegate::GetIcon() const {
-  // Experiment for the chrome product icon used on the default browser
-  // prompt.
-  std::string icon_color = variations::GetVariationParamValue(
-      kDefaultBrowserPromptStyle, "IconColor");
-  if (icon_color == "Colored") {
-    return ResourceBundle::GetSharedInstance().GetNativeImageNamed(
-        IDR_PRODUCT_LOGO_16);
-  }
-  if ((icon_color == "Blue") && (GetInfoBarType() == WARNING_TYPE)) {
-    // WARNING_TYPE infobars use orange icons by default.
-    return gfx::Image(
-        gfx::CreateVectorIcon(GetVectorIconId(), 16, gfx::kGoogleBlue500));
-  }
-  return ConfirmInfoBarDelegate::GetIcon();
-}
-#endif  // defined(OS_WIN)
-
 bool DefaultBrowserInfoBarDelegate::ShouldExpire(
     const NavigationDetails& details) const {
   return should_expire_ && ConfirmInfoBarDelegate::ShouldExpire(details);
diff --git a/chrome/browser/ui/startup/default_browser_infobar_delegate.h b/chrome/browser/ui/startup/default_browser_infobar_delegate.h
index 26818da..50b4c81f 100644
--- a/chrome/browser/ui/startup/default_browser_infobar_delegate.h
+++ b/chrome/browser/ui/startup/default_browser_infobar_delegate.h
@@ -56,10 +56,7 @@
   // ConfirmInfoBarDelegate:
   Type GetInfoBarType() const override;
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
-  int GetIconId() const override;
-#if defined(OS_WIN)
-  gfx::Image GetIcon() const override;
-#endif  // defined(OS_WIN)
+  gfx::VectorIconId GetVectorIconId() const override;
   bool ShouldExpire(const NavigationDetails& details) const override;
   void InfoBarDismissed() override;
   base::string16 GetMessageText() const override;
diff --git a/chrome/browser/ui/webui/foreign_session_handler.cc b/chrome/browser/ui/webui/foreign_session_handler.cc
index 62f33c2f..df6d257 100644
--- a/chrome/browser/ui/webui/foreign_session_handler.cc
+++ b/chrome/browser/ui/webui/foreign_session_handler.cc
@@ -237,13 +237,6 @@
   HandleGetForeignSessions(nullptr);
 }
 
-bool ForeignSessionHandler::IsTabSyncEnabled() {
-  Profile* profile = Profile::FromWebUI(web_ui());
-  browser_sync::ProfileSyncService* service =
-      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
-  return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
-}
-
 base::string16 ForeignSessionHandler::FormatSessionTime(
     const base::Time& time) {
   // Return a time like "1 hour ago", "2 days ago", etc.
@@ -338,9 +331,7 @@
       session_list.Append(std::move(session_data));
     }
   }
-  base::FundamentalValue tab_sync_enabled(IsTabSyncEnabled());
-  web_ui()->CallJavascriptFunctionUnsafe("setForeignSessions", session_list,
-                                         tab_sync_enabled);
+  web_ui()->CallJavascriptFunctionUnsafe("setForeignSessions", session_list);
 }
 
 void ForeignSessionHandler::HandleOpenForeignSession(
diff --git a/chrome/browser/ui/webui/foreign_session_handler.h b/chrome/browser/ui/webui/foreign_session_handler.h
index a85312769..d41d0ec37 100644
--- a/chrome/browser/ui/webui/foreign_session_handler.h
+++ b/chrome/browser/ui/webui/foreign_session_handler.h
@@ -61,9 +61,6 @@
   void OnSyncConfigurationCompleted() override;
   void OnForeignSessionUpdated() override;
 
-  // Returns true if tab sync is enabled for this profile, otherwise false.
-  bool IsTabSyncEnabled();
-
   // Returns a string used to show the user when a session was last modified.
   base::string16 FormatSessionTime(const base::Time& time);
 
diff --git a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc
index 6e43620b..fcb0b59 100644
--- a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc
@@ -40,17 +40,6 @@
       sessions);
 }
 
-ntp_tiles::NTPTileSource ConvertTileSource(NTPLoggingTileSource tile_source) {
-  switch (tile_source) {
-    case NTPLoggingTileSource::CLIENT:
-      return ntp_tiles::NTPTileSource::TOP_SITES;
-    case NTPLoggingTileSource::SERVER:
-      return ntp_tiles::NTPTileSource::SUGGESTIONS_SERVICE;
-  }
-  NOTREACHED();
-  return ntp_tiles::NTPTileSource::TOP_SITES;
-}
-
 }  // namespace
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(NTPUserDataLogger);
@@ -109,7 +98,8 @@
 }
 
 void NTPUserDataLogger::LogMostVisitedImpression(
-    int position, NTPLoggingTileSource tile_source) {
+    int position,
+    ntp_tiles::NTPTileSource tile_source) {
   if ((position >= kNumMostVisited) || impression_was_logged_[position]) {
     return;
   }
@@ -118,8 +108,9 @@
 }
 
 void NTPUserDataLogger::LogMostVisitedNavigation(
-    int position, NTPLoggingTileSource tile_source) {
-  ntp_tiles::metrics::RecordTileClick(position, ConvertTileSource(tile_source),
+    int position,
+    ntp_tiles::NTPTileSource tile_source) {
+  ntp_tiles::metrics::RecordTileClick(position, tile_source,
                                       ntp_tiles::metrics::THUMBNAIL);
 
   // Records the action. This will be available as a time-stamped stream
@@ -172,10 +163,11 @@
     if (!impression_was_logged_[i]) {
       break;
     }
-    if (impression_tile_source_[i] == NTPLoggingTileSource::SERVER) {
+    if (impression_tile_source_[i] ==
+        ntp_tiles::NTPTileSource::SUGGESTIONS_SERVICE) {
       has_server_side_suggestions = true;
     }
-    tiles.emplace_back(ConvertTileSource(impression_tile_source_[i]),
+    tiles.emplace_back(impression_tile_source_[i],
                        ntp_tiles::metrics::THUMBNAIL);
   }
   ntp_tiles::metrics::RecordPageImpression(tiles);
diff --git a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h
index 17c2861..59e967c 100644
--- a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h
+++ b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "chrome/common/search/ntp_logging_events.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -39,10 +40,12 @@
   void LogEvent(NTPLoggingEventType event, base::TimeDelta time);
 
   // Logs an impression on one of the NTP tiles by a given source.
-  void LogMostVisitedImpression(int position, NTPLoggingTileSource tile_source);
+  void LogMostVisitedImpression(int position,
+                                ntp_tiles::NTPTileSource tile_source);
 
   // Logs a navigation on one of the NTP tiles by a given source.
-  void LogMostVisitedNavigation(int position, NTPLoggingTileSource tile_source);
+  void LogMostVisitedNavigation(int position,
+                                ntp_tiles::NTPTileSource tile_source);
 
  protected:
   explicit NTPUserDataLogger(content::WebContents* contents);
@@ -80,7 +83,7 @@
 
   // Stores the tile source for each impression. Entries are only valid if the
   // corresponding entry in |impression_was_logged_| is true.
-  std::vector<NTPLoggingTileSource> impression_tile_source_;
+  std::vector<ntp_tiles::NTPTileSource> impression_tile_source_;
 
   // Whether we have already emitted NTP stats for this web contents.
   bool has_emitted_;
diff --git a/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc b/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc
index 1dab261..46a3a1b 100644
--- a/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc
@@ -16,6 +16,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::Bucket;
+using ntp_tiles::NTPTileSource;
 using testing::ElementsAre;
 using testing::IsEmpty;
 
@@ -44,7 +45,7 @@
   base::TimeDelta delta = base::TimeDelta::FromMilliseconds(0);
 
   for (int i = 0; i < 8; ++i)
-    logger.LogMostVisitedImpression(i, NTPLoggingTileSource::SERVER);
+    logger.LogMostVisitedImpression(i, NTPTileSource::SUGGESTIONS_SERVICE);
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta);
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
               ElementsAre(Bucket(8, 1)));
@@ -75,24 +76,24 @@
   base::TimeDelta delta = base::TimeDelta::FromMilliseconds(0);
 
   // Impressions increment the associated bins.
-  logger.LogMostVisitedImpression(0, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(1, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(2, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(3, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(4, NTPLoggingTileSource::CLIENT);
-  logger.LogMostVisitedImpression(5, NTPLoggingTileSource::CLIENT);
-  logger.LogMostVisitedImpression(6, NTPLoggingTileSource::CLIENT);
-  logger.LogMostVisitedImpression(7, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedImpression(0, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(1, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(2, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(3, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(4, NTPTileSource::TOP_SITES);
+  logger.LogMostVisitedImpression(5, NTPTileSource::TOP_SITES);
+  logger.LogMostVisitedImpression(6, NTPTileSource::TOP_SITES);
+  logger.LogMostVisitedImpression(7, NTPTileSource::TOP_SITES);
 
   // Repeated impressions for the same bins are ignored.
-  logger.LogMostVisitedImpression(0, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(1, NTPLoggingTileSource::CLIENT);
-  logger.LogMostVisitedImpression(2, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(3, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedImpression(0, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(1, NTPTileSource::TOP_SITES);
+  logger.LogMostVisitedImpression(2, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(3, NTPTileSource::TOP_SITES);
 
   // Impressions are silently ignored for tiles >= 8.
-  logger.LogMostVisitedImpression(8, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(9, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedImpression(8, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(9, NTPTileSource::TOP_SITES);
 
   // The actual histograms are emitted only after the ALL_TILES_LOADED event, so
   // at this point everything should still be empty.
@@ -125,10 +126,10 @@
                                GURL("http://chromium.org"));
   logger.NavigatedFromURLToURL(GURL("http://chromium.org"),
                                GURL("chrome://newtab/"));
-  logger.LogMostVisitedImpression(0, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(1, NTPLoggingTileSource::CLIENT);
-  logger.LogMostVisitedImpression(2, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedImpression(3, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedImpression(0, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(1, NTPTileSource::TOP_SITES);
+  logger.LogMostVisitedImpression(2, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedImpression(3, NTPTileSource::TOP_SITES);
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta);
 
   EXPECT_THAT(
@@ -151,7 +152,7 @@
 
   TestNTPUserDataLogger logger;
 
-  logger.LogMostVisitedNavigation(0, NTPLoggingTileSource::SERVER);
+  logger.LogMostVisitedNavigation(0, NTPTileSource::SUGGESTIONS_SERVICE);
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
       ElementsAre(Bucket(0, 1)));
@@ -162,7 +163,7 @@
       histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
       IsEmpty());
 
-  logger.LogMostVisitedNavigation(1, NTPLoggingTileSource::SERVER);
+  logger.LogMostVisitedNavigation(1, NTPTileSource::SUGGESTIONS_SERVICE);
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
       ElementsAre(Bucket(0, 1), Bucket(1, 1)));
@@ -173,7 +174,7 @@
       histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
       IsEmpty());
 
-  logger.LogMostVisitedNavigation(2, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedNavigation(2, NTPTileSource::TOP_SITES);
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
       ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1)));
@@ -184,7 +185,7 @@
       histogram_tester.GetAllSamples("NewTabPage.MostVisited.client"),
       ElementsAre(Bucket(2, 1)));
 
-  logger.LogMostVisitedNavigation(3, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedNavigation(3, NTPTileSource::TOP_SITES);
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
       ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1), Bucket(3, 1)));
@@ -196,10 +197,10 @@
       ElementsAre(Bucket(2, 1), Bucket(3, 1)));
 
   // Navigations always increase.
-  logger.LogMostVisitedNavigation(0, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedNavigation(1, NTPLoggingTileSource::CLIENT);
-  logger.LogMostVisitedNavigation(2, NTPLoggingTileSource::SERVER);
-  logger.LogMostVisitedNavigation(3, NTPLoggingTileSource::CLIENT);
+  logger.LogMostVisitedNavigation(0, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedNavigation(1, NTPTileSource::TOP_SITES);
+  logger.LogMostVisitedNavigation(2, NTPTileSource::SUGGESTIONS_SERVICE);
+  logger.LogMostVisitedNavigation(3, NTPTileSource::TOP_SITES);
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.MostVisited"),
       ElementsAre(Bucket(0, 2), Bucket(1, 2), Bucket(2, 2), Bucket(3, 2)));
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 4b762851..de6f3b74 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -192,6 +192,7 @@
     "//components/metrics:net",
     "//components/nacl/common:process_type",
     "//components/nacl/common:switches",
+    "//components/ntp_tiles",
     "//components/offline_pages:switches",
     "//components/omnibox/common",
     "//components/password_manager/content/common:mojo_interfaces",
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index fe9e090..1a6e250a 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -17,6 +17,7 @@
   "+components/metrics/client_info.h",
   "+components/metrics/metrics_pref_names.h",
   "+components/nacl/common",
+  "+components/ntp_tiles",
   "+components/offline_pages/offline_page_feature.h",
   "+components/password_manager/core/common",
   "+components/policy/core/common",
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 40cfebb..ff49fe9 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -18,6 +18,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/common/browser_controls_state.h"
 #include "content/public/common/webplugininfo.h"
@@ -171,8 +172,8 @@
 IPC_ENUM_TRAITS_MAX_VALUE(NTPLoggingEventType,
                           NTP_EVENT_TYPE_LAST)
 
-IPC_ENUM_TRAITS_MAX_VALUE(NTPLoggingTileSource,
-                          NTPLoggingTileSource::LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(ntp_tiles::NTPTileSource,
+                          ntp_tiles::NTPTileSource::LAST)
 
 IPC_ENUM_TRAITS_MAX_VALUE(WebApplicationInfo::MobileCapable,
                           WebApplicationInfo::MOBILE_CAPABLE_APPLE)
@@ -537,14 +538,14 @@
 IPC_MESSAGE_ROUTED3(ChromeViewHostMsg_LogMostVisitedImpression,
                     int /* page_seq_no */,
                     int /* position */,
-                    NTPLoggingTileSource /* tile_source */)
+                    ntp_tiles::NTPTileSource /* tile_source */)
 
 // Logs a navigation on one of the Most Visited tile on the InstantExtended
 // New Tab Page.
 IPC_MESSAGE_ROUTED3(ChromeViewHostMsg_LogMostVisitedNavigation,
                     int /* page_seq_no */,
                     int /* position */,
-                    NTPLoggingTileSource /* tile_source */)
+                    ntp_tiles::NTPTileSource /* tile_source */)
 
 // The Instant page asks whether the user syncs its history.
 IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_HistorySyncCheck,
diff --git a/chrome/common/search/ntp_logging_events.h b/chrome/common/search/ntp_logging_events.h
index cbb1100..51893222 100644
--- a/chrome/common/search/ntp_logging_events.h
+++ b/chrome/common/search/ntp_logging_events.h
@@ -53,13 +53,4 @@
   NTP_EVENT_TYPE_LAST = NTP_ALL_TILES_LOADED
 };
 
-// The source of an NTP tile.
-// Note: Keep in sync with browser/resources/local_ntp/most_visited_single.js.
-// TODO(treib): Merge this into MostVisitedSource from components/ntp_tiles.
-enum class NTPLoggingTileSource {
-  CLIENT = 0,
-  SERVER = 1,
-  LAST = SERVER
-};
-
 #endif  // CHROME_COMMON_SEARCH_NTP_LOGGING_EVENTS_H_
diff --git a/chrome/renderer/searchbox/DEPS b/chrome/renderer/searchbox/DEPS
index 3daf8ae6..0309349 100644
--- a/chrome/renderer/searchbox/DEPS
+++ b/chrome/renderer/searchbox/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/favicon_base",
+  "+components/ntp_tiles",
 ]
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 53d21e9..6262ed56 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -267,13 +267,13 @@
 }
 
 void SearchBox::LogMostVisitedImpression(int position,
-                                         NTPLoggingTileSource tile_source) {
+                                         ntp_tiles::NTPTileSource tile_source) {
   render_view()->Send(new ChromeViewHostMsg_LogMostVisitedImpression(
       render_view()->GetRoutingID(), page_seq_no_, position, tile_source));
 }
 
 void SearchBox::LogMostVisitedNavigation(int position,
-                                         NTPLoggingTileSource tile_source) {
+                                         ntp_tiles::NTPTileSource tile_source) {
   render_view()->Send(new ChromeViewHostMsg_LogMostVisitedNavigation(
       render_view()->GetRoutingID(), page_seq_no_, position, tile_source));
 }
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index 51ea59b..50e89ec 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -13,6 +13,7 @@
 #include "chrome/common/search/instant_types.h"
 #include "chrome/common/search/ntp_logging_events.h"
 #include "chrome/renderer/instant_restricted_id_cache.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/renderer/render_view_observer.h"
 #include "content/public/renderer/render_view_observer_tracker.h"
@@ -54,10 +55,12 @@
   void LogEvent(NTPLoggingEventType event);
 
   // Sends ChromeViewHostMsg_LogMostVisitedImpression to the browser.
-  void LogMostVisitedImpression(int position, NTPLoggingTileSource tile_source);
+  void LogMostVisitedImpression(int position,
+                                ntp_tiles::NTPTileSource tile_source);
 
   // Sends ChromeViewHostMsg_LogMostVisitedNavigation to the browser.
-  void LogMostVisitedNavigation(int position, NTPLoggingTileSource tile_source);
+  void LogMostVisitedNavigation(int position,
+                                ntp_tiles::NTPTileSource tile_source);
 
   // Sends ChromeViewHostMsg_ChromeIdentityCheck to the browser.
   void CheckIsUserSignedInToChromeAs(const base::string16& identity);
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index 38ad0f9..fe57fb3 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -22,6 +22,7 @@
 #include "chrome/grit/renderer_resources.h"
 #include "chrome/renderer/searchbox/searchbox.h"
 #include "components/crx_file/id_util.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "content/public/renderer/render_view.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
@@ -1019,9 +1020,10 @@
 
   DVLOG(1) << render_view << " LogMostVisitedImpression";
 
-  if (args[1]->Uint32Value() <= static_cast<int>(NTPLoggingTileSource::LAST)) {
-    NTPLoggingTileSource tile_source =
-        static_cast<NTPLoggingTileSource>(args[1]->Uint32Value());
+  if (args[1]->Uint32Value() <=
+      static_cast<int>(ntp_tiles::NTPTileSource::LAST)) {
+    ntp_tiles::NTPTileSource tile_source =
+        static_cast<ntp_tiles::NTPTileSource>(args[1]->Uint32Value());
     SearchBox::Get(render_view)->LogMostVisitedImpression(
         args[0]->IntegerValue(), tile_source);
   }
@@ -1041,9 +1043,10 @@
 
   DVLOG(1) << render_view << " LogMostVisitedNavigation";
 
-  if (args[1]->Uint32Value() <= static_cast<int>(NTPLoggingTileSource::LAST)) {
-    NTPLoggingTileSource tile_source =
-        static_cast<NTPLoggingTileSource>(args[1]->Uint32Value());
+  if (args[1]->Uint32Value() <=
+      static_cast<int>(ntp_tiles::NTPTileSource::LAST)) {
+    ntp_tiles::NTPTileSource tile_source =
+        static_cast<ntp_tiles::NTPTileSource>(args[1]->Uint32Value());
     SearchBox::Get(render_view)->LogMostVisitedNavigation(
         args[0]->IntegerValue(), tile_source);
   }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c68c0e2..af7bdf0e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2165,7 +2165,7 @@
         "../browser/chromeos/app_mode/kiosk_app_update_service_browsertest.cc",
         "../browser/chromeos/app_mode/kiosk_crash_restore_browsertest.cc",
         "../browser/chromeos/arc/arc_session_manager_browsertest.cc",
-        "../browser/chromeos/arc/auth/arc_robot_auth_browsertest.cc",
+        "../browser/chromeos/arc/auth/arc_robot_auth_code_fetcher_browsertest.cc",
         "../browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc",
         "../browser/chromeos/attestation/attestation_policy_browsertest.cc",
         "../browser/chromeos/customization/customization_document_browsertest.cc",
diff --git a/chrome/test/chromedriver/VERSION b/chrome/test/chromedriver/VERSION
index 8bd2249..e90e35f 100644
--- a/chrome/test/chromedriver/VERSION
+++ b/chrome/test/chromedriver/VERSION
@@ -1 +1 @@
-2.25
+2.26
diff --git a/chrome/test/data/webui/md_history/history_metrics_test.js b/chrome/test/data/webui/md_history/history_metrics_test.js
index 1f30a10..c9c6e80 100644
--- a/chrome/test/data/webui/md_history/history_metrics_test.js
+++ b/chrome/test/data/webui/md_history/history_metrics_test.js
@@ -168,7 +168,7 @@
             ]
         ),
       ];
-      setForeignSessions(sessionList, true);
+      setForeignSessions(sessionList);
       return PolymerTest.flushTasks();
     }).then(() => {
       assertEquals(1, histogram[SyncedTabsHistogram.HAS_FOREIGN_DATA]);
diff --git a/chrome/test/data/webui/md_history/history_synced_tabs_test.js b/chrome/test/data/webui/md_history/history_synced_tabs_test.js
index c4dcdda..e7949b0 100644
--- a/chrome/test/data/webui/md_history/history_synced_tabs_test.js
+++ b/chrome/test/data/webui/md_history/history_synced_tabs_test.js
@@ -420,48 +420,3 @@
     registerMessageCallback('deleteForeignSession', this, undefined);
   });
 });
-
-suite('<history-synced-device-manager> integration', function() {
-  var element;
-
-  setup(function() {
-    var app = replaceApp();
-    // Not rendered until selected.
-    assertEquals(null, app.$$('#synced-devices'));
-
-    app.selectedPage_ = 'syncedTabs';
-    assertEquals('syncedTabs', app.$['content-side-bar'].$.menu.selected);
-    return PolymerTest.flushTasks().then(function() {
-      element = app.$$('#synced-devices');
-      assertTrue(!!element);
-    });
-  });
-
-  test('enable and disable tab sync', function() {
-    updateSignInState(true);
-    var sessionList = [
-      createSession(
-          'Nexus 5',
-          [createWindow(['http://www.google.com', 'http://example.com'])]
-      )
-    ];
-    // Open tabs sync is enabled.
-    setForeignSessions(sessionList, true);
-
-    return PolymerTest.flushTasks().then(function() {
-      var cards = getCards(element);
-      assertEquals(1, cards.length);
-      assertTrue(element.$['no-synced-tabs'].hidden);
-
-      // Open tabs sync is disabled.
-      setForeignSessions(sessionList, false);
-
-      return PolymerTest.flushTasks();
-    }).then(function() {
-      cards = getCards(element);
-      assertEquals(0, cards.length);
-      // If tab sync is disabled, show 'no synced tabs'.
-      assertNoSyncedTabsMessageShown(element, 'noSyncedResults');
-    });
-  });
-});
diff --git a/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js b/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js
index ba47f20..9bc4ec64 100644
--- a/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js
@@ -99,7 +99,7 @@
       var bluetooth =
           bluetoothSection.querySelector('settings-bluetooth-page');
       assertTrue(!!bluetooth);
-      expectFalse(bluetooth.bluetoothEnabled);
+      expectFalse(bluetooth.bluetoothEnabled_);
       var enable = bluetooth.$.enableBluetooth;
       assertTrue(!!enable);
       expectFalse(enable.checked);
@@ -110,10 +110,10 @@
       expectTrue(enable.checked);
       expectTrue(self.bluetoothApi_.adapterState.powered);
       // Confirm that 'bluetoothEnabled' remains set to true.
-      expectTrue(bluetooth.bluetoothEnabled);
+      expectTrue(bluetooth.bluetoothEnabled_);
       // Set 'bluetoothEnabled' directly and confirm that the checkbox
       // toggles.
-      bluetooth.bluetoothEnabled = false;
+      bluetooth.bluetoothEnabled_ = false;
       Polymer.dom.flush();
       expectFalse(enable.checked);
     });
@@ -137,7 +137,7 @@
       // should be hidden.
       self.bluetoothApi_.setDevicesForTest(fakeDevices_);
       Polymer.dom.flush();
-      assertEquals(bluetooth.deviceList.length, 4);
+      assertEquals(bluetooth.deviceList_.length, 4);
       var devicesIronList = bluetooth.$$('#deviceList iron-list');
       assertTrue(!!devicesIronList);
       devicesIronList.notifyResize();
diff --git a/chromecast/base/pref_names.cc b/chromecast/base/pref_names.cc
index 1b679dbb..237f1336 100644
--- a/chromecast/base/pref_names.cc
+++ b/chromecast/base/pref_names.cc
@@ -4,6 +4,7 @@
 
 #include "chromecast/base/pref_names.h"
 
+namespace chromecast {
 namespace prefs {
 
 // Boolean which specifies if remote debugging is enabled
@@ -48,3 +49,4 @@
     "user_experience_metrics.stability.system_unclean_shutdowns";
 
 }  // namespace prefs
+}  // namespace chromecast
diff --git a/chromecast/base/pref_names.h b/chromecast/base/pref_names.h
index 2959717..7454885d 100644
--- a/chromecast/base/pref_names.h
+++ b/chromecast/base/pref_names.h
@@ -5,6 +5,7 @@
 #ifndef CHROMECAST_BASE_PREF_NAMES_H_
 #define CHROMECAST_BASE_PREF_NAMES_H_
 
+namespace chromecast {
 namespace prefs {
 
 extern const char kEnableRemoteDebugging[];
@@ -19,5 +20,6 @@
 extern const char kStabilitySystemUncleanShutdownCount[];
 
 }  // namespace prefs
+}  // namespace chromecast
 
 #endif  // CHROMECAST_BASE_PREF_NAMES_H_
diff --git a/chromeos/audio/audio_devices_pref_handler.h b/chromeos/audio/audio_devices_pref_handler.h
index deba1e6..09bb787 100644
--- a/chromeos/audio/audio_devices_pref_handler.h
+++ b/chromeos/audio/audio_devices_pref_handler.h
@@ -9,8 +9,6 @@
 #include "chromeos/audio/audio_pref_observer.h"
 #include "chromeos/chromeos_export.h"
 
-class PrefService;
-
 namespace chromeos {
 
 struct AudioDevice;
diff --git a/chromeos/audio/audio_pref_observer.h b/chromeos/audio/audio_pref_observer.h
index b183ebfe..f701fcdd 100644
--- a/chromeos/audio/audio_pref_observer.h
+++ b/chromeos/audio/audio_pref_observer.h
@@ -8,8 +8,6 @@
 #include "base/memory/ref_counted.h"
 #include "chromeos/chromeos_export.h"
 
-class PrefRegistrySimple;
-
 namespace chromeos {
 
 // Interface for observing audio preference changes.
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index 8383e9d1..a4f9cc3 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -22,9 +22,6 @@
 #include "chromeos/dbus/session_manager_client.h"
 #include "chromeos/dbus/volume_state.h"
 
-class PrefRegistrySimple;
-class PrefService;
-
 namespace chromeos {
 
 class AudioDevicesPrefHandler;
diff --git a/chromeos/dbus/cros_disks_client.h b/chromeos/dbus/cros_disks_client.h
index 9af1d86..f6d9ce2 100644
--- a/chromeos/dbus/cros_disks_client.h
+++ b/chromeos/dbus/cros_disks_client.h
@@ -21,7 +21,6 @@
 }
 
 namespace dbus {
-class MessageReader;
 class Response;
 }
 
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index 4824d85..380bdec4 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -19,7 +19,6 @@
 class ArcObbMounterClient;
 class AuthPolicyClient;
 class CrosDisksClient;
-class CryptohomeClient;
 class DebugDaemonClient;
 class EasyUnlockClient;
 class ImageBurnerClient;
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index ea0c69ba..e8b1287 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -19,7 +19,6 @@
 
 namespace dbus {
 class Bus;
-class ObjectPath;
 }  // namespace dbus
 
 namespace chromeos {
diff --git a/chromeos/dbus/fake_cras_audio_client.h b/chromeos/dbus/fake_cras_audio_client.h
index 4430853..0dc3c4e 100644
--- a/chromeos/dbus/fake_cras_audio_client.h
+++ b/chromeos/dbus/fake_cras_audio_client.h
@@ -13,8 +13,6 @@
 
 namespace chromeos {
 
-class CrasAudioHandlerTest;
-
 // The CrasAudioClient implementation used on Linux desktop.
 class CHROMEOS_EXPORT FakeCrasAudioClient : public CrasAudioClient {
  public:
diff --git a/chromeos/dbus/fake_shill_manager_client.h b/chromeos/dbus/fake_shill_manager_client.h
index 9bda9f1e..57b3a75 100644
--- a/chromeos/dbus/fake_shill_manager_client.h
+++ b/chromeos/dbus/fake_shill_manager_client.h
@@ -12,10 +12,6 @@
 #include "chromeos/chromeos_export.h"
 #include "chromeos/dbus/shill_manager_client.h"
 
-namespace net {
-class IPEndPoint;
-}
-
 namespace chromeos {
 
 // A fake implementation of ShillManagerClient. This works in close coordination
diff --git a/chromeos/dbus/mock_permission_broker_client.h b/chromeos/dbus/mock_permission_broker_client.h
index 2380a21..a7e22f10 100644
--- a/chromeos/dbus/mock_permission_broker_client.h
+++ b/chromeos/dbus/mock_permission_broker_client.h
@@ -10,10 +10,6 @@
 #include "chromeos/dbus/permission_broker_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-namespace dbus {
-class FileDescriptor;
-}  // namespace dbus
-
 namespace chromeos {
 
 class MockPermissionBrokerClient : public PermissionBrokerClient {
diff --git a/chromeos/dbus/services/liveness_service_provider.h b/chromeos/dbus/services/liveness_service_provider.h
index bfde7702..75092a3 100644
--- a/chromeos/dbus/services/liveness_service_provider.h
+++ b/chromeos/dbus/services/liveness_service_provider.h
@@ -17,7 +17,6 @@
 
 namespace dbus {
 class MethodCall;
-class Response;
 }
 
 namespace chromeos {
diff --git a/chromeos/dbus/shill_client_helper.h b/chromeos/dbus/shill_client_helper.h
index 50766df..8ab8f54 100644
--- a/chromeos/dbus/shill_client_helper.h
+++ b/chromeos/dbus/shill_client_helper.h
@@ -25,13 +25,9 @@
 
 namespace dbus {
 
-class Bus;
-class ErrorResponse;
 class MessageWriter;
 class MethodCall;
-class ObjectPath;
 class ObjectProxy;
-class Response;
 class Signal;
 
 }  // namespace dbus
diff --git a/chromeos/dbus/shill_device_client.h b/chromeos/dbus/shill_device_client.h
index 272e9a7..fccb376c8 100644
--- a/chromeos/dbus/shill_device_client.h
+++ b/chromeos/dbus/shill_device_client.h
@@ -16,7 +16,6 @@
 namespace base {
 
 class Value;
-class DictionaryValue;
 
 }  // namespace base
 
diff --git a/chromeos/dbus/shill_manager_client.h b/chromeos/dbus/shill_manager_client.h
index abe5135..2d012c4 100644
--- a/chromeos/dbus/shill_manager_client.h
+++ b/chromeos/dbus/shill_manager_client.h
@@ -17,10 +17,6 @@
 class ObjectPath;
 }
 
-namespace net {
-class IPEndPoint;
-}
-
 namespace chromeos {
 
 class ShillPropertyChangedObserver;
diff --git a/chromeos/dbus/shill_profile_client.h b/chromeos/dbus/shill_profile_client.h
index dba383c..5f60d7e 100644
--- a/chromeos/dbus/shill_profile_client.h
+++ b/chromeos/dbus/shill_profile_client.h
@@ -15,7 +15,6 @@
 
 namespace base {
 
-class Value;
 class DictionaryValue;
 
 }  // namespace base
diff --git a/chromeos/login/auth/login_performer.h b/chromeos/login/auth/login_performer.h
index d9f16ef..633f540 100644
--- a/chromeos/login/auth/login_performer.h
+++ b/chromeos/login/auth/login_performer.h
@@ -24,10 +24,6 @@
 class URLRequestContextGetter;
 }
 
-namespace policy {
-class WildcardLoginChecker;
-}
-
 namespace content {
 class BrowserContext;
 }
diff --git a/chromeos/network/network_configuration_observer.h b/chromeos/network/network_configuration_observer.h
index f9e94694..5605a47 100644
--- a/chromeos/network/network_configuration_observer.h
+++ b/chromeos/network/network_configuration_observer.h
@@ -15,8 +15,6 @@
 
 namespace chromeos {
 
-struct NetworkConfiguration;
-
 // Observer class for network configuration events.
 class NetworkConfigurationObserver {
  public:
diff --git a/chromeos/network/network_profile_handler.h b/chromeos/network/network_profile_handler.h
index f7c70c7..773bdac 100644
--- a/chromeos/network/network_profile_handler.h
+++ b/chromeos/network/network_profile_handler.h
@@ -26,7 +26,6 @@
 namespace chromeos {
 
 class NetworkProfileObserver;
-class NetworkStateHandler;
 
 class CHROMEOS_EXPORT NetworkProfileHandler
     : public ShillPropertyChangedObserver {
diff --git a/chromeos/network/onc/onc_certificate_importer_impl.h b/chromeos/network/onc/onc_certificate_importer_impl.h
index 0a81e4d..15f39e5 100644
--- a/chromeos/network/onc/onc_certificate_importer_impl.h
+++ b/chromeos/network/onc/onc_certificate_importer_impl.h
@@ -21,7 +21,6 @@
 class DictionaryValue;
 class ListValue;
 class SequencedTaskRunner;
-class SingleThreadTaskRunner;
 }
 
 namespace net {
diff --git a/chromeos/network/proxy/proxy_config_handler.h b/chromeos/network/proxy/proxy_config_handler.h
index 124c1ba..d387d2d 100644
--- a/chromeos/network/proxy/proxy_config_handler.h
+++ b/chromeos/network/proxy/proxy_config_handler.h
@@ -10,14 +10,9 @@
 #include "chromeos/chromeos_export.h"
 #include "components/onc/onc_constants.h"
 
-class PrefRegistrySimple;
 class PrefService;
 class ProxyConfigDictionary;
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
 namespace chromeos {
 
 class NetworkState;
diff --git a/cloud_print/BRANDING b/cloud_print/BRANDING
new file mode 100644
index 0000000..0ab41da
--- /dev/null
+++ b/cloud_print/BRANDING
@@ -0,0 +1,3 @@
+PRODUCT_FULLNAME=Google Cloud Print
+PRODUCT_SHORTNAME=GCP
+PRODUCT_DESCRIPTION=Google Cloud Print (GCP) enables any app on any device to print to any cloud-connected printer.
diff --git a/cloud_print/BUILD.gn b/cloud_print/BUILD.gn
new file mode 100644
index 0000000..75ed5e0
--- /dev/null
+++ b/cloud_print/BUILD.gn
@@ -0,0 +1,36 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+group("cloud_print") {
+  if (is_win) {
+    public_deps = [
+      "//cloud_print/virtual_driver/win/install:virtual_driver_setup",
+      "//cloud_print/virtual_driver/win/port_monitor",
+    ]
+
+    # When compiling 32-bit, also reference the 64-bit driver for installing on
+    # 64-bit systems.
+    if (target_cpu == "x86" && current_cpu == "x86") {
+      public_deps += [ "//cloud_print/virtual_driver/win/port_monitor(//build/toolchain/win:x64)" ]
+    }
+  }
+}
+
+test("cloud_print_unittests") {
+  sources = [
+    "virtual_driver/win/port_monitor/port_monitor_unittest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:run_all_unittests",
+    "//cloud_print/virtual_driver/win/port_monitor:lib",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  libs = [ "secur32.lib" ]
+}
diff --git a/cloud_print/DEPS b/cloud_print/DEPS
new file mode 100644
index 0000000..0f0c1a6
--- /dev/null
+++ b/cloud_print/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+chrome/common",
+  "+chrome/installer/launcher_support",
+  "+grit",
+]
diff --git a/cloud_print/OWNERS b/cloud_print/OWNERS
new file mode 100644
index 0000000..8a47a27
--- /dev/null
+++ b/cloud_print/OWNERS
@@ -0,0 +1,7 @@
+# vitalybuka and pastarmovj are only temporary owneres until the new Cloud Print
+# team decides who will own this code and where its final repository will be.
+pastarmovj@chromium.org
+
+# vitalybuka is not on the Chrome project use only as a last resort!
+vitalybuka@chromium.org
+
diff --git a/cloud_print/common/BUILD.gn b/cloud_print/common/BUILD.gn
new file mode 100644
index 0000000..ca0b546d
--- /dev/null
+++ b/cloud_print/common/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("common") {
+  sources = [
+    "win/cloud_print_utils.cc",
+    "win/cloud_print_utils.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("install_utils") {
+  sources = [
+    "win/install_utils.cc",
+    "win/install_utils.h",
+  ]
+
+  deps = [
+    ":common",
+    "//base",
+  ]
+}
diff --git a/cloud_print/common/win/cloud_print_utils.cc b/cloud_print/common/win/cloud_print_utils.cc
new file mode 100644
index 0000000..5a8574c1
--- /dev/null
+++ b/cloud_print/common/win/cloud_print_utils.cc
@@ -0,0 +1,61 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cloud_print/common/win/cloud_print_utils.h"
+
+#include <windows.h>
+
+#include "base/win/registry.h"
+
+namespace cloud_print {
+
+namespace {
+
+// Google Update related constants.
+const wchar_t kClientStateKey[] = L"SOFTWARE\\Google\\Update\\ClientState\\";
+const wchar_t* kUsageKey = L"dr";
+
+}  // namespace
+
+HRESULT GetLastHResult() {
+  DWORD error_code = GetLastError();
+  return error_code ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
+}
+
+base::string16 LoadLocalString(DWORD id) {
+  static wchar_t dummy = L'\0';
+  HMODULE module = NULL;
+  ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT |
+                          GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                      &dummy, &module);
+  LPCWSTR buffer = NULL;
+  // If the last parameter is 0, LoadString assume that 3rd parameter type is
+  // LPCWSTR* and assign pointer to read-only memory with resource.
+  int count = ::LoadString(module, id, reinterpret_cast<LPWSTR>(&buffer), 0);
+  if (!buffer)
+    return base::string16();
+  return base::string16(buffer, buffer + count);
+}
+
+base::string16 GetErrorMessage(HRESULT hr) {
+  LPWSTR buffer = NULL;
+  ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
+                      FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                  0, hr, 0, reinterpret_cast<LPWSTR>(&buffer), 0, NULL);
+  base::string16 result(buffer);
+  ::LocalFree(buffer);
+  return result;
+}
+
+void SetGoogleUpdateUsage(const base::string16& product_id) {
+  // Set appropriate key to 1 to let Omaha record usage.
+  base::win::RegKey key;
+  if (key.Create(HKEY_CURRENT_USER, (kClientStateKey + product_id).c_str(),
+                 KEY_SET_VALUE) != ERROR_SUCCESS ||
+      key.WriteValue(kUsageKey, L"1") != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to set usage key";
+  }
+}
+
+}  // namespace cloud_print
diff --git a/cloud_print/common/win/cloud_print_utils.h b/cloud_print/common/win/cloud_print_utils.h
new file mode 100644
index 0000000..39fe017b
--- /dev/null
+++ b/cloud_print/common/win/cloud_print_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CLOUD_PRINT_COMMON_CLOUD_PRINT_UTILS_H_
+#define CLOUD_PRINT_COMMON_CLOUD_PRINT_UTILS_H_
+
+#include <wtypes.h>
+
+#include "base/strings/string16.h"
+
+namespace cloud_print {
+
+// Similar to the Windows API call GetLastError but returns an HRESULT.
+HRESULT GetLastHResult();
+
+// Convert an HRESULT to a localized string.
+base::string16 GetErrorMessage(HRESULT hr);
+
+// Retrieves a string from the string table of the module that contains the
+// calling code.
+base::string16 LoadLocalString(DWORD id);
+
+// Sets registry value to notify Google Update that product was used.
+void SetGoogleUpdateUsage(const base::string16& product_id);
+
+}  // namespace cloud_print
+
+#endif  // CLOUD_PRINT_COMMON_CLOUD_PRINT_UTILS_H_
diff --git a/cloud_print/common/win/install_utils.cc b/cloud_print/common/win/install_utils.cc
new file mode 100644
index 0000000..955b3ebc
--- /dev/null
+++ b/cloud_print/common/win/install_utils.cc
@@ -0,0 +1,214 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cloud_print/common/win/install_utils.h"
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/file_version_info_win.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/win/current_module.h"
+#include "base/win/registry.h"
+#include "cloud_print/common/win/cloud_print_utils.h"
+
+namespace cloud_print {
+
+namespace {
+
+// Google Update related constants.
+const wchar_t kClientsKey[] = L"SOFTWARE\\Google\\Update\\Clients\\";
+const wchar_t kClientStateKey[] = L"SOFTWARE\\Google\\Update\\ClientState\\";
+const wchar_t kVersionKey[] = L"pv";
+const wchar_t kNameKey[] = L"name";
+
+enum InstallerResult {
+  INSTALLER_RESULT_FAILED_CUSTOM_ERROR = 1,
+  INSTALLER_RESULT_FAILED_SYSTEM_ERROR = 3,
+};
+
+const wchar_t kRegValueInstallerResult[] = L"InstallerResult";
+const wchar_t kRegValueInstallerResultUIString[] = L"InstallerResultUIString";
+const wchar_t kRegValueInstallerError[] = L"InstallerError";
+
+// Uninstall related constants.
+const wchar_t kUninstallKey[] =
+    L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\";
+const wchar_t kInstallLocation[] = L"InstallLocation";
+const wchar_t kUninstallString[] = L"UninstallString";
+const wchar_t kDisplayVersion[] = L"DisplayVersion";
+const wchar_t kDisplayIcon[] = L"DisplayIcon";
+const wchar_t kDisplayName[] = L"DisplayName";
+const wchar_t kPublisher[] = L"Publisher";
+const wchar_t kNoModify[] = L"NoModify";
+const wchar_t kNoRepair[] = L"NoRepair";
+
+}  // namespace
+
+void SetGoogleUpdateKeys(const base::string16& product_id,
+                         const base::string16& product_name) {
+  base::win::RegKey key;
+  if (key.Create(HKEY_LOCAL_MACHINE,
+                 (cloud_print::kClientsKey + product_id).c_str(),
+                 KEY_SET_VALUE) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to open key";
+  }
+
+  // Get the version from the resource file.
+  base::string16 version_string;
+  std::unique_ptr<FileVersionInfo> version_info(
+      FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE()));
+  if (version_info.get()) {
+    FileVersionInfoWin* version_info_win =
+        static_cast<FileVersionInfoWin*>(version_info.get());
+    version_string = version_info_win->product_version();
+  } else {
+    LOG(ERROR) << "Unable to get version string";
+    // Use a random version string so that Google Update has something to go by.
+    version_string = L"0.0.0.99";
+  }
+
+  if (key.WriteValue(kVersionKey, version_string.c_str()) != ERROR_SUCCESS ||
+      key.WriteValue(kNameKey, product_name.c_str()) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to set registry keys";
+  }
+}
+
+void SetGoogleUpdateError(const base::string16& product_id,
+                          const base::string16& message) {
+  LOG(ERROR) << message;
+  base::win::RegKey key;
+  if (key.Create(HKEY_LOCAL_MACHINE,
+                 (cloud_print::kClientStateKey + product_id).c_str(),
+                 KEY_SET_VALUE) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to open key";
+  }
+
+  if (key.WriteValue(kRegValueInstallerResult,
+                     INSTALLER_RESULT_FAILED_CUSTOM_ERROR) != ERROR_SUCCESS ||
+      key.WriteValue(kRegValueInstallerResultUIString, message.c_str()) !=
+          ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to set registry keys";
+  }
+}
+
+void SetGoogleUpdateError(const base::string16& product_id, HRESULT hr) {
+  LOG(ERROR) << cloud_print::GetErrorMessage(hr);
+  base::win::RegKey key;
+  if (key.Create(HKEY_LOCAL_MACHINE,
+                 (cloud_print::kClientStateKey + product_id).c_str(),
+                 KEY_SET_VALUE) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to open key";
+  }
+
+  if (key.WriteValue(kRegValueInstallerResult,
+                     INSTALLER_RESULT_FAILED_SYSTEM_ERROR) != ERROR_SUCCESS ||
+      key.WriteValue(kRegValueInstallerError, hr) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to set registry keys";
+  }
+}
+
+void DeleteGoogleUpdateKeys(const base::string16& product_id) {
+  base::win::RegKey key;
+  if (key.Open(HKEY_LOCAL_MACHINE,
+               (cloud_print::kClientsKey + product_id).c_str(),
+               DELETE) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to open key to delete";
+    return;
+  }
+  if (key.DeleteKey(L"") != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to delete key";
+  }
+}
+
+void CreateUninstallKey(const base::string16& uninstall_id,
+                        const base::string16& product_name,
+                        const std::string& uninstall_switch) {
+  // Now write the Windows Uninstall entries
+  // Minimal error checking here since the install can continue
+  // if this fails.
+  base::win::RegKey key;
+  if (key.Create(HKEY_LOCAL_MACHINE,
+                 (cloud_print::kUninstallKey + uninstall_id).c_str(),
+                 KEY_SET_VALUE) != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to open key";
+    return;
+  }
+
+  base::FilePath unstall_binary;
+  CHECK(PathService::Get(base::FILE_EXE, &unstall_binary));
+
+  base::CommandLine uninstall_command(unstall_binary);
+  uninstall_command.AppendSwitch(uninstall_switch);
+  key.WriteValue(kUninstallString,
+                 uninstall_command.GetCommandLineString().c_str());
+  key.WriteValue(kInstallLocation, unstall_binary.DirName().value().c_str());
+
+  // Get the version resource.
+  std::unique_ptr<FileVersionInfo> version_info(
+      FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE()));
+
+  if (version_info.get()) {
+    FileVersionInfoWin* version_info_win =
+        static_cast<FileVersionInfoWin*>(version_info.get());
+    key.WriteValue(kDisplayVersion, version_info_win->file_version().c_str());
+    key.WriteValue(kPublisher, version_info_win->company_name().c_str());
+  } else {
+    LOG(ERROR) << "Unable to get version string";
+  }
+  key.WriteValue(kDisplayName, product_name.c_str());
+  key.WriteValue(kDisplayIcon, unstall_binary.value().c_str());
+  key.WriteValue(kNoModify, 1);
+  key.WriteValue(kNoRepair, 1);
+}
+
+void DeleteUninstallKey(const base::string16& uninstall_id) {
+  ::RegDeleteKey(HKEY_LOCAL_MACHINE,
+                 (cloud_print::kUninstallKey + uninstall_id).c_str());
+}
+
+base::FilePath GetInstallLocation(const base::string16& uninstall_id) {
+  base::win::RegKey key;
+  if (key.Open(HKEY_LOCAL_MACHINE,
+               (cloud_print::kUninstallKey + uninstall_id).c_str(),
+               KEY_QUERY_VALUE) != ERROR_SUCCESS) {
+    // Not installed.
+    return base::FilePath();
+  }
+  base::string16 install_path_value;
+  key.ReadValue(kInstallLocation, &install_path_value);
+  return base::FilePath(install_path_value);
+}
+
+void DeleteProgramDir(const std::string& delete_switch) {
+  base::FilePath installer_source;
+  if (!PathService::Get(base::FILE_EXE, &installer_source))
+    return;
+  // Deletes only subdirs of program files.
+  if (!IsProgramsFilesParent(installer_source))
+    return;
+  base::FilePath temp_path;
+  if (!base::CreateTemporaryFile(&temp_path))
+    return;
+  base::CopyFile(installer_source, temp_path);
+  base::DeleteFileAfterReboot(temp_path);
+  base::CommandLine command_line(temp_path);
+  command_line.AppendSwitchPath(delete_switch, installer_source.DirName());
+  base::LaunchOptions options;
+  if (!base::LaunchProcess(command_line, options).IsValid()) {
+    LOG(ERROR) << "Unable to launch child uninstall.";
+  }
+}
+
+bool IsProgramsFilesParent(const base::FilePath& path) {
+  base::FilePath program_files;
+  if (!PathService::Get(base::DIR_PROGRAM_FILESX86, &program_files))
+    return false;
+  return program_files.IsParent(path);
+}
+
+}  // namespace cloud_print
diff --git a/cloud_print/common/win/install_utils.h b/cloud_print/common/win/install_utils.h
new file mode 100644
index 0000000..92b8fd87
--- /dev/null
+++ b/cloud_print/common/win/install_utils.h
@@ -0,0 +1,49 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CLOUD_PRINT_COMMON_INSTALL_UTILS_H_
+#define CLOUD_PRINT_COMMON_INSTALL_UTILS_H_
+
+#include <wtypes.h>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+
+namespace cloud_print {
+
+// Sets Google Update registry keys after install or update.
+void SetGoogleUpdateKeys(const base::string16& product_id,
+                         const base::string16& product_name);
+
+// Sets custom error message to show by Google Update installer
+void SetGoogleUpdateError(const base::string16& product_id,
+                          const base::string16& message);
+
+// Sets custom system error code to show by Google Update installer
+void SetGoogleUpdateError(const base::string16& product_id, HRESULT hr);
+
+// Deletes Google Update reg keys on product uninstall.
+void DeleteGoogleUpdateKeys(const base::string16& product_id);
+
+// Creates control panel uninstall item.
+void CreateUninstallKey(const base::string16& uninstall_id,
+                        const base::string16& product_name,
+                        const std::string& uninstall_switch);
+
+// Deletes control panel uninstall item.
+void DeleteUninstallKey(const base::string16& uninstall_id);
+
+// Returns install location retrieved from control panel uninstall key.
+base::FilePath GetInstallLocation(const base::string16& uninstall_id);
+
+// Returns install location retrieved from control panel uninstall key.
+void DeleteProgramDir(const std::string& delete_switch);
+
+// Returns true if path is part of program files.
+bool IsProgramsFilesParent(const base::FilePath& path);
+
+}  // namespace cloud_print
+
+#endif  // CLOUD_PRINT_COMMON_INSTALL_UTILS_H_
diff --git a/cloud_print/virtual_driver/win/BUILD.gn b/cloud_print/virtual_driver/win/BUILD.gn
new file mode 100644
index 0000000..110b5a36
--- /dev/null
+++ b/cloud_print/virtual_driver/win/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_win)
+
+source_set("win") {
+  sources = [
+    "virtual_driver_consts.cc",
+    "virtual_driver_consts.h",
+    "virtual_driver_helpers.cc",
+    "virtual_driver_helpers.h",
+  ]
+
+  deps = [
+    "//base",
+    "//cloud_print/common",
+  ]
+}
diff --git a/cloud_print/virtual_driver/win/gcp_portmon_dll.ver b/cloud_print/virtual_driver/win/gcp_portmon_dll.ver
new file mode 100644
index 0000000..dd75a0f1
--- /dev/null
+++ b/cloud_print/virtual_driver/win/gcp_portmon_dll.ver
@@ -0,0 +1,3 @@
+INTERNAL_NAME=gcp_portmon.dll
+ORIGINAL_FILENAME=gcp_portmon.dll
+FILETYPE=0x2L
diff --git a/cloud_print/virtual_driver/win/install/BUILD.gn b/cloud_print/virtual_driver/win/install/BUILD.gn
new file mode 100644
index 0000000..3018a21
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/BUILD.gn
@@ -0,0 +1,120 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/process_version_rc_template.gni")
+import("//tools/grit/grit_rule.gni")
+
+assert(is_win)
+
+executable("virtual_driver_setup") {
+  sources = [
+    "setup.cc",
+  ]
+
+  configs -= [ "//build/config/win:console" ]
+  configs += [ "//build/config/win:windowed" ]
+
+  deps = [
+    ":copy_gcp_driver_gpd",
+    ":copy_gcp_driver_inf",
+    ":resources",
+    ":setup_version",
+    "//base",
+    "//cloud_print/common",
+    "//cloud_print/common:install_utils",
+    "//cloud_print/virtual_driver/win",
+  ]
+
+  libs = [ "setupapi.lib" ]
+  ldflags = [ "/DELAYLOAD:winspool.drv" ]
+}
+
+copy("copy_gcp_driver_gpd") {
+  sources = [
+    "inf/gcp_driver.gpd",
+  ]
+  outputs = [
+    "$root_build_dir/gcp_driver.gpd",
+  ]
+}
+
+copy("copy_gcp_driver_inf") {
+  sources = [
+    "inf/gcp_driver.inf",
+  ]
+  outputs = [
+    "$root_build_dir/gcp_driver.inf",
+  ]
+}
+
+process_version_rc_template("setup_version") {
+  sources = [
+    "virtual_driver_setup_exe.ver",
+  ]
+  output = "$target_gen_dir/virtual_driver_setup.rc"
+}
+
+grit("resources") {
+  visibility = [ ":*" ]
+
+  source = "virtual_driver_setup_resources.grd"
+
+  output_dir = "$root_gen_dir/virtual_driver_setup_resources"
+
+  outputs = [
+    "grit/virtual_driver_setup_resources.h",
+    "virtual_driver_setup_resources_ar.rc",
+    "virtual_driver_setup_resources_bg.rc",
+    "virtual_driver_setup_resources_bn.rc",
+    "virtual_driver_setup_resources_ca.rc",
+    "virtual_driver_setup_resources_cs.rc",
+    "virtual_driver_setup_resources_da.rc",
+    "virtual_driver_setup_resources_de.rc",
+    "virtual_driver_setup_resources_el.rc",
+    "virtual_driver_setup_resources_en.rc",
+    "virtual_driver_setup_resources_en-GB.rc",
+    "virtual_driver_setup_resources_es.rc",
+    "virtual_driver_setup_resources_es-419.rc",
+    "virtual_driver_setup_resources_et.rc",
+    "virtual_driver_setup_resources_fa.rc",
+    "virtual_driver_setup_resources_fi.rc",
+    "virtual_driver_setup_resources_fil.rc",
+    "virtual_driver_setup_resources_fr.rc",
+    "virtual_driver_setup_resources_gu.rc",
+    "virtual_driver_setup_resources_he.rc",
+    "virtual_driver_setup_resources_hi.rc",
+    "virtual_driver_setup_resources_hr.rc",
+    "virtual_driver_setup_resources_hu.rc",
+    "virtual_driver_setup_resources_id.rc",
+    "virtual_driver_setup_resources_it.rc",
+    "virtual_driver_setup_resources_ja.rc",
+    "virtual_driver_setup_resources_kn.rc",
+    "virtual_driver_setup_resources_ko.rc",
+    "virtual_driver_setup_resources_lt.rc",
+    "virtual_driver_setup_resources_lv.rc",
+    "virtual_driver_setup_resources_ml.rc",
+    "virtual_driver_setup_resources_mr.rc",
+    "virtual_driver_setup_resources_ms.rc",
+    "virtual_driver_setup_resources_nl.rc",
+    "virtual_driver_setup_resources_nb.rc",
+    "virtual_driver_setup_resources_pl.rc",
+    "virtual_driver_setup_resources_pt-BR.rc",
+    "virtual_driver_setup_resources_pt-PT.rc",
+    "virtual_driver_setup_resources_ro.rc",
+    "virtual_driver_setup_resources_ru.rc",
+    "virtual_driver_setup_resources_sk.rc",
+    "virtual_driver_setup_resources_sl.rc",
+    "virtual_driver_setup_resources_sr.rc",
+    "virtual_driver_setup_resources_sv.rc",
+    "virtual_driver_setup_resources_sw.rc",
+    "virtual_driver_setup_resources_ta.rc",
+    "virtual_driver_setup_resources_te.rc",
+    "virtual_driver_setup_resources_th.rc",
+    "virtual_driver_setup_resources_tr.rc",
+    "virtual_driver_setup_resources_uk.rc",
+    "virtual_driver_setup_resources_vi.rc",
+    "virtual_driver_setup_resources_zh-CN.rc",
+    "virtual_driver_setup_resources_zh-TW.rc",
+  ]
+}
diff --git a/cloud_print/virtual_driver/win/install/inf/gcp_driver.gpd b/cloud_print/virtual_driver/win/install/inf/gcp_driver.gpd
new file mode 100644
index 0000000..3e853d6
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/inf/gcp_driver.gpd
@@ -0,0 +1,1034 @@
+*% 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.
+
+*GPDFileVersion: "1.0"
+*GPDSpecVersion: "1.0"
+*Include:        "StdNames.gpd"
+*ResourceDLL:    "unires.dll"
+*ModelName:      "Google Cloud Printer"
+*MasterUnits:    PAIR(1200, 1200)
+*MaxCopies:      1
+*PrintRatePPM: 200
+*PrinterType:    PAGE
+*IsXPSDriver?:   TRUE
+
+*Feature: ColorMode {
+  *rcNameID: =COLOR_PRINTING_MODE_DISPLAY
+  *DefaultOption: 24bpp
+  *ConcealFromUI?: TRUE
+  *Option: 24bpp {
+    *rcNameID: =24BPP_DISPLAY
+    *DevNumOfPlanes: 1
+    *DevBPP: 24
+    *DrvBPP: 24
+  }
+}
+
+*Feature: Memory {
+  *rcNameID: =PRINTER_MEMORY_DISPLAY
+  *DefaultOption: 65536KB
+  *Option: 16384KB {
+    *Name: "16MB"
+    *MemoryConfigKB: PAIR(16384, 16384)
+  }
+  *Option: 65536KB {
+    *Name: "64MB"
+    *MemoryConfigKB: PAIR(65536, 65536)
+  }
+}
+
+*Feature: Orientation {
+  *rcNameID: =ORIENTATION_DISPLAY
+  *DefaultOption: PORTRAIT
+  *Option: PORTRAIT {
+    *rcNameID: =PORTRAIT_DISPLAY
+  }
+  *Option: LANDSCAPE_CC270 {
+    *rcNameID: =LANDSCAPE_DISPLAY
+  }
+}
+
+*Feature: PaperSize {
+  *rcNameID: =PAPER_SIZE_DISPLAY
+  *DefaultOption: LETTER
+
+  *Option: A2 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(19842, 28062)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(28062, 19842)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A3 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(14031, 19842)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(19842, 14031)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(9921, 14031)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(14031, 9921)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A5 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(6992, 9921)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(9921, 6992)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A6 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4960, 6992)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(6992, 4960)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A4_PLUS {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(9921, 15590)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(15590, 9921)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A3_EXTRA {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(15212, 21023)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(21023, 15212)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A4_EXTRA {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(11102, 15212)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(15212, 11102)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: A5_EXTRA {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(8220, 11102)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(11102, 8220)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: B4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(12141, 17196)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(17196, 12141)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: B5 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(8598, 12141)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(12141, 8598)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_B4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(11811, 16677)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(16677, 11811)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_B5 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(8314, 11811)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(11811, 8314)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_B6 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5905, 8314)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(8314, 5905)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: B6_JIS {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(6047, 8598)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(8598, 6047)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: B5_EXTRA {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(9496, 13039)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(13039, 9496)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_C3 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(15307, 21637)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(21637, 15307)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_C4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10818, 15307)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(15307, 10818)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_C5 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(7653, 10818)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10818, 7653)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_C6 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5385, 7653)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(7653, 5385)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_C65 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5385, 10818)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10818, 5385)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_DL {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5196, 10393)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10393, 5196)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: JENV_CHOU3 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5669, 11102)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(11102, 5669)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: JENV_CHOU4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4251, 9685)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(9685, 4251)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: JAPANESE_POSTCARD {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4724, 6992)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(6992, 4724)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: JENV_KAKU2 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(11338, 15685)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(15685, 11338)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: DBL_JAPANESE_POSTCARD {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(6992, 9448)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(9448, 6992)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: JENV_YOU4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4960, 11102)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(11102, 4960)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: 10X11 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(12000, 13200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(13200, 12000)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: 10X14 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(12000, 16800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(16800, 12000)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: 9X11 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10800, 13200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(13200, 10800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: CSHEET {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(20400, 26400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(26400, 20400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: DSHEET {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(26400, 40800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(40800, 26400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ESHEET {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(40800, 52800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(52800, 40800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: EXECUTIVE {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(8700, 12600)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(12600, 8700)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: FANFOLD_STD_GERMAN {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10200, 14400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(14400, 10200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: FANFOLD_US {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(13200, 17850)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(17850, 13200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: FOLIO {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10200, 15600)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(15600, 10200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: STATEMENT {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(6600, 10200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10200, 6600)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: TABLOID {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(13200, 20400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(20400, 13200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: LEGAL {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10200, 16800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(16800, 10200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: LEGAL_EXTRA {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(11400, 18000)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(18000, 11400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: LETTER {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10200, 13200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(13200, 10200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: LETTER_EXTRA {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(11400, 14400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(14400, 11400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: LETTER_PLUS {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10200, 15228)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(15228, 10200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_MONARCH {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4650, 9000)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(9000, 4650)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_9 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4650, 10650)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10650, 4650)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_10 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4950, 11400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(11400, 4950)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_11 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5400, 12450)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(12450, 5400)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_12 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5700, 13200)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(13200, 5700)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_14 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(6000, 13800)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(13800, 6000)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: ENV_INVITE {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(10393, 10393)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10393, 10393)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_1 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(4818, 7795)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(7795, 4818)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_3 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5905, 8314)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(8314, 5905)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_4 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5196, 9826)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(9826, 5196)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_5 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5196, 10393)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10393, 5196)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_6 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5669, 10866)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10866, 5669)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_7 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(7559, 10866)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(10866, 7559)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_8 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(5669, 14598)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(14598, 5669)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+
+  *Option: PENV_10 {
+    *rcNameID: =RCID_DMPAPER_SYSTEM_NAME
+    *switch: Orientation {
+      *case: PORTRAIT {
+        *PrintableArea: PAIR(15307, 21637)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+      *case: LANDSCAPE_CC270 {
+        *PrintableArea: PAIR(21637, 15307)
+        *PrintableOrigin: PAIR(0, 0)
+        *CursorOrigin: PAIR(0, 0)
+      }
+    }
+  }
+}
+
+*Feature: Resolution {
+  *rcNameID: =RESOLUTION_DISPLAY
+  *DefaultOption: 600dpi
+
+  *Option: 600dpi {
+    *Name: "600 x 600 " =DOTS_PER_INCH
+    *DPI: PAIR(600, 600)
+    *TextDPI: PAIR(600, 600)
+    *SpotDiameter: 100
+    *Command: CmdBeginRaster { *Cmd: "<1B>*v7S<1B>*r1A" }
+    *Command: CmdEndRaster { *Cmd: "<1B>*rC" }
+    *Command: CmdSendBlockData { *Cmd: "<1B>*b" %d {NumOfDataBytes}"W" }
+  }
+}
+
+*Command: CmdCR { *Cmd: "<0D>" }
+*Command: CmdLF { *Cmd: "<0A>" }
+*Command: CmdFF { *Cmd: "<0C>" }
diff --git a/cloud_print/virtual_driver/win/install/inf/gcp_driver.inf b/cloud_print/virtual_driver/win/install/inf/gcp_driver.inf
new file mode 100644
index 0000000..8d780245
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/inf/gcp_driver.inf
Binary files differ
diff --git a/cloud_print/virtual_driver/win/install/resources/cloudprint.ico b/cloud_print/virtual_driver/win/install/resources/cloudprint.ico
new file mode 100644
index 0000000..e362c72
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/cloudprint.ico
Binary files differ
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ar.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ar.xtb
new file mode 100644
index 0000000..280740c
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ar.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ar">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>برنامج تشغيل XPS<ph name="XPS_URL_END"/> غير مثبّت.</translation>
+<translation id="4983496916481712098">طابعة متوافقة مع خدمة طباعة في السحاب من Google</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bg.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bg.xtb
new file mode 100644
index 0000000..c5eeb0b
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bg.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bg">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Драйверът XPS<ph name="XPS_URL_END"/> не е инсталиран.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bn.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bn.xtb
new file mode 100644
index 0000000..92c00281
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bn">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ড্রাইভার<ph name="XPS_URL_END"/> ইনস্টল করা নেই৷</translation>
+<translation id="4983496916481712098">Google মেঘ মুদ্রণ</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ca.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ca.xtb
new file mode 100644
index 0000000..06c9597
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ca.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ca">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">El <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> no està instal·lat.</translation>
+<translation id="4983496916481712098">Impressora de Google Cloud</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_cs.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_cs.xtb
new file mode 100644
index 0000000..49281b001
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_cs.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="cs">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">Není nainstalován <ph name="XPS_URL"/>ovladač XPS<ph name="XPS_URL_END"/>.</translation>
+<translation id="4983496916481712098">Tiskárna v cloudu Google</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_da.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_da.xtb
new file mode 100644
index 0000000..fa7f7d21
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_da.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="da">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-driver<ph name="XPS_URL_END"/> er ikke installeret.</translation>
+<translation id="4983496916481712098">Google Cloudprinter</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_de.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_de.xtb
new file mode 100644
index 0000000..0e298c4
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_de.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="de">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-Treiber<ph name="XPS_URL_END"/> ist nicht installiert.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_el.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_el.xtb
new file mode 100644
index 0000000..a54377b
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_el.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="el">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Το πρόγραμμα οδήγησης XPS<ph name="XPS_URL_END"/> δεν είναι εγκατεστημένο.</translation>
+<translation id="4983496916481712098">Εκτυπωτής Google Cloud</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_en-GB.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_en-GB.xtb
new file mode 100644
index 0000000..83f468e
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_en-GB.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="en-GB">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS driver<ph name="XPS_URL_END"/> is not installed.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es-419.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es-419.xtb
new file mode 100644
index 0000000..96ac8039
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es-419.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es-419">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">El <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> no está instalado.</translation>
+<translation id="4983496916481712098">Impresora de Google Cloud</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es.xtb
new file mode 100644
index 0000000..75d1a247
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">El <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> no está instalado.</translation>
+<translation id="4983496916481712098">Impresora en la nube de Google</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_et.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_et.xtb
new file mode 100644
index 0000000..d023313
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_et.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="et">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-draiver<ph name="XPS_URL_END"/> on installimata.</translation>
+<translation id="4983496916481712098">Google'i pilvprinter</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fa.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fa.xtb
new file mode 100644
index 0000000..28944fd
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fa.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fa">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>راه‌انداز XPS‏<ph name="XPS_URL_END"/> نصب نشده است.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fi.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fi.xtb
new file mode 100644
index 0000000..5ecb49d
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fi">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-ajuria<ph name="XPS_URL_END"/> ei ole asennettu.</translation>
+<translation id="4983496916481712098">Google Cloud -tulostin</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fil.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fil.xtb
new file mode 100644
index 0000000..4423db92
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fil.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fil">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">Hindi naka-install ang <ph name="XPS_URL"/>XPS driver<ph name="XPS_URL_END"/>.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fr.xtb
new file mode 100644
index 0000000..8479057
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fr">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">Le <ph name="XPS_URL"/>pilote XPS<ph name="XPS_URL_END"/> n'est pas installé.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_gu.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_gu.xtb
new file mode 100644
index 0000000..372d6ca
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_gu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="gu">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ડ્રાઇવર<ph name="XPS_URL_END"/> ઇન્સ્ટોલ કરેલું નથી.</translation>
+<translation id="4983496916481712098">Google મેઘ મુદ્રણ</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_he.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_he.xtb
new file mode 100644
index 0000000..d284b854
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_he.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="he">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/> מנהל ההתקן של XPS<ph name="XPS_URL_END"/> לא מותקן.</translation>
+<translation id="4983496916481712098">מדפסת Google Cloud Print</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hi.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hi.xtb
new file mode 100644
index 0000000..0ca331d
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hi">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ड्राइवर<ph name="XPS_URL_END"/> इंस्टॉल नहीं है.</translation>
+<translation id="4983496916481712098">Google क्लाउड प्रिंटर</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hr.xtb
new file mode 100644
index 0000000..3a23556
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hr">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Upravljački program za XPS<ph name="XPS_URL_END"/> nije instaliran.</translation>
+<translation id="4983496916481712098">Google pisač u oblaku</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hu.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hu.xtb
new file mode 100644
index 0000000..6a8f56e
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hu">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">Nincs telepítve <ph name="XPS_URL"/>XPS illesztőprogram<ph name="XPS_URL_END"/>.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_id.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_id.xtb
new file mode 100644
index 0000000..a29c270
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_id.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="id">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Driver XPS<ph name="XPS_URL_END"/> tidak terpasang.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_it.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_it.xtb
new file mode 100644
index 0000000..ee07dc3
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_it.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="it">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Il driver XPS<ph name="XPS_URL_END"/> non è installato.</translation>
+<translation id="4983496916481712098">Google Cloud Print</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ja.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ja.xtb
new file mode 100644
index 0000000..1cf9c1b
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ja.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ja">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ドライバ<ph name="XPS_URL_END"/>がインストールされていません。</translation>
+<translation id="4983496916481712098">Google クラウド プリンタ</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_kn.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_kn.xtb
new file mode 100644
index 0000000..e64a2fea
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_kn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="kn">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ಡ್ರೈವರ್<ph name="XPS_URL_END"/> ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ.</translation>
+<translation id="4983496916481712098">Google ಮೇಘ ಮುದ್ರಕ</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ko.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ko.xtb
new file mode 100644
index 0000000..a73deef
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ko.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ko">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS 드라이버<ph name="XPS_URL_END"/>가 설치되지 않았습니다.</translation>
+<translation id="4983496916481712098">Google 클라우드 프린터</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lt.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lt.xtb
new file mode 100644
index 0000000..712778f6
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lt.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lt">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS tvarkyklė<ph name="XPS_URL_END"/> neįdiegta.</translation>
+<translation id="4983496916481712098">„Google“ spausdinimo iš debesies programa</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lv.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lv.xtb
new file mode 100644
index 0000000..c4499a4
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lv.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lv">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS dzinis<ph name="XPS_URL_END"/> nav instalēts.</translation>
+<translation id="4983496916481712098">Google mākoņprinteris</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ml.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ml.xtb
new file mode 100644
index 0000000..7fad033
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ml.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ml">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ഡ്രൈവർ<ph name="XPS_URL_END"/> ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല.</translation>
+<translation id="4983496916481712098">Google ക്ലൗഡ് പ്രിന്റർ</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_mr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_mr.xtb
new file mode 100644
index 0000000..9cc832bd
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_mr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="mr">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ड्राइव्हर<ph name="XPS_URL_END"/> स्थापित केलेला नाही.</translation>
+<translation id="4983496916481712098">Google मेघ प्रिंटर</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ms.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ms.xtb
new file mode 100644
index 0000000..63c3567
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ms.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ms">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Pemacu XPS<ph name="XPS_URL_END"/> tidak dipasang.</translation>
+<translation id="4983496916481712098">Pencetak Awan Google</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_nl.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_nl.xtb
new file mode 100644
index 0000000..76f87ad
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_nl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="nl">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-stuurprogramma<ph name="XPS_URL_END"/> is niet geïnstalleerd.</translation>
+<translation id="4983496916481712098">Google Cloudprinter</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_no.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_no.xtb
new file mode 100644
index 0000000..ddadb1c
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_no.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="no">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-driveren<ph name="XPS_URL_END"/> er ikke installert.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pl.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pl.xtb
new file mode 100644
index 0000000..07ee486
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pl">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Sterownik XPS<ph name="XPS_URL_END"/> nie jest zainstalowany.</translation>
+<translation id="4983496916481712098">Drukarka Google Cloud</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-BR.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-BR.xtb
new file mode 100644
index 0000000..9d7aff02
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-BR.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-BR">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">O <ph name="XPS_URL"/>driver XPS<ph name="XPS_URL_END"/> não está instalado.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-PT.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-PT.xtb
new file mode 100644
index 0000000..128896c
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-PT.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-PT">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">O <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> não está instalado.</translation>
+<translation id="4983496916481712098">Impressora do Google Cloud Print</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ro.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ro.xtb
new file mode 100644
index 0000000..7afd35a
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ro.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ro">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Driverul XPS<ph name="XPS_URL_END"/> nu este instalat.</translation>
+<translation id="4983496916481712098">Imprimanta Google Cloud Print</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ru.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ru.xtb
new file mode 100644
index 0000000..6828c60
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ru.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ru">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Драйвер XPS<ph name="XPS_URL_END"/> не установлен.</translation>
+<translation id="4983496916481712098">Виртуальный принтер Google</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sk.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sk.xtb
new file mode 100644
index 0000000..f50b627
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sk">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Ovládač XPS<ph name="XPS_URL_END"/> nie je nainštalovaný.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sl.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sl.xtb
new file mode 100644
index 0000000..1eba0bd
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sl">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">Gonilnik <ph name="XPS_URL"/>XPS<ph name="XPS_URL_END"/> ni nameščen.</translation>
+<translation id="4983496916481712098">Tiskalnik za Google Tiskanje v oblaku</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sr.xtb
new file mode 100644
index 0000000..5861cb7
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sr">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Управљачки програм за XPS<ph name="XPS_URL_END"/> није инсталиран.</translation>
+<translation id="4983496916481712098">Google Cloud штампач</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sv.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sv.xtb
new file mode 100644
index 0000000..07a9e93
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sv.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sv">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">Ingen <ph name="XPS_URL"/>XPS-drivrutin<ph name="XPS_URL_END"/> är installerad.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sw.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sw.xtb
new file mode 100644
index 0000000..7106d22
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sw.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sw">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS driver<ph name="XPS_URL_END"/> is not installed.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ta.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ta.xtb
new file mode 100644
index 0000000..612c1908
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ta.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ta">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS இயக்கி<ph name="XPS_URL_END"/> நிறுவப்படவில்லை.</translation>
+<translation id="4983496916481712098">Google மேகக்கணி அச்சுப்பொறி</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_te.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_te.xtb
new file mode 100644
index 0000000..0323475
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_te.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="te">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS డ్రైవర్<ph name="XPS_URL_END"/> ఇన్‌స్టాల్ చేయబడలేదు.</translation>
+<translation id="4983496916481712098">Google మేఘ ప్రింటర్</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_th.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_th.xtb
new file mode 100644
index 0000000..67bf8ce
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_th.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="th">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">ไม่ได้ติดตั้ง<ph name="XPS_URL"/>ไดรเวอร์ XPS<ph name="XPS_URL_END"/></translation>
+<translation id="4983496916481712098">Google เครื่องพิมพ์ระบบคลาวด์</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_tr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_tr.xtb
new file mode 100644
index 0000000..7748a61
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_tr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="tr">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS sürücüsü<ph name="XPS_URL_END"/> yüklü değil.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_uk.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_uk.xtb
new file mode 100644
index 0000000..fff693c5
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_uk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="uk">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Драйвер XPS<ph name="XPS_URL_END"/> не встановлено.</translation>
+<translation id="4983496916481712098">Веб-доступний принтер Google</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_vi.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_vi.xtb
new file mode 100644
index 0000000..93186dc
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_vi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="vi">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631"><ph name="XPS_URL"/>Trình điều khiển XPS<ph name="XPS_URL_END"/> chưa được cài đặt.</translation>
+<translation id="4983496916481712098">Google Cloud Printer</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-CN.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-CN.xtb
new file mode 100644
index 0000000..d9eface2
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-CN.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-CN">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">未安装 <ph name="XPS_URL"/>XPS 驱动程序<ph name="XPS_URL_END"/>。</translation>
+<translation id="4983496916481712098">Google 云端打印机</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-HK.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-HK.xtb
new file mode 100644
index 0000000..68d7922
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-HK.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-HK">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">未安裝 <ph name="XPS_URL"/>XPS 驅動程式<ph name="XPS_URL_END"/>。</translation>
+<translation id="4983496916481712098">Google 雲端印表機</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-TW.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-TW.xtb
new file mode 100644
index 0000000..87f03685
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-TW.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-TW">
+<translation id="2424268526671779403">Google</translation>
+<translation id="4659506378548526631">未安裝 <ph name="XPS_URL"/>XPS 驅動程式<ph name="XPS_URL_END"/>。</translation>
+<translation id="4983496916481712098">Google 雲端印表機</translation>
+</translationbundle>
\ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc
new file mode 100644
index 0000000..3c4bcad2
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/setup.cc
@@ -0,0 +1,409 @@
+// 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 <windows.h>
+#include <setupapi.h>  // Must be included after windows.h
+#include <winspool.h>
+#include <stddef.h>
+
+#include <iomanip>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/win/windows_version.h"
+#include "cloud_print/common/win/cloud_print_utils.h"
+#include "cloud_print/common/win/install_utils.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
+#include "grit/virtual_driver_setup_resources.h"
+
+#include <strsafe.h>  // Must be after base headers to avoid deprecation
+                      // warnings.
+
+namespace cloud_print {
+
+namespace {
+
+const wchar_t kNameValue[] = L"GCP Virtual Driver";
+const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
+const wchar_t kGcpUrl[] = L"https://www.google.com/cloudprint";
+
+const wchar_t kInfFileName[] = L"gcp_driver.inf";
+
+const char kDelete[] = "delete";
+const char kInstallSwitch[] = "install";
+const char kRegisterSwitch[] = "register";
+const char kUninstallSwitch[] = "uninstall";
+const char kUnregisterSwitch[] = "unregister";
+
+base::FilePath GetSystemPath(const base::string16& binary) {
+  base::FilePath path;
+  if (!PathService::Get(base::DIR_SYSTEM, &path)) {
+    LOG(ERROR) << "Unable to get system path.";
+    return path;
+  }
+  return path.Append(binary);
+}
+
+void SpoolerServiceCommand(const char* command) {
+  base::FilePath net_path = GetSystemPath(L"net");
+  if (net_path.empty())
+    return;
+  base::CommandLine command_line(net_path);
+  command_line.AppendArg(command);
+  command_line.AppendArg("spooler");
+  command_line.AppendArg("/y");
+
+  base::LaunchOptions options;
+  options.wait = true;
+  options.start_hidden = true;
+  VLOG(0) << command_line.GetCommandLineString();
+  base::LaunchProcess(command_line, options);
+}
+
+HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
+  DCHECK(install || install_path.empty());
+  base::FilePath target_path = GetSystemPath(L"gcp_portmon.dll");
+  if (target_path.empty()) {
+    LOG(ERROR) << "Unable to get port monitor target path.";
+    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  }
+  if (install) {
+    base::FilePath source_path = install_path.Append(L"gcp_portmon.dll");
+    if (!base::CopyFile(source_path, target_path)) {
+      LOG(ERROR) << "Unable copy port monitor dll from " << source_path.value()
+                 << " to " << target_path.value();
+      return GetLastHResult();
+    }
+  } else if (!base::PathExists(target_path)) {
+    // Already removed.  Just "succeed" silently.
+    return S_OK;
+  }
+
+  base::FilePath regsvr32_path = GetSystemPath(L"regsvr32.exe");
+  if (regsvr32_path.empty()) {
+    LOG(ERROR) << "Can't find regsvr32.exe.";
+    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  }
+
+  base::CommandLine command_line(regsvr32_path);
+  command_line.AppendArg("/s");
+  if (!install) {
+    command_line.AppendArg("/u");
+  }
+
+  // Use system32 path here because otherwise ::AddMonitor would fail.
+  command_line.AppendArgPath(GetSystemPath(L"gcp_portmon.dll"));
+
+  base::LaunchOptions options;
+  options.wait = true;
+
+  base::Process regsvr32_process =
+      base::LaunchProcess(command_line.GetCommandLineString(), options);
+  if (!regsvr32_process.IsValid()) {
+    LOG(ERROR) << "Unable to launch regsvr32.exe.";
+    return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+  }
+
+  DWORD exit_code = S_OK;
+  if (install) {
+    if (!GetExitCodeProcess(regsvr32_process.Handle(), &exit_code)) {
+      LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
+      return GetLastHResult();
+    }
+    if (exit_code != 0) {
+      LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
+      return HRESULT_FROM_WIN32(exit_code);
+    }
+  } else {
+    if (!base::DeleteFile(target_path, false)) {
+      SpoolerServiceCommand("stop");
+      bool deleted = base::DeleteFile(target_path, false);
+      SpoolerServiceCommand("start");
+
+      if (!deleted) {
+        LOG(ERROR) << "Unable to delete " << target_path.value();
+        return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
+      }
+    }
+  }
+  return S_OK;
+}
+
+HRESULT InstallDriver(const base::FilePath& install_path) {
+  DWORD size = MAX_PATH * 10;
+  wchar_t package_path[MAX_PATH * 10] = {0};
+
+  base::FilePath inf_file = install_path.Append(kInfFileName);
+  base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
+
+  HRESULT result = UploadPrinterDriverPackage(
+      NULL, inf_file.value().c_str(), NULL,
+      UPDP_SILENT_UPLOAD | UPDP_UPLOAD_ALWAYS, NULL, package_path, &size);
+  if (FAILED(result)) {
+    LOG(ERROR)
+        << "Uploading the printer driver package to the driver cache failed.";
+    return result;
+  }
+
+  result = InstallPrinterDriverFromPackage(
+      NULL, package_path, driver_name.c_str(), NULL, IPDFP_COPY_ALL_FILES);
+  if (FAILED(result)) {
+    LOG(ERROR) << "Installing the printer driver failed.";
+  }
+  return result;
+}
+
+HRESULT UninstallDriver(const base::FilePath& install_path) {
+  base::FilePath inf_file = install_path.Append(kInfFileName);
+  int tries = 3;
+
+  while (!DeletePrinterDriverPackage(NULL, inf_file.value().c_str(), NULL)) {
+    if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
+      LOG(WARNING) << "Print driver is already uninstalled.";
+      return S_OK;
+    }
+    // After deleting the printer it can take a few seconds before
+    // the driver is free for deletion.  Retry a few times before giving up.
+    LOG(WARNING) << "Attempt to delete printer driver failed.  Retrying.";
+    tries--;
+    Sleep(2000);
+  }
+  if (tries <= 0) {
+    HRESULT result = GetLastHResult();
+    LOG(ERROR) << "Unable to delete printer driver.";
+    return result;
+  }
+  return S_OK;
+}
+
+HRESULT InstallPrinter(void) {
+  PRINTER_INFO_2 printer_info = {0};
+
+  // None of the print API structures likes constant strings even though they
+  // don't modify the string.  const_casting is the cleanest option.
+  base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
+  printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
+  printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
+  printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str());
+  printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
+  base::string16 port_name;
+  printer_info.pPortName = const_cast<LPWSTR>(kPortName);
+  printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT | PRINTER_ATTRIBUTE_LOCAL;
+  printer_info.pPrintProcessor = const_cast<LPWSTR>(L"winprint");
+  HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
+  if (handle == NULL) {
+    HRESULT result = GetLastHResult();
+    LOG(ERROR) << "Unable to add printer";
+    return result;
+  }
+  ClosePrinter(handle);
+  return S_OK;
+}
+
+HRESULT UninstallPrinter(void) {
+  HANDLE handle = NULL;
+  PRINTER_DEFAULTS printer_defaults = {0};
+  printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
+  base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
+  if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()), &handle,
+                   &printer_defaults)) {
+    // If we can't open the printer, it was probably already removed.
+    LOG(WARNING) << "Unable to open printer";
+    return S_OK;
+  }
+  if (!DeletePrinter(handle)) {
+    HRESULT result = GetLastHResult();
+    LOG(ERROR) << "Unable to delete printer";
+    ClosePrinter(handle);
+    return result;
+  }
+  ClosePrinter(handle);
+  return S_OK;
+}
+
+bool IsOSSupported() {
+  // We don't support Vista or older.
+  base::win::Version version = base::win::GetVersion();
+  return (version >= base::win::VERSION_WIN7);
+}
+
+HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
+  HRESULT result = S_OK;
+
+  DCHECK(base::DirectoryExists(install_path));
+  if (!IsOSSupported()) {
+    LOG(ERROR) << "Requires Windows 7 or later.";
+    return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
+  }
+
+  result = InstallDriver(install_path);
+  if (FAILED(result)) {
+    LOG(ERROR) << "Unable to install driver.";
+    return result;
+  }
+
+  result = RegisterPortMonitor(true, install_path);
+  if (FAILED(result)) {
+    LOG(ERROR) << "Unable to register port monitor.";
+    return result;
+  }
+
+  result = InstallPrinter();
+  if (FAILED(result) &&
+      result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
+    LOG(ERROR) << "Unable to install printer.";
+    return result;
+  }
+  return S_OK;
+}
+
+HRESULT TryUnregisterVirtualDriver(const base::FilePath& install_path) {
+  HRESULT result = S_OK;
+  result = UninstallPrinter();
+  if (FAILED(result)) {
+    LOG(ERROR) << "Unable to delete printer.";
+    return result;
+  }
+  result = UninstallDriver(install_path);
+  if (FAILED(result)) {
+    LOG(ERROR) << "Unable to remove driver.";
+    return result;
+  }
+  // The second argument is ignored if the first is false.
+  result = RegisterPortMonitor(false, base::FilePath());
+  if (FAILED(result)) {
+    LOG(ERROR) << "Unable to remove port monitor.";
+    return result;
+  }
+  return S_OK;
+}
+
+HRESULT UnregisterVirtualDriver(const base::FilePath& install_path) {
+  HRESULT hr = S_FALSE;
+  for (int i = 0; i < 2; ++i) {
+    hr = TryUnregisterVirtualDriver(install_path);
+    if (SUCCEEDED(hr)) {
+      break;
+    }
+    // Restart spooler and try again.
+    SpoolerServiceCommand("stop");
+    SpoolerServiceCommand("start");
+  }
+  return hr;
+}
+
+HRESULT DoUninstall(const base::FilePath& install_path) {
+  DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
+  HRESULT result = UnregisterVirtualDriver(install_path);
+  if (FAILED(result))
+    return result;
+  DeleteUninstallKey(kUninstallId);
+  DeleteProgramDir(kDelete);
+  return S_OK;
+}
+
+HRESULT DoUnregister(const base::FilePath& install_path) {
+  return UnregisterVirtualDriver(install_path);
+}
+
+HRESULT DoRegister(const base::FilePath& install_path) {
+  HRESULT result = UnregisterVirtualDriver(install_path);
+  if (FAILED(result))
+    return result;
+  return RegisterVirtualDriver(install_path);
+}
+
+HRESULT DoDelete(const base::FilePath& install_path) {
+  if (install_path.value().empty())
+    return E_INVALIDARG;
+  if (!base::DirectoryExists(install_path))
+    return S_FALSE;
+  Sleep(5000);  // Give parent some time to exit.
+  return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
+}
+
+HRESULT DoInstall(const base::FilePath& install_path) {
+  HRESULT result = UnregisterVirtualDriver(install_path);
+  if (FAILED(result)) {
+    LOG(ERROR) << "Unable to unregister.";
+    return result;
+  }
+  base::FilePath old_install_path = GetInstallLocation(kUninstallId);
+  if (!old_install_path.value().empty() && install_path != old_install_path) {
+    if (base::DirectoryExists(old_install_path))
+      base::DeleteFile(old_install_path, true);
+  }
+  CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
+                     kUninstallSwitch);
+  result = RegisterVirtualDriver(install_path);
+  if (FAILED(result))
+    return result;
+  SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
+  return result;
+}
+
+HRESULT ExecuteCommands() {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  base::FilePath exe_path;
+  if (!PathService::Get(base::DIR_EXE, &exe_path) ||
+      !base::DirectoryExists(exe_path)) {
+    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  }
+
+  if (command_line.HasSwitch(kDelete)) {
+    return DoDelete(command_line.GetSwitchValuePath(kDelete));
+  } else if (command_line.HasSwitch(kUninstallSwitch)) {
+    return DoUninstall(exe_path);
+  } else if (command_line.HasSwitch(kInstallSwitch)) {
+    return DoInstall(exe_path);
+  } else if (command_line.HasSwitch(kUnregisterSwitch)) {
+    return DoUnregister(exe_path);
+  } else if (command_line.HasSwitch(kRegisterSwitch)) {
+    return DoRegister(exe_path);
+  }
+
+  return E_INVALIDARG;
+}
+
+}  // namespace
+
+}  // namespace cloud_print
+
+int WINAPI WinMain(__in HINSTANCE hInstance,
+                   __in HINSTANCE hPrevInstance,
+                   __in LPSTR lpCmdLine,
+                   __in int nCmdShow) {
+  using namespace cloud_print;
+
+  base::AtExitManager at_exit_manager;
+  base::CommandLine::Init(0, NULL);
+
+  HRESULT retval = ExecuteCommands();
+
+  if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) {
+    SetGoogleUpdateError(kGoogleUpdateProductId,
+                         LoadLocalString(IDS_ERROR_NO_XPS));
+  } else if (FAILED(retval)) {
+    SetGoogleUpdateError(kGoogleUpdateProductId, retval);
+  }
+
+  VLOG(0) << GetErrorMessage(retval) << " HRESULT=0x" << std::setbase(16)
+          << retval;
+
+  // Installer is silent by default as required by Google Update.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
+    DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME));
+  }
+  return retval;
+}
diff --git a/cloud_print/virtual_driver/win/install/virtual_driver_setup_exe.ver b/cloud_print/virtual_driver/win/install/virtual_driver_setup_exe.ver
new file mode 100644
index 0000000..1667c07
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/virtual_driver_setup_exe.ver
@@ -0,0 +1,3 @@
+INTERNAL_NAME=virtual_driver_setup.exe
+ORIGINAL_FILENAME=virtual_driver_setup.exe
+FILETYPE=0x1L
diff --git a/cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd b/cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd
new file mode 100644
index 0000000..e9f9713
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+
+<grit latest_public_release="0" current_release="1">
+  <outputs>
+    <output filename="grit/virtual_driver_setup_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="virtual_driver_setup_resources_ar.rc" type="rc_all" lang="ar" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_bg.rc" type="rc_all" lang="bg" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_bn.rc" type="rc_all" lang="bn" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ca.rc" type="rc_all" lang="ca" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_cs.rc" type="rc_all" lang="cs" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_da.rc" type="rc_all" lang="da" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_de.rc" type="rc_all" lang="de" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_el.rc" type="rc_all" lang="el" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_en.rc" type="rc_all" lang="en" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_en-GB.rc" type="rc_all" lang="en-GB" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_es.rc" type="rc_all" lang="es" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_es-419.rc" type="rc_all" lang="es-419" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_et.rc" type="rc_all" lang="et" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_fa.rc" type="rc_all" lang="fa" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_fi.rc" type="rc_all" lang="fi" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_fil.rc" type="rc_all" lang="fil"  language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_fr.rc" type="rc_all" lang="fr" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_gu.rc" type="rc_all" lang="gu" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_he.rc" type="rc_all" lang="he" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_hi.rc" type="rc_all" lang="hi" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_hr.rc" type="rc_all" lang="hr" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_hu.rc" type="rc_all" lang="hu" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_id.rc" type="rc_all" lang="id" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_it.rc" type="rc_all" lang="it" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ja.rc" type="rc_all" lang="ja" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_kn.rc" type="rc_all" lang="kn" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ko.rc" type="rc_all" lang="ko" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_lt.rc" type="rc_all" lang="lt" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_lv.rc" type="rc_all" lang="lv" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ml.rc" type="rc_all" lang="ml" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_mr.rc" type="rc_all" lang="mr" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ms.rc" type="rc_all" lang="ms" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_nl.rc" type="rc_all" lang="nl" language_section="lang"/>
+    <!-- The translation console uses 'no' for Norwegian Bokmål. It should be 'nb'. -->
+    <output filename="virtual_driver_setup_resources_nb.rc" type="rc_all" lang="no" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_pl.rc" type="rc_all" lang="pl" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_pt-BR.rc" type="rc_all" lang="pt-BR" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_pt-PT.rc" type="rc_all" lang="pt-PT" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ro.rc" type="rc_all" lang="ro" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ru.rc" type="rc_all" lang="ru" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_sk.rc" type="rc_all" lang="sk" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_sl.rc" type="rc_all" lang="sl" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_sr.rc" type="rc_all" lang="sr" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_sv.rc" type="rc_all" lang="sv" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_sw.rc" type="rc_all" lang="sw" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_ta.rc" type="rc_all" lang="ta" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_te.rc" type="rc_all" lang="te" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_th.rc" type="rc_all" lang="th" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_tr.rc" type="rc_all" lang="tr" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_uk.rc" type="rc_all" lang="uk" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_vi.rc" type="rc_all" lang="vi" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_zh-CN.rc" type="rc_all" lang="zh-CN" language_section="lang"/>
+    <output filename="virtual_driver_setup_resources_zh-TW.rc" type="rc_all" lang="zh-TW" language_section="lang"/>
+  </outputs>
+  <translations>
+    <file path="resources/virtual_driver_setup_resources_ar.xtb" lang="ar"/>
+    <file path="resources/virtual_driver_setup_resources_bg.xtb" lang="bg"/>
+    <file path="resources/virtual_driver_setup_resources_bn.xtb" lang="bn"/>
+    <file path="resources/virtual_driver_setup_resources_ca.xtb" lang="ca"/>
+    <file path="resources/virtual_driver_setup_resources_cs.xtb" lang="cs"/>
+    <file path="resources/virtual_driver_setup_resources_da.xtb" lang="da"/>
+    <file path="resources/virtual_driver_setup_resources_de.xtb" lang="de"/>
+    <file path="resources/virtual_driver_setup_resources_el.xtb" lang="el"/>
+    <file path="resources/virtual_driver_setup_resources_en-GB.xtb" lang="en-GB"/>
+    <file path="resources/virtual_driver_setup_resources_es.xtb" lang="es"/>
+    <file path="resources/virtual_driver_setup_resources_es-419.xtb" lang="es-419"/>
+    <file path="resources/virtual_driver_setup_resources_et.xtb" lang="et"/>
+    <file path="resources/virtual_driver_setup_resources_fa.xtb" lang="fa"/>
+    <file path="resources/virtual_driver_setup_resources_fi.xtb" lang="fi"/>
+    <file path="resources/virtual_driver_setup_resources_fil.xtb" lang="fil" />
+    <file path="resources/virtual_driver_setup_resources_fr.xtb" lang="fr" />
+    <file path="resources/virtual_driver_setup_resources_gu.xtb" lang="gu" />
+    <file path="resources/virtual_driver_setup_resources_he.xtb" lang="he" />
+    <file path="resources/virtual_driver_setup_resources_hi.xtb" lang="hi" />
+    <file path="resources/virtual_driver_setup_resources_hr.xtb" lang="hr" />
+    <file path="resources/virtual_driver_setup_resources_hu.xtb" lang="hu" />
+    <file path="resources/virtual_driver_setup_resources_id.xtb" lang="id" />
+    <file path="resources/virtual_driver_setup_resources_it.xtb" lang="it" />
+    <file path="resources/virtual_driver_setup_resources_ja.xtb" lang="ja" />
+    <file path="resources/virtual_driver_setup_resources_kn.xtb" lang="kn" />
+    <file path="resources/virtual_driver_setup_resources_ko.xtb" lang="ko" />
+    <file path="resources/virtual_driver_setup_resources_lt.xtb" lang="lt" />
+    <file path="resources/virtual_driver_setup_resources_lv.xtb" lang="lv" />
+    <file path="resources/virtual_driver_setup_resources_ml.xtb" lang="ml" />
+    <file path="resources/virtual_driver_setup_resources_mr.xtb" lang="mr" />
+    <file path="resources/virtual_driver_setup_resources_ms.xtb" lang="ms" />
+    <file path="resources/virtual_driver_setup_resources_nl.xtb" lang="nl" />
+    <file path="resources/virtual_driver_setup_resources_no.xtb" lang="no" />
+    <file path="resources/virtual_driver_setup_resources_pl.xtb" lang="pl" />
+    <file path="resources/virtual_driver_setup_resources_pt-BR.xtb" lang="pt-BR" />
+    <file path="resources/virtual_driver_setup_resources_pt-PT.xtb" lang="pt-PT" />
+    <file path="resources/virtual_driver_setup_resources_ro.xtb" lang="ro" />
+    <file path="resources/virtual_driver_setup_resources_ru.xtb" lang="ru" />
+    <file path="resources/virtual_driver_setup_resources_sk.xtb" lang="sk" />
+    <file path="resources/virtual_driver_setup_resources_sl.xtb" lang="sl" />
+    <file path="resources/virtual_driver_setup_resources_sr.xtb" lang="sr" />
+    <file path="resources/virtual_driver_setup_resources_sv.xtb" lang="sv" />
+    <file path="resources/virtual_driver_setup_resources_sw.xtb" lang="sw" />
+    <file path="resources/virtual_driver_setup_resources_ta.xtb" lang="ta" />
+    <file path="resources/virtual_driver_setup_resources_te.xtb" lang="te" />
+    <file path="resources/virtual_driver_setup_resources_th.xtb" lang="th" />
+    <file path="resources/virtual_driver_setup_resources_tr.xtb" lang="tr" />
+    <file path="resources/virtual_driver_setup_resources_uk.xtb" lang="uk" />
+    <file path="resources/virtual_driver_setup_resources_vi.xtb" lang="vi" />
+    <file path="resources/virtual_driver_setup_resources_zh-CN.xtb" lang="zh-CN" />
+    <file path="resources/virtual_driver_setup_resources_zh-TW.xtb" lang="zh-TW" />
+  </translations>
+    <release seq="1">
+    <messages>
+      <message name="IDS_GOOGLE"
+               meaning="Google" desc="The name of the company that built this.">
+        Google
+      </message>
+      <message name="IDS_DRIVER_NAME"
+               meaning="Driver Name" desc="The user visible name of the printer we create and its associated driver.">
+        Google Cloud Printer
+      </message>
+      <message name="IDS_ERROR_NO_XPS"
+               meaning="Error message" desc="XPS driver is not installed.">
+        <ph name="xps_url">&lt;a href=&quot;https://www.microsoft.com/download/details.aspx?id=11816&quot;&gt;<ex>&lt;a href=&quot;https://www.microsoft.com/download/details.aspx?id=11816&quot;&gt;</ex></ph>XPS driver<ph name="xps_url_end">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> is not installed.
+      </message>
+    </messages>
+    <includes>
+      <if expr="lang == 'en'">
+        <include name="IDI_ICON" file="resources/cloudprint.ico" type="ICON" translateable="false" />
+      </if>
+    </includes>
+  </release>
+</grit>
diff --git a/cloud_print/virtual_driver/win/port_monitor/BUILD.gn b/cloud_print/virtual_driver/win/port_monitor/BUILD.gn
new file mode 100644
index 0000000..43c92f2
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/process_version_rc_template.gni")
+
+assert(is_win)
+
+group("port_monitor") {
+  public_deps = [
+    ":port_monitor_dll",
+  ]
+}
+
+shared_library("port_monitor_dll") {
+  output_name = "gcp_portmon"
+
+  sources = [
+    "port_monitor.def",
+    "port_monitor_dll.cc",
+  ]
+
+  deps = [
+    ":lib",
+    ":resources",
+    "//base",
+    "//chrome/common:constants",
+    "//chrome/common:version_header",
+    "//cloud_print/common",
+    "//cloud_print/virtual_driver/win",
+  ]
+
+  libs = [ "userenv.lib" ]
+}
+
+source_set("lib") {
+  sources = [
+    "port_monitor.cc",
+    "port_monitor.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/common:constants",
+    "//chrome/installer/launcher_support",
+    "//cloud_print/common",
+    "//cloud_print/virtual_driver/win",
+  ]
+}
+
+process_version_rc_template("resources") {
+  sources = [
+    "../gcp_portmon_dll.ver",
+  ]
+
+  # Note: target_gen_dir will be different for each toolchain so the output
+  # name doesn't need mangling.
+  output = "$target_gen_dir/gcp_portmon_dll.rc"
+}
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc
new file mode 100644
index 0000000..8748bb6
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc
@@ -0,0 +1,689 @@
+// 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 "cloud_print/virtual_driver/win/port_monitor/port_monitor.h"
+
+#include <windows.h>
+#include <lmcons.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <strsafe.h>
+#include <userenv.h>
+#include <winspool.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/installer/launcher_support/chrome_launcher_support.h"
+#include "cloud_print/common/win/cloud_print_utils.h"
+#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
+
+namespace cloud_print {
+
+namespace {
+
+const wchar_t kIePath[] = L"Internet Explorer\\iexplore.exe";
+
+const char kChromeInstallUrl[] =
+    "https://google.com/cloudprint/learn/chrome.html";
+
+const wchar_t kCloudPrintRegKey[] = L"Software\\Google\\CloudPrint";
+
+const wchar_t kXpsMimeType[] = L"application/vnd.ms-xpsdocument";
+
+const wchar_t kAppDataDir[] = L"Google\\Cloud Printer";
+
+const wchar_t kDocumentPathPlaceHolder[] = L"%%Document_Path%%";
+
+const wchar_t kDocumentTypePlaceHolder[] = L"%%Document_Type%%";
+
+const wchar_t kJobTitlePlaceHolder[] = L"%%Job_Title%%";
+
+struct MonitorData {
+  std::unique_ptr<base::AtExitManager> at_exit_manager;
+};
+
+struct PortData {
+  PortData() : job_id(0), printer_handle(NULL), file(0) {}
+  ~PortData() { Close(); }
+  void Close() {
+    if (printer_handle) {
+      ClosePrinter(printer_handle);
+      printer_handle = NULL;
+    }
+    if (file) {
+      base::CloseFile(file);
+      file = NULL;
+    }
+  }
+  DWORD job_id;
+  HANDLE printer_handle;
+  FILE* file;
+  base::FilePath file_path;
+};
+
+typedef struct { ACCESS_MASK granted_access; } XcvUiData;
+
+MONITORUI g_monitor_ui = {sizeof(MONITORUI), MonitorUiAddPortUi,
+                          MonitorUiConfigureOrDeletePortUI,
+                          MonitorUiConfigureOrDeletePortUI};
+
+MONITOR2 g_monitor_2 = {sizeof(MONITOR2),
+                        Monitor2EnumPorts,
+                        Monitor2OpenPort,
+                        NULL,  // OpenPortEx is not supported.
+                        Monitor2StartDocPort,
+                        Monitor2WritePort,
+                        Monitor2ReadPort,
+                        Monitor2EndDocPort,
+                        Monitor2ClosePort,
+                        NULL,  // AddPort is not supported.
+                        NULL,  // AddPortEx is not supported.
+                        NULL,  // ConfigurePort is not supported.
+                        NULL,  // DeletePort is not supported.
+                        NULL,
+                        NULL,  // SetPortTimeOuts is not supported.
+                        Monitor2XcvOpenPort,
+                        Monitor2XcvDataPort,
+                        Monitor2XcvClosePort,
+                        Monitor2Shutdown};
+
+base::FilePath GetLocalAppDataLow() {
+  wchar_t system_buffer[MAX_PATH];
+  if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
+                             system_buffer)))
+    return base::FilePath();
+  return base::FilePath(system_buffer).DirName().AppendASCII("LocalLow");
+}
+
+base::FilePath GetAppDataDir() {
+  base::FilePath file_path;
+  if (base::win::GetVersion() >= base::win::VERSION_VISTA)
+    file_path = GetLocalAppDataLow();
+  else
+    PathService::Get(base::DIR_LOCAL_APP_DATA, &file_path);
+  if (file_path.empty()) {
+    LOG(ERROR) << "Can't get app data dir";
+  }
+  return file_path.Append(kAppDataDir);
+}
+
+// Delete files which where not deleted by chrome.
+void DeleteLeakedFiles(const base::FilePath& dir) {
+  base::Time delete_before = base::Time::Now() - base::TimeDelta::FromDays(1);
+  base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES);
+  for (base::FilePath file_path = enumerator.Next(); !file_path.empty();
+       file_path = enumerator.Next()) {
+    if (enumerator.GetInfo().GetLastModifiedTime() < delete_before)
+      base::DeleteFile(file_path, false);
+  }
+}
+
+// Attempts to retrieve the title of the specified print job.
+// On success returns TRUE and the first title_chars characters of the job title
+// are copied into title.
+// On failure returns FALSE and title is unmodified.
+bool GetJobTitle(HANDLE printer_handle, DWORD job_id, base::string16* title) {
+  DCHECK(printer_handle != NULL);
+  DCHECK(title != NULL);
+  DWORD bytes_needed = 0;
+  GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed);
+  if (bytes_needed == 0) {
+    LOG(ERROR) << "Unable to get bytes needed for job info.";
+    return false;
+  }
+  std::unique_ptr<BYTE[]> buffer(new BYTE[bytes_needed]);
+  if (!GetJob(printer_handle, job_id, 1, buffer.get(), bytes_needed,
+              &bytes_needed)) {
+    LOG(ERROR) << "Unable to get job info.";
+    return false;
+  }
+  JOB_INFO_1* job_info = reinterpret_cast<JOB_INFO_1*>(buffer.get());
+  *title = job_info->pDocument;
+  return true;
+}
+
+// Handler for the UI functions exported by the port monitor.
+// Verifies that a valid parent Window exists and then just displays an
+// error message to let the user know that there is no interactive
+// configuration.
+void HandlePortUi(HWND hwnd, const base::string16& caption) {
+  if (hwnd != NULL && IsWindow(hwnd)) {
+    DisplayWindowsMessage(hwnd, CO_E_NOT_SUPPORTED, cloud_print::kPortName);
+  }
+}
+
+// Gets the primary token for the user that submitted the print job.
+bool GetUserToken(HANDLE* primary_token) {
+  HANDLE token = NULL;
+  if (!OpenThreadToken(GetCurrentThread(),
+                       TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY,
+                       FALSE, &token)) {
+    LOG(ERROR) << "Unable to get thread token.";
+    return false;
+  }
+  base::win::ScopedHandle token_scoped(token);
+  if (!DuplicateTokenEx(
+          token, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, NULL,
+          SecurityImpersonation, TokenPrimary, primary_token)) {
+    LOG(ERROR) << "Unable to get primary thread token.";
+    return false;
+  }
+  return true;
+}
+
+bool LaunchCommandAsUser(const base::CommandLine& command) {
+  HANDLE token = NULL;
+  if (!GetUserToken(&token)) {
+    LOG(ERROR) << "Unable to get user token.";
+    return false;
+  }
+  base::win::ScopedHandle primary_token_scoped(token);
+  base::LaunchOptions options;
+  options.as_user = primary_token_scoped.Get();
+  base::LaunchProcess(command, options);
+  return true;
+}
+
+// Escape the command line argument as necessary per Microsoft rules.
+// See QuoteForCommandLineToArgvW in base/command_line.cc
+base::string16 EscapeCommandLineArg(const base::string16& arg) {
+  base::string16 quotable_chars(L" \\\"");
+  if (arg.find_first_of(quotable_chars) == base::string16::npos) {
+    // No quoting necessary.
+    return arg;
+  }
+
+  base::string16 out;
+  out.push_back(L'"');
+  for (size_t i = 0; i < arg.size(); ++i) {
+    if (arg[i] == '\\') {
+      // Find the extent of this run of backslashes.
+      size_t start = i, end = start + 1;
+      for (; end < arg.size() && arg[end] == '\\'; ++end) {
+      }
+      size_t backslash_count = end - start;
+
+      // Backslashes are escapes only if the run is followed by a double quote.
+      // Since we also will end the string with a double quote, we escape for
+      // either a double quote or the end of the string.
+      if (end == arg.size() || arg[end] == '"') {
+        // To quote, we need to output 2x as many backslashes.
+        backslash_count *= 2;
+      }
+      for (size_t j = 0; j < backslash_count; ++j)
+        out.push_back('\\');
+
+      // Advance i to one before the end to balance i++ in loop.
+      i = end - 1;
+    } else if (arg[i] == '"') {
+      out.push_back('\\');
+      out.push_back('"');
+    } else {
+      out.push_back(arg[i]);
+    }
+  }
+  out.push_back('"');
+
+  return out;
+}
+
+// Launch the print command as specified in the cloud print registry.
+bool LaunchPrintCommandFromTemplate(const base::string16& command_template,
+                                    const base::FilePath& xps_path,
+                                    const base::string16& job_title) {
+  base::string16 command_string(command_template);
+  // Substitude the place holder with the document path wrapped in quotes.
+  base::ReplaceFirstSubstringAfterOffset(
+      &command_string, 0, kDocumentPathPlaceHolder,
+      EscapeCommandLineArg(xps_path.value()));
+  // Substitude the place holder with the document type wrapped in quotes.
+  base::ReplaceFirstSubstringAfterOffset(
+      &command_string, 0, kDocumentTypePlaceHolder, kXpsMimeType);
+  // Substitude the place holder with the job title wrapped in quotes.
+  base::ReplaceFirstSubstringAfterOffset(&command_string, 0,
+                                         kJobTitlePlaceHolder,
+                                         EscapeCommandLineArg(job_title));
+
+  base::CommandLine command = base::CommandLine::FromString(command_string);
+
+  return LaunchCommandAsUser(command);
+}
+
+// Launches a page to allow the user to download chrome.
+// TODO(abodenha@chromium.org) Point to a custom page explaining what's wrong
+// rather than the generic chrome download page.  See
+// http://code.google.com/p/chromium/issues/detail?id=112019
+void LaunchChromeDownloadPage() {
+  if (kIsUnittest)
+    return;
+  HANDLE token = NULL;
+  if (!GetUserToken(&token)) {
+    LOG(ERROR) << "Unable to get user token.";
+    return;
+  }
+  base::win::ScopedHandle token_scoped(token);
+
+  // Consider using the shell to invoke the default browser instead of hardcoded
+  // reference to IE which might not be available on the system.
+  base::FilePath ie_path;
+  PathService::Get(base::DIR_PROGRAM_FILESX86, &ie_path);
+  ie_path = ie_path.Append(kIePath);
+  base::CommandLine command_line(ie_path);
+  command_line.AppendArg(kChromeInstallUrl);
+
+  base::LaunchOptions options;
+  options.as_user = token_scoped.Get();
+  base::LaunchProcess(command_line, options);
+}
+
+// Returns false if the print job is being run in a context
+// that shouldn't be launching Chrome.
+bool ValidateCurrentUser() {
+  HANDLE token = NULL;
+  if (!GetUserToken(&token)) {
+    // If we can't get the token we're probably not impersonating
+    // the user, so validation should fail.
+    return false;
+  }
+  base::win::ScopedHandle token_scoped(token);
+
+  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+    DWORD session_id = 0;
+    DWORD dummy;
+    if (!GetTokenInformation(token_scoped.Get(), TokenSessionId,
+                             reinterpret_cast<void*>(&session_id),
+                             sizeof(DWORD), &dummy)) {
+      return false;
+    }
+    if (session_id == 0) {
+      return false;
+    }
+  }
+  return true;
+}
+}  // namespace
+
+base::string16 ReadStringFromRegistry(HKEY root, const wchar_t* path_name) {
+  base::win::RegKey gcp_key(root, kCloudPrintRegKey, KEY_READ);
+  base::string16 data;
+  gcp_key.ReadValue(path_name, &data);
+  return data;
+}
+
+base::string16 ReadStringFromAnyRegistry(const wchar_t* path_name) {
+  base::string16 result = ReadStringFromRegistry(HKEY_CURRENT_USER, path_name);
+  if (!result.empty())
+    return result;
+  return ReadStringFromRegistry(HKEY_LOCAL_MACHINE, path_name);
+}
+
+base::FilePath GetChromeExePath() {
+  base::string16 value = ReadStringFromAnyRegistry(kChromeExePathRegValue);
+  if (!value.empty() && base::PathExists(base::FilePath(value)))
+    return base::FilePath(value);
+  return chrome_launcher_support::GetAnyChromePath(false /* is_sxs */);
+}
+
+base::FilePath GetChromeProfilePath() {
+  base::string16 value = ReadStringFromAnyRegistry(kChromeProfilePathRegValue);
+  if (!value.empty() && base::DirectoryExists(base::FilePath(value)))
+    return base::FilePath(value);
+  return base::FilePath();
+}
+
+// Launches the Cloud Print dialog in Chrome.
+bool LaunchChromePrintDialog(const base::FilePath& xps_path,
+                             const base::string16& job_title) {
+  base::FilePath chrome_path = GetChromeExePath();
+  if (chrome_path.empty()) {
+    LOG(ERROR) << "Unable to get chrome exe path.";
+    LaunchChromeDownloadPage();
+    return false;
+  }
+
+  base::CommandLine command_line(chrome_path);
+
+  base::FilePath chrome_profile = GetChromeProfilePath();
+  if (!chrome_profile.empty())
+    command_line.AppendSwitchPath(switches::kUserDataDir, chrome_profile);
+
+  command_line.AppendSwitchPath(switches::kCloudPrintFile, xps_path);
+  command_line.AppendSwitchNative(switches::kCloudPrintFileType, kXpsMimeType);
+  command_line.AppendSwitchNative(switches::kCloudPrintJobTitle, job_title);
+
+  return LaunchCommandAsUser(command_line);
+}
+
+base::string16 GetPrintCommandTemplate() {
+  return ReadStringFromAnyRegistry(kPrintCommandRegValue);
+}
+
+// Launches the print command. This will either launch Chrome to display the
+// Cloud Print dialog or another exe as specified in the cloud print registry.
+// xps_path references a file to print.
+// job_title is the title to be used for the resulting print job.
+bool LaunchPrintCommand(const base::FilePath& xps_path,
+                        const base::string16& job_title) {
+  base::string16 command_template = GetPrintCommandTemplate();
+  if (!command_template.empty()) {
+    return LaunchPrintCommandFromTemplate(command_template, xps_path,
+                                          job_title);
+  } else {
+    return LaunchChromePrintDialog(xps_path, job_title);
+  }
+}
+
+BOOL WINAPI Monitor2EnumPorts(HANDLE,
+                              wchar_t*,
+                              DWORD level,
+                              BYTE* ports,
+                              DWORD ports_size,
+                              DWORD* needed_bytes,
+                              DWORD* returned) {
+  if (needed_bytes == NULL) {
+    LOG(ERROR) << "needed_bytes should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  if (level == 1) {
+    *needed_bytes = sizeof(PORT_INFO_1);
+  } else if (level == 2) {
+    *needed_bytes = sizeof(PORT_INFO_2);
+  } else {
+    LOG(ERROR) << "Level " << level << "is not supported.";
+    SetLastError(ERROR_INVALID_LEVEL);
+    return FALSE;
+  }
+  *needed_bytes += static_cast<DWORD>(cloud_print::kPortNameSize);
+  if (ports_size < *needed_bytes) {
+    LOG(WARNING) << *needed_bytes << " bytes are required.  Only " << ports_size
+                 << " were allocated.";
+    SetLastError(ERROR_INSUFFICIENT_BUFFER);
+    return FALSE;
+  }
+  if (ports == NULL) {
+    LOG(ERROR) << "ports should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  if (returned == NULL) {
+    LOG(ERROR) << "returned should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+
+  // Windows expects any strings refernced by PORT_INFO_X structures to
+  // appear at the END of the buffer referenced by ports.  Placing
+  // strings immediately after the PORT_INFO_X structure will cause
+  // EnumPorts to fail until the spooler is restarted.
+  // This is NOT mentioned in the documentation.
+  wchar_t* string_target = reinterpret_cast<wchar_t*>(
+      ports + ports_size - cloud_print::kPortNameSize);
+  if (level == 1) {
+    PORT_INFO_1* port_info = reinterpret_cast<PORT_INFO_1*>(ports);
+    port_info->pName = string_target;
+    StringCbCopy(port_info->pName, cloud_print::kPortNameSize,
+                 cloud_print::kPortName);
+  } else {
+    PORT_INFO_2* port_info = reinterpret_cast<PORT_INFO_2*>(ports);
+    port_info->pPortName = string_target;
+    StringCbCopy(port_info->pPortName, cloud_print::kPortNameSize,
+                 cloud_print::kPortName);
+    port_info->pMonitorName = NULL;
+    port_info->pDescription = NULL;
+    port_info->fPortType = PORT_TYPE_WRITE;
+    port_info->Reserved = 0;
+  }
+  *returned = 1;
+  return TRUE;
+}
+
+BOOL WINAPI Monitor2OpenPort(HANDLE, wchar_t*, HANDLE* handle) {
+  if (handle == NULL) {
+    LOG(ERROR) << "handle should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  *handle = new PortData();
+  return TRUE;
+}
+
+BOOL WINAPI Monitor2StartDocPort(HANDLE port_handle,
+                                 wchar_t* printer_name,
+                                 DWORD job_id,
+                                 DWORD,
+                                 BYTE*) {
+  SetGoogleUpdateUsage(kGoogleUpdateProductId);
+  if (port_handle == NULL) {
+    LOG(ERROR) << "port_handle should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  if (printer_name == NULL) {
+    LOG(ERROR) << "printer_name should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  if (!ValidateCurrentUser()) {
+    // TODO(abodenha@chromium.org) Abort the print job.
+    return FALSE;
+  }
+  PortData* port_data = reinterpret_cast<PortData*>(port_handle);
+  port_data->job_id = job_id;
+  if (!OpenPrinter(printer_name, &(port_data->printer_handle), NULL)) {
+    LOG(WARNING) << "Unable to open printer " << printer_name << ".";
+    // We can continue without a handle to the printer.
+    // It just means we can't get the job title or tell the spooler that
+    // the print job is complete.
+    // This is the normal flow during a unit test.
+    port_data->printer_handle = NULL;
+  }
+  base::FilePath& file_path = port_data->file_path;
+  base::FilePath app_data_dir = GetAppDataDir();
+  if (app_data_dir.empty())
+    return FALSE;
+  DeleteLeakedFiles(app_data_dir);
+  if (!base::CreateDirectory(app_data_dir) ||
+      !base::CreateTemporaryFileInDir(app_data_dir, &file_path)) {
+    LOG(ERROR) << "Can't create temporary file in " << app_data_dir.value();
+    return FALSE;
+  }
+  port_data->file = base::OpenFile(file_path, "wb+");
+  if (port_data->file == NULL) {
+    LOG(ERROR) << "Error opening file " << file_path.value() << ".";
+    return FALSE;
+  }
+  return TRUE;
+}
+
+BOOL WINAPI Monitor2WritePort(HANDLE port_handle,
+                              BYTE* buffer,
+                              DWORD buffer_size,
+                              DWORD* bytes_written) {
+  PortData* port_data = reinterpret_cast<PortData*>(port_handle);
+  if (!ValidateCurrentUser()) {
+    // TODO(abodenha@chromium.org) Abort the print job.
+    return FALSE;
+  }
+  *bytes_written =
+      static_cast<DWORD>(fwrite(buffer, 1, buffer_size, port_data->file));
+  if (*bytes_written > 0) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+BOOL WINAPI Monitor2ReadPort(HANDLE, BYTE*, DWORD, DWORD* read_bytes) {
+  LOG(ERROR) << "Read is not supported.";
+  *read_bytes = 0;
+  SetLastError(ERROR_NOT_SUPPORTED);
+  return FALSE;
+}
+
+BOOL WINAPI Monitor2EndDocPort(HANDLE port_handle) {
+  if (!ValidateCurrentUser()) {
+    // TODO(abodenha@chromium.org) Abort the print job.
+    return FALSE;
+  }
+  PortData* port_data = reinterpret_cast<PortData*>(port_handle);
+  if (port_data == NULL) {
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+
+  if (port_data->file != NULL) {
+    base::CloseFile(port_data->file);
+    port_data->file = NULL;
+    bool delete_file = true;
+    int64_t file_size = 0;
+    base::GetFileSize(port_data->file_path, &file_size);
+    if (file_size > 0) {
+      base::string16 job_title;
+      if (port_data->printer_handle != NULL) {
+        GetJobTitle(port_data->printer_handle, port_data->job_id, &job_title);
+      }
+      if (LaunchPrintCommand(port_data->file_path, job_title)) {
+        delete_file = false;
+      }
+    }
+    if (delete_file)
+      base::DeleteFile(port_data->file_path, false);
+  }
+  if (port_data->printer_handle != NULL) {
+    // Tell the spooler that the job is complete.
+    SetJob(port_data->printer_handle, port_data->job_id, 0, NULL,
+           JOB_CONTROL_SENT_TO_PRINTER);
+  }
+  port_data->Close();
+  // Return success even if we can't display the dialog.
+  // TODO(abodenha@chromium.org) Come up with a better way of handling
+  // this situation.
+  return TRUE;
+}
+
+BOOL WINAPI Monitor2ClosePort(HANDLE port_handle) {
+  if (port_handle == NULL) {
+    LOG(ERROR) << "port_handle should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  delete reinterpret_cast<PortData*>(port_handle);
+  return TRUE;
+}
+
+VOID WINAPI Monitor2Shutdown(HANDLE monitor_handle) {
+  if (monitor_handle != NULL) {
+    delete reinterpret_cast<MonitorData*>(monitor_handle);
+  }
+}
+
+BOOL WINAPI Monitor2XcvOpenPort(HANDLE,
+                                const wchar_t*,
+                                ACCESS_MASK granted_access,
+                                HANDLE* handle) {
+  if (handle == NULL) {
+    LOG(ERROR) << "handle should not be NULL.";
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return FALSE;
+  }
+  XcvUiData* xcv_data = new XcvUiData();
+  xcv_data->granted_access = granted_access;
+  *handle = xcv_data;
+  return TRUE;
+}
+
+DWORD WINAPI Monitor2XcvDataPort(HANDLE xcv_handle,
+                                 const wchar_t* data_name,
+                                 BYTE*,
+                                 DWORD,
+                                 BYTE* output_data,
+                                 DWORD output_data_bytes,
+                                 DWORD* output_data_bytes_needed) {
+  XcvUiData* xcv_data = reinterpret_cast<XcvUiData*>(xcv_handle);
+  DWORD ret_val = ERROR_SUCCESS;
+  if ((xcv_data->granted_access & SERVER_ACCESS_ADMINISTER) == 0) {
+    return ERROR_ACCESS_DENIED;
+  }
+  if (output_data == NULL || output_data_bytes == 0) {
+    return ERROR_INVALID_PARAMETER;
+  }
+  // We don't handle AddPort or DeletePort since we don't support
+  // dynamic creation of ports.
+  if (lstrcmp(L"MonitorUI", data_name) == 0) {
+    DWORD dll_path_len = 0;
+    base::FilePath dll_path(L"gcp_portmon.dll");
+    dll_path_len = static_cast<DWORD>(dll_path.value().length());
+    if (output_data_bytes_needed != NULL) {
+      *output_data_bytes_needed = dll_path_len;
+    }
+    if (output_data_bytes < dll_path_len) {
+      return ERROR_INSUFFICIENT_BUFFER;
+    } else {
+      ret_val = StringCbCopy(reinterpret_cast<wchar_t*>(output_data),
+                             output_data_bytes, dll_path.value().c_str());
+    }
+  } else {
+    return ERROR_INVALID_PARAMETER;
+  }
+  return ret_val;
+}
+
+BOOL WINAPI Monitor2XcvClosePort(HANDLE handle) {
+  delete reinterpret_cast<XcvUiData*>(handle);
+  return TRUE;
+}
+
+BOOL WINAPI MonitorUiAddPortUi(const wchar_t*,
+                               HWND hwnd,
+                               const wchar_t* monitor_name,
+                               wchar_t**) {
+  HandlePortUi(hwnd, monitor_name);
+  return TRUE;
+}
+
+BOOL WINAPI MonitorUiConfigureOrDeletePortUI(const wchar_t*,
+                                             HWND hwnd,
+                                             const wchar_t* port_name) {
+  HandlePortUi(hwnd, port_name);
+  return TRUE;
+}
+
+}  // namespace cloud_print
+
+MONITOR2* WINAPI InitializePrintMonitor2(MONITORINIT*, HANDLE* handle) {
+  if (handle == NULL) {
+    SetLastError(ERROR_INVALID_PARAMETER);
+    return NULL;
+  }
+  cloud_print::MonitorData* monitor_data = new cloud_print::MonitorData;
+  *handle = monitor_data;
+  if (!cloud_print::kIsUnittest) {
+    // Unit tests set up their own AtExitManager
+    monitor_data->at_exit_manager.reset(new base::AtExitManager());
+    // Single spooler.exe handles verbose users.
+    PathService::DisableCache();
+  }
+  return &cloud_print::g_monitor_2;
+}
+
+MONITORUI* WINAPI InitializePrintMonitorUI(void) {
+  return &cloud_print::g_monitor_ui;
+}
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.def b/cloud_print/virtual_driver/win/port_monitor/port_monitor.def
new file mode 100644
index 0000000..3b2cd17
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/port_monitor.def
@@ -0,0 +1,9 @@
+; Copyright (c) 2011 The Chromium Authors. All rights reserved.
+; Use of this source code is governed by a BSD-style license that can be
+; found in the LICENSE file.
+
+EXPORTS        
+  InitializePrintMonitor2
+  InitializePrintMonitorUI
+  DllRegisterServer PRIVATE
+  DllUnregisterServer PRIVATE
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.h b/cloud_print/virtual_driver/win/port_monitor/port_monitor.h
new file mode 100644
index 0000000..aac9663
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/port_monitor.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_PORT_MONITOR_H_
+#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_PORT_MONITOR_H_
+
+#include <windows.h>
+#include <string>
+#include "base/files/file_util.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+
+namespace cloud_print {
+
+// Returns path to be used for launching Chrome.
+base::FilePath GetChromeExePath();
+
+// Returns path to user profile to be used for launching Chrome.
+base::FilePath GetChromeProfilePath();
+
+// Returns the print command to launch, if set, instead of Chrome.
+base::string16 GetPrintCommandTemplate();
+
+// Implementations for the function pointers in the MONITOR2 structure
+// returned by InitializePrintMonitor2.  The prototypes and behaviors
+// are as described in the MONITOR2 documentation from Microsoft.
+
+BOOL WINAPI Monitor2EnumPorts(HANDLE,
+                              wchar_t*,
+                              DWORD level,
+                              BYTE* ports,
+                              DWORD ports_size,
+                              DWORD* needed_bytes,
+                              DWORD* returned);
+
+BOOL WINAPI Monitor2OpenPort(HANDLE monitor_data, wchar_t*, HANDLE* handle);
+
+BOOL WINAPI Monitor2StartDocPort(HANDLE port_handle,
+                                 wchar_t* printer_name,
+                                 DWORD job_id,
+                                 DWORD,
+                                 BYTE*);
+
+BOOL WINAPI Monitor2WritePort(HANDLE port,
+                              BYTE* buffer,
+                              DWORD buffer_size,
+                              DWORD* bytes_written);
+
+BOOL WINAPI Monitor2ReadPort(HANDLE, BYTE*, DWORD, DWORD* bytes_read);
+
+BOOL WINAPI Monitor2EndDocPort(HANDLE port_handle);
+
+BOOL WINAPI Monitor2ClosePort(HANDLE port_handle);
+
+VOID WINAPI Monitor2Shutdown(HANDLE monitor_handle);
+
+BOOL WINAPI Monitor2XcvOpenPort(HANDLE monitor,
+                                const wchar_t*,
+                                ACCESS_MASK granted_access,
+                                HANDLE* handle);
+
+DWORD WINAPI Monitor2XcvDataPort(HANDLE xcv_handle,
+                                 const wchar_t* data_name,
+                                 BYTE*,
+                                 DWORD,
+                                 BYTE* output_data,
+                                 DWORD output_data_bytes,
+                                 DWORD* output_data_bytes_needed);
+
+BOOL WINAPI Monitor2XcvClosePort(HANDLE handle);
+
+// Implementations for the function pointers in the MONITORUI structure
+// returned by InitializePrintMonitorUI.  The prototypes and behaviors
+// are as described in the MONITORUI documentation from Microsoft.
+
+BOOL WINAPI MonitorUiAddPortUi(const wchar_t*,
+                               HWND hwnd,
+                               const wchar_t* monitor_name,
+                               wchar_t**);
+
+BOOL WINAPI MonitorUiConfigureOrDeletePortUI(const wchar_t*,
+                                             HWND hwnd,
+                                             const wchar_t* port_name);
+
+extern const wchar_t kChromeExePath[];
+extern const wchar_t kChromeExePathRegValue[];
+extern const wchar_t kChromeProfilePathRegValue[];
+extern const wchar_t kPrintCommandRegValue[];
+extern const bool kIsUnittest;
+
+}  // namespace cloud_print
+
+#endif  // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_PORT_MONITOR_H_
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor_dll.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor_dll.cc
new file mode 100644
index 0000000..66039bc9
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/port_monitor_dll.cc
@@ -0,0 +1,96 @@
+// 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 "cloud_print/virtual_driver/win/port_monitor/port_monitor.h"
+
+#include <windows.h>
+#include <lmcons.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <strsafe.h>
+#include <userenv.h>
+#include <winspool.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/process/process_info.h"
+#include "base/strings/string16.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/chrome_switches.h"
+#include "cloud_print/common/win/cloud_print_utils.h"
+#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
+
+namespace cloud_print {
+
+const wchar_t kChromeExePath[] = L"google\\chrome\\application\\chrome.exe";
+const wchar_t kChromeExePathRegValue[] = L"PathToChromeExe";
+const wchar_t kChromeProfilePathRegValue[] = L"PathToChromeProfile";
+const wchar_t kPrintCommandRegValue[] = L"PrintCommand";
+const bool kIsUnittest = false;
+
+namespace {
+
+// Returns true if Xps support is installed.
+bool XpsIsInstalled() {
+  base::FilePath xps_path;
+  if (!SUCCEEDED(GetPrinterDriverDir(&xps_path))) {
+    return false;
+  }
+  xps_path = xps_path.Append(L"mxdwdrv.dll");
+  if (!base::PathExists(xps_path)) {
+    return false;
+  }
+  return true;
+}
+
+// Returns true if registration/unregistration can be attempted.
+bool CanRegister() {
+  if (!XpsIsInstalled()) {
+    return false;
+  }
+  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+    if (base::GetCurrentProcessIntegrityLevel() != base::HIGH_INTEGRITY)
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+}  // namespace cloud_print
+
+HRESULT WINAPI DllRegisterServer(void) {
+  base::AtExitManager at_exit_manager;
+  if (!cloud_print::CanRegister()) {
+    return E_ACCESSDENIED;
+  }
+  MONITOR_INFO_2 monitor_info = {0};
+  // YUCK!!!  I can either copy the constant, const_cast, or define my own
+  // MONITOR_INFO_2 that will take const strings.
+  base::FilePath dll_path(L"gcp_portmon.dll");
+  monitor_info.pDLLName = const_cast<LPWSTR>(dll_path.value().c_str());
+  monitor_info.pName = const_cast<LPWSTR>(dll_path.value().c_str());
+  if (AddMonitor(NULL, 2, reinterpret_cast<BYTE*>(&monitor_info))) {
+    return S_OK;
+  }
+  return cloud_print::GetLastHResult();
+}
+
+HRESULT WINAPI DllUnregisterServer(void) {
+  base::AtExitManager at_exit_manager;
+  if (!cloud_print::CanRegister()) {
+    return E_ACCESSDENIED;
+  }
+  base::FilePath dll_path(L"gcp_portmon.dll");
+  if (DeleteMonitor(NULL, NULL, const_cast<LPWSTR>(dll_path.value().c_str()))) {
+    return S_OK;
+  }
+  return cloud_print::GetLastHResult();
+}
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor_unittest.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor_unittest.cc
new file mode 100644
index 0000000..3ef126c
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/port_monitor_unittest.cc
@@ -0,0 +1,248 @@
+// 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 "cloud_print/virtual_driver/win/port_monitor/port_monitor.h"
+
+#include <stddef.h>
+#include <winspool.h>
+
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_handle.h"
+#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cloud_print {
+
+const wchar_t kChromeExePath[] = L"google\\chrome\\application\\chrometest.exe";
+const wchar_t kChromeExePathRegValue[] = L"PathToChromeTestExe";
+const wchar_t kChromeProfilePathRegValue[] = L"PathToChromeTestProfile";
+const wchar_t kPrintCommandRegValue[] = L"TestPrintCommand";
+const bool kIsUnittest = true;
+
+namespace {
+
+const wchar_t kAlternateChromeExePath[] =
+    L"google\\chrome\\application\\chrometestalternate.exe";
+const wchar_t kTestPrintCommand[] = L"testprintcommand.exe";
+
+const wchar_t kCloudPrintRegKey[] = L"Software\\Google\\CloudPrint";
+
+}  // namespace
+
+class PortMonitorTest : public testing::Test {
+ public:
+  PortMonitorTest() {}
+
+ protected:
+  // Creates a registry entry pointing at a chrome
+  virtual void SetUpChromeExeRegistry() {
+    // Create a temporary chrome.exe location value.
+    base::win::RegKey key(HKEY_CURRENT_USER, cloud_print::kCloudPrintRegKey,
+                          KEY_ALL_ACCESS);
+
+    base::FilePath path;
+    PathService::Get(base::DIR_LOCAL_APP_DATA, &path);
+    path = path.Append(kAlternateChromeExePath);
+    ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(cloud_print::kChromeExePathRegValue,
+                                            path.value().c_str()));
+    base::FilePath temp;
+    PathService::Get(base::DIR_TEMP, &temp);
+    // Write any dir here.
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.WriteValue(cloud_print::kChromeProfilePathRegValue,
+                             temp.value().c_str()));
+
+    ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(cloud_print::kPrintCommandRegValue,
+                                            kTestPrintCommand));
+  }
+  // Deletes the registry entry created in SetUpChromeExeRegistry
+  virtual void DeleteChromeExeRegistry() {
+    base::win::RegKey key(HKEY_CURRENT_USER, cloud_print::kCloudPrintRegKey,
+                          KEY_ALL_ACCESS);
+    key.DeleteValue(cloud_print::kChromeExePathRegValue);
+    key.DeleteValue(cloud_print::kChromeProfilePathRegValue);
+    key.DeleteValue(cloud_print::kPrintCommandRegValue);
+  }
+
+  virtual void CreateTempChromeExeFiles() {
+    base::FilePath path;
+    PathService::Get(base::DIR_LOCAL_APP_DATA, &path);
+    base::FilePath main_path = path.Append(kChromeExePath);
+    ASSERT_TRUE(base::CreateDirectory(main_path));
+    base::FilePath alternate_path = path.Append(kAlternateChromeExePath);
+    ASSERT_TRUE(base::CreateDirectory(alternate_path));
+  }
+
+  virtual void DeleteTempChromeExeFiles() {
+    base::FilePath path;
+    PathService::Get(base::DIR_LOCAL_APP_DATA, &path);
+    base::FilePath main_path = path.Append(kChromeExePath);
+    ASSERT_TRUE(base::DeleteFile(main_path, true));
+    PathService::Get(base::DIR_LOCAL_APP_DATA, &path);
+    base::FilePath alternate_path = path.Append(kAlternateChromeExePath);
+    ASSERT_TRUE(base::DeleteFile(alternate_path, true));
+  }
+
+ protected:
+  void SetUp() override { SetUpChromeExeRegistry(); }
+
+  void TearDown() override { DeleteChromeExeRegistry(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PortMonitorTest);
+};
+
+TEST_F(PortMonitorTest, GetChromeExePathTest) {
+  CreateTempChromeExeFiles();
+  base::FilePath chrome_path = cloud_print::GetChromeExePath();
+  EXPECT_FALSE(chrome_path.empty());
+  EXPECT_TRUE(chrome_path.value().rfind(kAlternateChromeExePath) !=
+              std::string::npos);
+  EXPECT_TRUE(base::PathExists(chrome_path));
+  DeleteChromeExeRegistry();
+  chrome_path = cloud_print::GetChromeExePath();
+  // No Chrome or regular chrome path.
+  EXPECT_TRUE(chrome_path.empty() ||
+              chrome_path.value().rfind(kChromeExePath) == std::string::npos);
+}
+
+TEST_F(PortMonitorTest, GetPrintCommandTemplateTest) {
+  base::string16 print_command = cloud_print::GetPrintCommandTemplate();
+  EXPECT_FALSE(print_command.empty());
+  EXPECT_EQ(print_command, kTestPrintCommand);
+  DeleteChromeExeRegistry();
+  print_command = cloud_print::GetPrintCommandTemplate();
+  EXPECT_TRUE(print_command.empty());
+}
+
+TEST_F(PortMonitorTest, GetChromeProfilePathTest) {
+  base::FilePath data_path = cloud_print::GetChromeProfilePath();
+  EXPECT_FALSE(data_path.empty());
+  base::FilePath temp;
+  PathService::Get(base::DIR_TEMP, &temp);
+  EXPECT_EQ(data_path, temp);
+  EXPECT_TRUE(base::DirectoryExists(data_path));
+  DeleteChromeExeRegistry();
+  data_path = cloud_print::GetChromeProfilePath();
+  EXPECT_TRUE(data_path.empty());
+}
+
+TEST_F(PortMonitorTest, EnumPortsTest) {
+  DWORD needed_bytes = 0;
+  DWORD returned = 0;
+  EXPECT_FALSE(
+      Monitor2EnumPorts(NULL, NULL, 1, NULL, 0, &needed_bytes, &returned));
+  EXPECT_EQ(static_cast<DWORD>(ERROR_INSUFFICIENT_BUFFER), GetLastError());
+  EXPECT_NE(0u, needed_bytes);
+  EXPECT_EQ(0u, returned);
+
+  BYTE* buffer = new BYTE[needed_bytes];
+  ASSERT_TRUE(buffer != NULL);
+  EXPECT_TRUE(Monitor2EnumPorts(NULL, NULL, 1, buffer, needed_bytes,
+                                &needed_bytes, &returned));
+  EXPECT_NE(0u, needed_bytes);
+  EXPECT_EQ(1u, returned);
+  PORT_INFO_1* port_info_1 = reinterpret_cast<PORT_INFO_1*>(buffer);
+  EXPECT_TRUE(port_info_1->pName != NULL);
+  delete[] buffer;
+
+  returned = 0;
+  needed_bytes = 0;
+  EXPECT_FALSE(
+      Monitor2EnumPorts(NULL, NULL, 2, NULL, 0, &needed_bytes, &returned));
+  EXPECT_EQ(static_cast<DWORD>(ERROR_INSUFFICIENT_BUFFER), GetLastError());
+  EXPECT_NE(0u, needed_bytes);
+  EXPECT_EQ(0u, returned);
+
+  buffer = new BYTE[needed_bytes];
+  ASSERT_TRUE(buffer != NULL);
+  EXPECT_TRUE(Monitor2EnumPorts(NULL, NULL, 2, buffer, needed_bytes,
+                                &needed_bytes, &returned));
+  EXPECT_NE(0u, needed_bytes);
+  EXPECT_EQ(1u, returned);
+  PORT_INFO_2* port_info_2 = reinterpret_cast<PORT_INFO_2*>(buffer);
+  EXPECT_TRUE(port_info_2->pPortName != NULL);
+  delete[] buffer;
+}
+
+TEST_F(PortMonitorTest, FlowTest) {
+  const wchar_t kXcvDataItem[] = L"MonitorUI";
+  MONITORINIT monitor_init = {0};
+  HANDLE monitor_handle = NULL;
+  HANDLE port_handle = NULL;
+  HANDLE xcv_handle = NULL;
+  DWORD bytes_processed = 0;
+  DWORD bytes_needed = 0;
+  const size_t kBufferSize = 100;
+  BYTE buffer[kBufferSize] = {0};
+
+  // Initialize the print monitor
+  MONITOR2* monitor2 = InitializePrintMonitor2(&monitor_init, &monitor_handle);
+  EXPECT_TRUE(monitor2 != NULL);
+  EXPECT_TRUE(monitor_handle != NULL);
+
+  // Test the XCV functions.  Used for reporting the location of the
+  // UI portion of the port monitor.
+  EXPECT_TRUE(monitor2->pfnXcvOpenPort != NULL);
+  EXPECT_TRUE(monitor2->pfnXcvOpenPort(monitor_handle, NULL, 0, &xcv_handle));
+  EXPECT_TRUE(xcv_handle != NULL);
+  EXPECT_TRUE(monitor2->pfnXcvDataPort != NULL);
+  EXPECT_EQ(static_cast<DWORD>(ERROR_ACCESS_DENIED),
+            monitor2->pfnXcvDataPort(xcv_handle, kXcvDataItem, NULL, 0, buffer,
+                                     kBufferSize, &bytes_needed));
+  EXPECT_TRUE(monitor2->pfnXcvClosePort != NULL);
+  EXPECT_TRUE(monitor2->pfnXcvClosePort(xcv_handle));
+  EXPECT_TRUE(monitor2->pfnXcvOpenPort(monitor_handle, NULL,
+                                       SERVER_ACCESS_ADMINISTER, &xcv_handle));
+  EXPECT_TRUE(xcv_handle != NULL);
+  EXPECT_TRUE(monitor2->pfnXcvDataPort != NULL);
+  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+            monitor2->pfnXcvDataPort(xcv_handle, kXcvDataItem, NULL, 0, buffer,
+                                     kBufferSize, &bytes_needed));
+  EXPECT_TRUE(monitor2->pfnXcvClosePort != NULL);
+  EXPECT_TRUE(monitor2->pfnXcvClosePort(xcv_handle));
+
+  // Test opening the port and running a print job.
+  EXPECT_TRUE(monitor2->pfnOpenPort != NULL);
+  EXPECT_TRUE(monitor2->pfnOpenPort(monitor_handle, NULL, &port_handle));
+  EXPECT_TRUE(port_handle != NULL);
+  EXPECT_TRUE(monitor2->pfnStartDocPort != NULL);
+  EXPECT_TRUE(monitor2->pfnWritePort != NULL);
+  EXPECT_TRUE(monitor2->pfnReadPort != NULL);
+  EXPECT_TRUE(monitor2->pfnEndDocPort != NULL);
+
+  // These functions should fail if we have not impersonated the user.
+  EXPECT_FALSE(monitor2->pfnStartDocPort(port_handle, const_cast<wchar_t*>(L""),
+                                         0, 0, NULL));
+  EXPECT_FALSE(monitor2->pfnWritePort(port_handle, buffer, kBufferSize,
+                                      &bytes_processed));
+  EXPECT_EQ(0u, bytes_processed);
+  EXPECT_FALSE(monitor2->pfnReadPort(port_handle, buffer, sizeof(buffer),
+                                     &bytes_processed));
+  EXPECT_EQ(0u, bytes_processed);
+  EXPECT_FALSE(monitor2->pfnEndDocPort(port_handle));
+
+  // Now impersonate so we can test the success case.
+  ASSERT_TRUE(ImpersonateSelf(SecurityImpersonation));
+  EXPECT_TRUE(monitor2->pfnStartDocPort(port_handle, const_cast<wchar_t*>(L""),
+                                        0, 0, NULL));
+  EXPECT_TRUE(monitor2->pfnWritePort(port_handle, buffer, kBufferSize,
+                                     &bytes_processed));
+  EXPECT_EQ(kBufferSize, bytes_processed);
+  EXPECT_FALSE(monitor2->pfnReadPort(port_handle, buffer, sizeof(buffer),
+                                     &bytes_processed));
+  EXPECT_EQ(0u, bytes_processed);
+  EXPECT_TRUE(monitor2->pfnEndDocPort(port_handle));
+  RevertToSelf();
+  EXPECT_TRUE(monitor2->pfnClosePort != NULL);
+  EXPECT_TRUE(monitor2->pfnClosePort(port_handle));
+  // Shutdown the port monitor.
+  Monitor2Shutdown(monitor_handle);
+}
+
+}  // namespace cloud_print
diff --git a/cloud_print/virtual_driver/win/port_monitor/spooler_win.h b/cloud_print/virtual_driver/win/port_monitor/spooler_win.h
new file mode 100644
index 0000000..e6073f9
--- /dev/null
+++ b/cloud_print/virtual_driver/win/port_monitor/spooler_win.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_SPOOLER_WIN_H_
+#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_SPOOLER_WIN_H_
+
+#include <windows.h>
+
+// Compatible structures and prototypes are also defined in the Windows DDK in
+// winsplp.h.
+#ifndef _WINSPLP_
+
+typedef struct {
+  DWORD size;
+  BOOL(WINAPI* pfnEnumPorts)
+  (HANDLE,
+   wchar_t*,
+   DWORD level,
+   BYTE* ports,
+   DWORD ports_size,
+   DWORD* needed_bytes,
+   DWORD* returned);
+
+  BOOL(WINAPI* pfnOpenPort)(HANDLE monitor_data, wchar_t*, HANDLE* handle);
+
+  void* pfnOpenPortEx;  // Unused.
+
+  BOOL(WINAPI* pfnStartDocPort)
+  (HANDLE port_handle, wchar_t* printer_name, DWORD job_id, DWORD, BYTE*);
+
+  BOOL(WINAPI* pfnWritePort)
+  (HANDLE port, BYTE* buffer, DWORD buffer_size, DWORD* bytes_written);
+
+  BOOL(WINAPI* pfnReadPort)(HANDLE, BYTE*, DWORD, DWORD* bytes_read);
+
+  BOOL(WINAPI* pfnEndDocPort)(HANDLE port_handle);
+
+  BOOL(WINAPI* pfnClosePort)(HANDLE port_handle);
+
+  void* pfnAddPort;  // Unused.
+
+  void* pfnAddPortEx;  // Unused.
+
+  void* pfnConfigurePort;  // Unused.
+
+  void* pfnDeletePort;  // Unused.
+
+  void* pfnGetPrinterDataFromPort;  // Unused.
+
+  void* pfnSetPortTimeOuts;  // Unusued.
+
+  BOOL(WINAPI* pfnXcvOpenPort)
+  (HANDLE monitor, const wchar_t*, ACCESS_MASK granted_access, HANDLE* handle);
+
+  DWORD(WINAPI* pfnXcvDataPort)
+  (HANDLE xcv_handle,
+   const wchar_t* data_name,
+   BYTE*,
+   DWORD,
+   BYTE* output_data,
+   DWORD output_data_bytes,
+   DWORD* output_data_bytes_needed);
+
+  BOOL(WINAPI* pfnXcvClosePort)(HANDLE handle);
+
+  VOID(WINAPI* pfnShutdown)(HANDLE monitor_handle);
+} MONITOR2;
+
+typedef struct {
+  DWORD size;
+
+  BOOL(WINAPI* pfnAddPortUI)
+  (const wchar_t*, HWND hwnd, const wchar_t* monitor_name, wchar_t**);
+
+  BOOL(WINAPI* pfnConfigurePortUI)
+  (const wchar_t*, HWND hwnd, const wchar_t* port_name);
+
+  BOOL(WINAPI* pfnDeletePortUI)
+  (const wchar_t*, HWND hwnd, const wchar_t* port_name);
+} MONITORUI;
+
+typedef struct {
+  DWORD cbSize;
+  HANDLE hSpooler;
+  HKEY hckRegistryRoot;
+  void* pMonitorReg;  // Unused
+  BOOL bLocal;
+  LPCWSTR pszServerName;
+} MONITORINIT;
+
+MONITOR2* WINAPI InitializePrintMonitor2(MONITORINIT* monitor_init,
+                                         HANDLE* monitor_handle);
+
+MONITORUI* WINAPI InitializePrintMonitorUI(void);
+
+#endif  // ifdef USE_WIN_DDK
+#endif  // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_SPOOLER_WIN_H_
diff --git a/cloud_print/virtual_driver/win/virtual_driver_consts.cc b/cloud_print/virtual_driver/win/virtual_driver_consts.cc
new file mode 100644
index 0000000..ffa975f
--- /dev/null
+++ b/cloud_print/virtual_driver/win/virtual_driver_consts.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
+
+#include <windows.h>
+#include <stddef.h>
+
+#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
+
+namespace cloud_print {
+
+const wchar_t kPortName[] = L"GCP:";
+const size_t kPortNameSize = sizeof(kPortName);
+const wchar_t kGoogleUpdateProductId[] =
+    L"{9B13FA92-1F73-4761-AB78-2C6ADAC3660D}";
+
+}  // namespace cloud_print
diff --git a/cloud_print/virtual_driver/win/virtual_driver_consts.h b/cloud_print/virtual_driver/win/virtual_driver_consts.h
new file mode 100644
index 0000000..f97022d
--- /dev/null
+++ b/cloud_print/virtual_driver/win/virtual_driver_consts.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_
+#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_
+
+namespace cloud_print {
+
+extern const wchar_t kPortName[];
+extern const size_t kPortNameSize;
+extern const wchar_t kGoogleUpdateProductId[];
+
+}  // namespace cloud_print
+
+#endif  // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_
diff --git a/cloud_print/virtual_driver/win/virtual_driver_helpers.cc b/cloud_print/virtual_driver/win/virtual_driver_helpers.cc
new file mode 100644
index 0000000..603447c
--- /dev/null
+++ b/cloud_print/virtual_driver/win/virtual_driver_helpers.cc
@@ -0,0 +1,39 @@
+// 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 "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
+
+#include <windows.h>
+#include <winspool.h>
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/win/windows_version.h"
+#include "cloud_print/common/win/cloud_print_utils.h"
+
+namespace cloud_print {
+
+void DisplayWindowsMessage(HWND hwnd,
+                           HRESULT hr,
+                           const base::string16& caption) {
+  ::MessageBox(hwnd, GetErrorMessage(hr).c_str(), caption.c_str(), MB_OK);
+}
+
+HRESULT GetPrinterDriverDir(base::FilePath* path) {
+  BYTE driver_dir_buffer[MAX_PATH * sizeof(wchar_t)];
+  DWORD needed = 0;
+  if (!GetPrinterDriverDirectory(NULL, NULL, 1, driver_dir_buffer,
+                                 MAX_PATH * sizeof(wchar_t), &needed)) {
+    // We could try to allocate a larger buffer if needed > MAX_PATH
+    // but that really shouldn't happen.
+    return cloud_print::GetLastHResult();
+  }
+  *path = base::FilePath(reinterpret_cast<wchar_t*>(driver_dir_buffer));
+
+  // The XPS driver is a "Level 3" driver
+  *path = path->Append(L"3");
+  return S_OK;
+}
+}
diff --git a/cloud_print/virtual_driver/win/virtual_driver_helpers.h b/cloud_print/virtual_driver/win/virtual_driver_helpers.h
new file mode 100644
index 0000000..eadebe8
--- /dev/null
+++ b/cloud_print/virtual_driver/win/virtual_driver_helpers.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_HELPERS_H_
+#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_HELPERS_H_
+
+#include <windows.h>
+
+#include "base/strings/string16.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace cloud_print {
+
+// Convert an HRESULT to a localized string and display it in a message box.
+void DisplayWindowsMessage(HWND hwnd,
+                           HRESULT hr,
+                           const base::string16& caption);
+
+// Gets the standard install path for "version 3" print drivers.
+HRESULT GetPrinterDriverDir(base::FilePath* path);
+
+}  // namespace cloud_print
+
+#endif  // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_HELPERS_H_
diff --git a/components/metrics/public/cpp/call_stack_profile_struct_traits.h b/components/metrics/public/cpp/call_stack_profile_struct_traits.h
index f623b2d..99c4fe3c 100644
--- a/components/metrics/public/cpp/call_stack_profile_struct_traits.h
+++ b/components/metrics/public/cpp/call_stack_profile_struct_traits.h
@@ -136,10 +136,21 @@
 
   static bool Read(metrics::mojom::CallStackProfileDataView data,
                    base::StackSamplingProfiler::CallStackProfile* out) {
-    return data.ReadModules(&out->modules) && data.ReadSamples(&out->samples) &&
-        data.ReadProfileDuration(&out->profile_duration) &&
-        data.ReadSamplingPeriod(&out->sampling_period) &&
-        ValidateSamples(out->samples, out->modules.size());
+    std::vector<base::StackSamplingProfiler::Module> modules;
+    std::vector<base::StackSamplingProfiler::Sample> samples;
+    base::TimeDelta profile_duration, sampling_period;
+    if (!data.ReadModules(&modules) || !data.ReadSamples(&samples) ||
+        !data.ReadProfileDuration(&profile_duration) ||
+        !data.ReadSamplingPeriod(&sampling_period) ||
+        !ValidateSamples(samples, modules.size()))
+      return false;
+
+    *out = base::StackSamplingProfiler::CallStackProfile();
+    out->modules = std::move(modules);
+    out->samples = std::move(samples);
+    out->profile_duration = profile_duration;
+    out->sampling_period = sampling_period;
+    return true;
   }
 };
 
diff --git a/components/ntp_snippets/OWNERS b/components/ntp_snippets/OWNERS
index 9e1ec3b7..5facc94e 100644
--- a/components/ntp_snippets/OWNERS
+++ b/components/ntp_snippets/OWNERS
@@ -1,4 +1,10 @@
-noyau@chromium.org
+# Main owners:
 bauerb@chromium.org
+jkrcal@chromium.org
 treib@chromium.org
+
+# For ios:
+noyau@chromium.org
+
+# Fallback, in case all of the above are OOO:
 markusheintz@chromium.org
diff --git a/components/ntp_tiles/BUILD.gn b/components/ntp_tiles/BUILD.gn
index 9a8ae6b..503c3866 100644
--- a/components/ntp_tiles/BUILD.gn
+++ b/components/ntp_tiles/BUILD.gn
@@ -22,6 +22,7 @@
     "most_visited_sites.h",
     "ntp_tile.cc",
     "ntp_tile.h",
+    "ntp_tile_source.h",
     "popular_sites.cc",
     "popular_sites.h",
     "pref_names.cc",
@@ -99,7 +100,7 @@
   java_cpp_enum("ntp_tiles_enums_java") {
     sources = [
       "metrics.h",
-      "ntp_tile.h",
+      "ntp_tile_source.h",
     ]
   }
 }
diff --git a/components/ntp_tiles/ntp_tile.h b/components/ntp_tiles/ntp_tile.h
index ef228b54..fa37cf8 100644
--- a/components/ntp_tiles/ntp_tile.h
+++ b/components/ntp_tiles/ntp_tile.h
@@ -10,24 +10,11 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
 #include "url/gurl.h"
 
 namespace ntp_tiles {
 
-// The source of an NTP tile.
-// A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp
-enum class NTPTileSource {
-  // Tile comes from the personal top sites list, based on local history.
-  TOP_SITES,
-  // Tile comes from the suggestions service, based on synced history.
-  SUGGESTIONS_SERVICE,
-  // Tile is regionally popular.
-  POPULAR,
-  // Tile is on an custodian-managed whitelist.
-  WHITELIST
-};
-
 // A suggested site shown on the New Tab Page.
 struct NTPTile {
   base::string16 title;
diff --git a/components/ntp_tiles/ntp_tile_source.h b/components/ntp_tiles/ntp_tile_source.h
new file mode 100644
index 0000000..1ac85a9
--- /dev/null
+++ b/components/ntp_tiles/ntp_tile_source.h
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_NTP_TILES_NTP_TILE_SOURCE_H_
+#define COMPONENTS_NTP_TILES_NTP_TILE_SOURCE_H_
+
+namespace ntp_tiles {
+
+// The source of an NTP tile.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp
+enum class NTPTileSource {
+  // Tile comes from the personal top sites list, based on local history.
+  TOP_SITES,
+  // Tile comes from the suggestions service, based on synced history.
+  SUGGESTIONS_SERVICE,
+  // Tile is regionally popular.
+  POPULAR,
+  // Tile is on a custodian-managed whitelist.
+  WHITELIST,
+
+  LAST = WHITELIST
+};
+
+}  // namespace ntp_tiles
+
+#endif  // COMPONENTS_NTP_TILES_NTP_TILE_SOURCE_H_
diff --git a/components/subresource_filter/content/renderer/document_subresource_filter.cc b/components/subresource_filter/content/renderer/document_subresource_filter.cc
index 8e326a9e..dadb24c3eb 100644
--- a/components/subresource_filter/content/renderer/document_subresource_filter.cc
+++ b/components/subresource_filter/content/renderer/document_subresource_filter.cc
@@ -13,6 +13,7 @@
 #include "components/subresource_filter/core/common/first_party_origin.h"
 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
 #include "components/subresource_filter/core/common/scoped_timers.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 
 namespace subresource_filter {
@@ -130,18 +131,18 @@
   TRACE_EVENT1("loader", "DocumentSubresourceFilter::allowLoad", "url",
                resourceUrl.string().utf8());
 
-  auto wall_duration_exporter = [this](base::TimeDelta delta) {
-    statistics_.evaluation_total_wall_duration += delta;
-    UMA_HISTOGRAM_MICRO_TIMES(
-        "SubresourceFilter.SubresourceLoad.Evaluation.WallDuration", delta);
-  };
-  auto cpu_duration_exporter = [this](base::TimeDelta delta) {
-    statistics_.evaluation_total_cpu_duration += delta;
-    UMA_HISTOGRAM_MICRO_TIMES(
-        "SubresourceFilter.SubresourceLoad.Evaluation.CPUDuration", delta);
-  };
-  SCOPED_TIMER(wall_duration_exporter);
-  SCOPED_THREAD_TIMER(cpu_duration_exporter);
+  auto wall_duration_timer = ScopedTimers::StartIf(
+      ScopedThreadTimers::IsSupported(), [this](base::TimeDelta delta) {
+        statistics_.evaluation_total_wall_duration += delta;
+        UMA_HISTOGRAM_MICRO_TIMES(
+            "SubresourceFilter.SubresourceLoad.Evaluation.WallDuration", delta);
+      });
+  auto cpu_duration_timer =
+      ScopedThreadTimers::Start([this](base::TimeDelta delta) {
+        statistics_.evaluation_total_cpu_duration += delta;
+        UMA_HISTOGRAM_MICRO_TIMES(
+            "SubresourceFilter.SubresourceLoad.Evaluation.CPUDuration", delta);
+      });
 
   ++statistics_.num_loads_total;
 
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent.cc b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
index 6e6c1aa..409e646 100644
--- a/components/subresource_filter/content/renderer/subresource_filter_agent.cc
+++ b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -12,7 +12,7 @@
 #include "components/subresource_filter/content/renderer/document_subresource_filter.h"
 #include "components/subresource_filter/content/renderer/ruleset_dealer.h"
 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
-#include "components/subresource_filter/core/common/scoped_timers.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/renderer/render_frame.h"
 #include "ipc/ipc_message.h"
@@ -96,15 +96,16 @@
       "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed",
       statistics.num_loads_disallowed);
 
-  UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
-      "SubresourceFilter.DocumentLoad.SubresourceEvaluation."
-      "TotalWallDuration",
-      statistics.evaluation_total_wall_duration,
-      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
-      50);
+  // If ThreadTicks is not supported, then no CPU time measurements have been
+  // collected. Don't report both CPU and wall duration to be consistent.
+  if (ScopedThreadTimers::IsSupported()) {
+    UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+        "SubresourceFilter.DocumentLoad.SubresourceEvaluation."
+        "TotalWallDuration",
+        statistics.evaluation_total_wall_duration,
+        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+        50);
 
-  // If ThreadTicks is not supported, then no measurements have been collected.
-  if (base::ThreadTicks::IsSupported()) {
     UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
         "SubresourceFilter.DocumentLoad.SubresourceEvaluation.TotalCPUDuration",
         statistics.evaluation_total_cpu_duration,
diff --git a/components/subresource_filter/core/browser/ruleset_service.cc b/components/subresource_filter/core/browser/ruleset_service.cc
index 031a1f08..d772734 100644
--- a/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/components/subresource_filter/core/browser/ruleset_service.cc
@@ -28,7 +28,7 @@
 #include "components/subresource_filter/core/common/copying_file_stream.h"
 #include "components/subresource_filter/core/common/indexed_ruleset.h"
 #include "components/subresource_filter/core/common/proto/rules.pb.h"
-#include "components/subresource_filter/core/common/scoped_timers.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
 #include "components/subresource_filter/core/common/unindexed_ruleset.h"
 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
 
diff --git a/components/subresource_filter/core/common/BUILD.gn b/components/subresource_filter/core/common/BUILD.gn
index 7708731c..905a444 100644
--- a/components/subresource_filter/core/common/BUILD.gn
+++ b/components/subresource_filter/core/common/BUILD.gn
@@ -25,6 +25,7 @@
     "ngram_extractor.h",
     "scoped_timers.h",
     "string_splitter.h",
+    "time_measurements.h",
     "uint64_hasher.h",
     "unindexed_ruleset.cc",
     "unindexed_ruleset.h",
diff --git a/components/subresource_filter/core/common/scoped_timers.h b/components/subresource_filter/core/common/scoped_timers.h
index 58a834c2..6eb617b1 100644
--- a/components/subresource_filter/core/common/scoped_timers.h
+++ b/components/subresource_filter/core/common/scoped_timers.h
@@ -2,171 +2,34 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This file provides tools for measuring time intervals and reporting them to
-// UMA histograms or via custom functors. It is possible to measure time both
-// with base::TimeTicks and base::ThreadTicks.
-// WARNING: *UMA_HISTOGRAM_* macros in this file are not thread-safe.
-// See also: "base/metrics/histogram_macros*.h".
+// This file provides a ScopedTimerImpl class which measures its lifetime using
+// different time providers, and can report the measurement via user-defined
+// callbacks. The class comes with ScopedTimerImplFactory which has convenient
+// factory methods returning timer instances.
 //
-// TODO(pkalinnikov): Consider moving content of this file to "base/metrics/*"
-// after some refactoring. Note that most of the code generated by the macros
-// below is not thread-safe.
+// Although the classes can be used directly, the most common usages are covered
+// by the ScopedTimers and ScopedThreadTimers type aliases. See comments to
+// these aliases below.
+//
+// TODO(pkalinnikov): Consider moving this file to "base/metrics/".
 
 #ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_SCOPED_TIMERS_
 #define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_SCOPED_TIMERS_
 
 #include <type_traits>
+#include <utility>
 
 #include "base/macros.h"
-#include "base/metrics/histogram.h"
 #include "base/time/time.h"
 
 namespace subresource_filter {
 
-// Creates a scoped object that measures its lifetime using base::TimeTicks, and
-// reports the result as base::TimeDelta via provided |export_functor|. The
-// functor is copied if passed in by value.
-//
-// Example:
-//   void Function() {
-//     auto export_time = [](base::TimeDelta delta) {
-//       LOG(INFO) << "Duration: " << delta.InMicroseconds();
-//     };
-//     SCOPED_TIMER(export_time);
-//     ... Useful things happen here ...
-//   }  // |export_time| will be triggered here.
-//
-// This is recommended for when you want to measure the time it takes for a
-// method/scope to execute, including, possibly, the time spent by the thread on
-// being blocked and/or descheduled.
-#define SCOPED_TIMER(export_functor)                                  \
-  IMPL_SCOPED_TIMER_EXPANDER(impl::TimeTicksProvider, export_functor, \
-                             __COUNTER__)
-
-// Similar to SCOPED_TIMER, but uses base::ThreadTicks for measuring time.
-//
-// This is recommended for when you want to measure the time it takes for a
-// method/scope to do actual work, i.e. excluding the time spent by the thread
-// on being blocked and/or descheduled.
-#define SCOPED_THREAD_TIMER(export_functor)                             \
-  IMPL_SCOPED_TIMER_EXPANDER(impl::ThreadTicksProvider, export_functor, \
-                             __COUNTER__)
-
-// Creates a scoped object that measures its lifetime using base::ThreadTicks,
-// and reports the result in milliseconds as a UMA statistic to a histogram with
-// the provided |name| which is expected to be a runtime constant. The histogram
-// collects times up to 10 seconds in 50 buckets.
-//
-// Under the hood there is a static base::HistogramBase* pointer initialized
-// right before the scoped object. The pointer is used by a specific
-// |export_functor| passed in to a SCOPED_THREAD_TIMER (see it above).
-//
-// Example:
-//   void Function() {
-//     SCOPED_UMA_HISTOGRAM_THREAD_TIMER("Component.FunctionTime");
-//     ... Useful things happen here ...
-//   }
-//
-// WARNING: The generated code is not thread-safe.
-#define SCOPED_UMA_HISTOGRAM_THREAD_TIMER(name)                             \
-  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                                 \
-      name, impl::ThreadTicksProvider, impl::ExportMillisecondsToHistogram, \
-      10 * 1000, __COUNTER__)
-
-// Similar to SCOPED_UMA_HISTOGRAM_THREAD_TIMER above, but the histogram
-// collects times in microseconds, up to 1 second, and using 50 buckets.
-//
-// WARNING: The generated code is not thread-safe.
-#define SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(name)                       \
-  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                                 \
-      name, impl::ThreadTicksProvider, impl::ExportMicrosecondsToHistogram, \
-      1000 * 1000, __COUNTER__)
-
-// Similar to SCOPED_UMA_HISTOGRAM_TIMER in "base/metrics/histogram_macros.h",
-// but the histogram stores times in microseconds, up to 1 second, in 50
-// buckets.
-//
-// WARNING: The generated code is not thread-safe.
-#define SCOPED_UMA_HISTOGRAM_MICRO_TIMER(name)                            \
-  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                               \
-      name, impl::TimeTicksProvider, impl::ExportMicrosecondsToHistogram, \
-      1000 * 1000, __COUNTER__)
-
-// Similar to UMA_HISTOGRAM_TIMES in "base/metrics/histogram_macros.h", but
-// the histogram stores times in microseconds, up to 1 second, in 50 buckets.
-//
-// WARNING: The generated code is not thread-safe.
-#define UMA_HISTOGRAM_MICRO_TIMES(name, sample)                          \
-  UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample,                         \
-                                   base::TimeDelta::FromMicroseconds(1), \
-                                   base::TimeDelta::FromSeconds(1), 50)
-
-// This can be used when the default ranges are not sufficient. This macro lets
-// the metric developer customize the min and max of the sampled range, as well
-// as the number of buckets recorded.
-#define UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample, min, max, bucket_count) \
-  IMPL_UMA_HISTOGRAM_ADD(name, sample.InMicroseconds(), min.InMicroseconds(),  \
-                         max.InMicroseconds(), bucket_count)
-
-// -----------------------------------------------------------------------------
-// Below are helpers used by other macros. Shouldn't be used directly. ---------
-
-// This is necessary to expand __COUNTER__ to an actual value.
-#define IMPL_SCOPED_TIMER_EXPANDER(time_provider, export_functor, suffix) \
-  IMPL_SCOPED_TIMER_UNIQUE(time_provider, export_functor, suffix)
-
-// Creates a scoped timer, which uses |time_provider| to measure time, and
-// |export_functor| to report it.
-#define IMPL_SCOPED_TIMER_UNIQUE(time_provider, export_functor, suffix) \
-  impl::ScopedTimer<time_provider, decltype(export_functor)>            \
-      scoped_timer_##suffix(export_functor);
-
-// This is necessary to expand __COUNTER__ to an actual value.
-#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(               \
-    name, time_provider, histogram_exporter, max_value, suffix) \
-  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(                       \
-      name, time_provider, histogram_exporter, max_value, suffix)
-
-// Creates a static histogram pointer and a scoped object referring to it
-// throught the |histogram_exporter| functor. Both the pointer and the scoped
-// object are uniquely-named, using the unique |suffix| passed in.
-#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(                            \
-    name, time_provider, histogram_exporter, max_value, suffix)            \
-  IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, 1, max_value, 50, suffix) \
-  IMPL_SCOPED_TIMER_UNIQUE(time_provider,                                  \
-                           (histogram_exporter(histogram_##suffix)), suffix)
-
-// This is necessary to expand __COUNTER__ to an actual value.
-#define IMPL_UMA_HISTOGRAM_MICRO_TIMES_EXPANDER(name, max_value, suffix, \
-                                                sample)                  \
-  IMPL_UMA_HISTOGRAM_MICRO_TIMES_UNIQUE(name, max_value, suffix, sample)
-
-// Defines a static UMA histogram pointer and writes a |sample| to it.
-#define IMPL_UMA_HISTOGRAM_ADD(name, sample, min, max, bucket_count)          \
-  do {                                                                        \
-    IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, 0) \
-    histogram_0->Add(sample);                                                 \
-  } while (0)
-
-// Defines a static pointer to a UMA histogram.
-//
-// WARNING: Static local variable initialization is deliberately *not*
-// thread-safe in Chrome builds. See the "-fno-threadsafe-statics" flag in
-// "build/config/compiler/BUILD.gn" and "/Zc:threadSafeInit-" in
-// "build/config/win/BUILD.gn" for details.
-#define IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, \
-                                                 suffix)                       \
-  static base::HistogramBase* histogram_##suffix =                             \
-      base::Histogram::FactoryGet(                                             \
-          name, min, max, bucket_count,                                        \
-          base::HistogramBase::kUmaTargetedHistogramFlag);
-
 namespace impl {
 
-// ScopedTimer is a multi-purpose scoped timer. It measures time delta from its
-// construction till destruction. For example, by putting an instance of this
-// class to the beginning of a scope it is possible to measure how long the
-// scope is being executed.
+// ScopedTimerImpl is a multi-purpose scoped timer. It measures time delta from
+// its construction until its destruction. For example, by putting an instance
+// of this class to the beginning of a scope it is possible to measure how long
+// the scope is being executed.
 //
 // The obtained time measurement is reported via ExportFunctor, which takes
 // base::TimeDelta as a parameter.
@@ -179,23 +42,46 @@
 //  * static TimeType Now();
 //    - Returns the current time of some TimeType, e.g., base::TimeTicks.
 //
-// Time measurement is exported exactly once, unless TimeProvider::IsSupported()
-// is false. In the latter case ExportFunctor is never called.
+// The ExportFunctor is invoked exactly once, upon destruction, if both
+// |activate| and TimeProvider::IsSupported() are true. Otherwise, the
+// ExportFunctor is not called and no time measurements are performed, making
+// the overhead of an instance practically zero.
 template <typename TimeProvider, typename ExportFunctor>
-class ScopedTimer {
+class ScopedTimerImpl {
  public:
-  ScopedTimer(ExportFunctor export_functor) : export_functor_(export_functor) {
-    if (TimeProvider::IsSupported()) {
+  // Constructs a timer reporting its measurement to |export_functor|. The timer
+  // will be activated iff |activate| && TimeProvider::IsSupported().
+  explicit ScopedTimerImpl(ExportFunctor&& export_functor, bool activate = true)
+      : export_functor_(std::forward<ExportFunctor>(export_functor)),
+        activated_(activate && TimeProvider::IsSupported()) {
+    if (activated_) {
       TimeProvider::WaitUntilInitialized();
       construction_time_ = TimeProvider::Now();
     }
   }
 
-  ~ScopedTimer() {
-    if (TimeProvider::IsSupported()) {
-      const base::TimeDelta delta = TimeProvider::Now() - construction_time_;
-      export_functor_(delta);
+  // The class is moveable. The |rhs| becomes deactivated when it is moved from,
+  // so that no time measurement will be reported upon its destruction.
+  ScopedTimerImpl(ScopedTimerImpl&& rhs)
+      : export_functor_(std::forward<ExportFunctor>(rhs.export_functor_)),
+        construction_time_(std::move(rhs.construction_time_)),
+        activated_(rhs.activated_) {
+    rhs.activated_ = false;
+  }
+
+  // If |this| was activated before assignment, it reports its measurement
+  // before stealing the one from |rhs|.
+  ScopedTimerImpl& operator=(ScopedTimerImpl&& rhs) {
+    if (&rhs != this) {
+      this->~ScopedTimerImpl();
+      ::new (this) ScopedTimerImpl(std::move(rhs));
     }
+    return *this;
+  }
+
+  ~ScopedTimerImpl() {
+    if (activated_)
+      export_functor_(TimeProvider::Now() - construction_time_);
   }
 
  private:
@@ -204,8 +90,9 @@
 
   ExportFunctor export_functor_;
   TimeType construction_time_;
+  bool activated_ = false;
 
-  DISALLOW_COPY_AND_ASSIGN(ScopedTimer);
+  DISALLOW_COPY_AND_ASSIGN(ScopedTimerImpl);
 };
 
 // TimeProvider implementations ------------------------------------------------
@@ -219,30 +106,51 @@
 
 using ThreadTicksProvider = base::ThreadTicks;
 
-// ExportFunctor implementations -----------------------------------------------
+// ScopedTimerImpl factories ---------------------------------------------------
 
-template <bool is_microsec_precision>
-class ExportTimeDeltaToHistogram {
+// The class used to produce scoped timers using a certain |TimeProvider|.
+template <typename TimeProvider>
+class ScopedTimerImplFactory {
  public:
-  ExportTimeDeltaToHistogram(base::HistogramBase* histogram)
-      : histogram_(histogram) {}
-
-  void operator()(base::TimeDelta delta) {
-    if (is_microsec_precision)
-      histogram_->Add(delta.InMicroseconds());
-    else
-      histogram_->Add(delta.InMilliseconds());
+  // Returns a scoped timer which uses |export_functor| to report its
+  // measurement. The timer is activated iff TimeProvider::IsSupported().
+  template <typename ExportFunctor>
+  static ScopedTimerImpl<TimeProvider, ExportFunctor> Start(
+      ExportFunctor&& export_functor) {
+    return ScopedTimerImpl<TimeProvider, ExportFunctor>(
+        std::forward<ExportFunctor>(export_functor));
   }
 
- private:
-  base::HistogramBase* histogram_;
+  // Similar to the Start method above, but the timer is activated iff
+  // |condition| && TimeProvider::IsSupported().
+  template <typename ExportFunctor>
+  static ScopedTimerImpl<TimeProvider, ExportFunctor> StartIf(
+      bool condition,
+      ExportFunctor&& export_functor) {
+    return ScopedTimerImpl<TimeProvider, ExportFunctor>(
+        std::forward<ExportFunctor>(export_functor), condition);
+  }
+
+  static bool IsSupported() { return TimeProvider::IsSupported(); }
 };
 
-using ExportMillisecondsToHistogram = ExportTimeDeltaToHistogram<false>;
-using ExportMicrosecondsToHistogram = ExportTimeDeltaToHistogram<true>;
-
 }  // namespace impl
 
+// A factory to produce scoped timers that measure time by means of TimeTicks.
+//
+// Example usage:
+//   void foo() {
+//     auto scoped_timer = ScopedTimers::Start([](base::TimeDelta duration) {
+//       LOG(INFO) << "Duration: " << duration.InMicroseconds() << " us";
+//     });
+//     ... Some time-consuming code goes here ...
+//   }
+using ScopedTimers = impl::ScopedTimerImplFactory<impl::TimeTicksProvider>;
+
+// Similar to ScopedTimers, but the produced timers use ThreadTicks.
+using ScopedThreadTimers =
+    impl::ScopedTimerImplFactory<impl::ThreadTicksProvider>;
+
 }  // namespace subresource_filter
 
 #endif  // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_SCOPED_TIMERS_
diff --git a/components/subresource_filter/core/common/scoped_timers_unittest.cc b/components/subresource_filter/core/common/scoped_timers_unittest.cc
index 80cc66c..4a10af7 100644
--- a/components/subresource_filter/core/common/scoped_timers_unittest.cc
+++ b/components/subresource_filter/core/common/scoped_timers_unittest.cc
@@ -6,11 +6,12 @@
 
 #include "base/test/histogram_tester.h"
 #include "base/time/time.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-class ExportFunctorForTests {
+class MockExportFunctor {
  public:
   int number_of_calls() const { return number_of_calls_; }
   void operator()(base::TimeDelta) { ++number_of_calls_; }
@@ -19,74 +20,138 @@
   int number_of_calls_ = 0;
 };
 
-// Casts an l-value parameter to a reference to it.
-template <typename T>
-T& reference(T& value) {
-  return value;
+template <typename TimerFactory>
+void ExpectFunctorIsCalledOnceOnDestruction() {
+  MockExportFunctor export_functor;
+  {
+    auto scoped_timer = TimerFactory::Start(export_functor);
+    EXPECT_EQ(0, export_functor.number_of_calls());
+  }
+  const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
+  EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+}
+
+template <typename TimerFactory>
+void ExpectStoredLambdaIsInvokedOnceOnDestruction() {
+  bool export_is_called = false;
+  auto export_functor = [&export_is_called](base::TimeDelta) {
+    EXPECT_FALSE(export_is_called);
+    export_is_called = true;
+  };
+
+  {
+    auto scoped_timer = TimerFactory::Start(export_functor);
+    EXPECT_FALSE(export_is_called);
+  }
+  EXPECT_EQ(TimerFactory::IsSupported(), export_is_called);
+}
+
+template <typename TimerFactory>
+void ExpectInlineLambdaIsInvokedOnceOnDestruction() {
+  bool export_is_called = false;
+  {
+    auto scoped_timer =
+        TimerFactory::Start([&export_is_called](base::TimeDelta) {
+          EXPECT_FALSE(export_is_called);
+          export_is_called = true;
+        });
+    EXPECT_FALSE(export_is_called);
+  }
+  EXPECT_EQ(TimerFactory::IsSupported(), export_is_called);
+}
+
+template <typename TimerFactory>
+void ExpectWellBehavedStartIf(bool condition) {
+  bool export_is_called = false;
+  auto export_functor = [&export_is_called](base::TimeDelta) {
+    EXPECT_FALSE(export_is_called);
+    export_is_called = true;
+  };
+
+  {
+    auto scoped_timer = TimerFactory::StartIf(condition, export_functor);
+    EXPECT_FALSE(export_is_called);
+  }
+  EXPECT_EQ(condition && TimerFactory::IsSupported(), export_is_called);
+}
+
+template <typename TimerFactory>
+void ExpectWellBehavedMoveContructor() {
+  MockExportFunctor export_functor;
+  const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
+  {
+    auto scoped_timer = TimerFactory::Start(export_functor);
+    EXPECT_EQ(0, export_functor.number_of_calls());
+    {
+      auto another_scoped_timer = std::move(scoped_timer);
+      EXPECT_EQ(0, export_functor.number_of_calls());
+    }
+    // |another_scoped_timer| should have called |export_functor|.
+    EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+  }
+  // But |scoped_timer| should have not since then.
+  EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+}
+
+template <typename TimerFactory>
+void ExpectWellBehavedMoveAssignment() {
+  MockExportFunctor export_functor;
+  const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
+  {
+    auto scoped_timer = TimerFactory::Start(export_functor);
+    EXPECT_EQ(0, export_functor.number_of_calls());
+    {
+      auto another_scoped_timer = std::move(scoped_timer);
+      scoped_timer = std::move(another_scoped_timer);
+      EXPECT_EQ(0, export_functor.number_of_calls());
+    }
+    // |another_scoped_timer| shouldn't have called |export_functor|.
+    EXPECT_EQ(0, export_functor.number_of_calls());
+  }
+  // But |scoped_timer| should have because it owns the measurement.
+  EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
 }
 
 }  // namespace
 
 namespace subresource_filter {
 
-TEST(ScopedTimersTest, ScopedTimerCallsFunctor) {
-  ExportFunctorForTests export_functor;
-  {
-    // Reference is needed to bypass object copying.
-    SCOPED_TIMER(reference(export_functor));
-    EXPECT_EQ(0, export_functor.number_of_calls());
-  }
-  EXPECT_EQ(1, export_functor.number_of_calls());
+TEST(ScopedTimersTest, CallsFunctor) {
+  ExpectFunctorIsCalledOnceOnDestruction<ScopedTimers>();
+  ExpectFunctorIsCalledOnceOnDestruction<ScopedThreadTimers>();
 }
 
-TEST(ScopedTimersTest, ScopedThreadTimerCallsFunctor) {
-  ExportFunctorForTests export_functor;
-  {
-    // Reference is needed to bypass functor copying.
-    SCOPED_THREAD_TIMER(reference(export_functor));
-    EXPECT_EQ(0, export_functor.number_of_calls());
-  }
-  const int expected_number_of_calls = base::ThreadTicks::IsSupported() ? 1 : 0;
-  EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+TEST(ScopedTimersTest, CallsStoredLambdaFunctor) {
+  ExpectStoredLambdaIsInvokedOnceOnDestruction<ScopedTimers>();
+  ExpectStoredLambdaIsInvokedOnceOnDestruction<ScopedThreadTimers>();
 }
 
-TEST(ScopedTimersTest, ScopedTimersCopyFunctor) {
-  ExportFunctorForTests export_functor;
-  {
-    SCOPED_TIMER(export_functor);
-    SCOPED_THREAD_TIMER(export_functor);
-    EXPECT_EQ(0, export_functor.number_of_calls());
-  }
-  EXPECT_EQ(0, export_functor.number_of_calls());
+TEST(ScopedTimersTest, CallsInlineLambdaFunctor) {
+  ExpectInlineLambdaIsInvokedOnceOnDestruction<ScopedTimers>();
+  ExpectInlineLambdaIsInvokedOnceOnDestruction<ScopedThreadTimers>();
 }
 
-TEST(ScopedTimersTest, ScopedThreadTimerCallsStoredLambdaFunctor) {
-  bool export_is_called = false;
-  auto export_functor = [&export_is_called](base::TimeDelta) {
-    EXPECT_FALSE(export_is_called);
-    export_is_called = true;
-  };
+TEST(ScopedTimersTest, StartIf) {
+  ExpectWellBehavedStartIf<ScopedTimers>(false);
+  ExpectWellBehavedStartIf<ScopedTimers>(true);
 
-  {
-    SCOPED_THREAD_TIMER(export_functor);
-    EXPECT_FALSE(export_is_called);
-  }
-  EXPECT_EQ(base::ThreadTicks::IsSupported(), export_is_called);
+  ExpectWellBehavedStartIf<ScopedThreadTimers>(false);
+  ExpectWellBehavedStartIf<ScopedThreadTimers>(true);
 }
 
-TEST(ScopedTimersTest, ScopedTimerCallsStoredLambdaFunctor) {
-  bool export_is_called = false;
-  auto export_functor = [&export_is_called](base::TimeDelta) {
-    export_is_called = true;
-  };
-
-  {
-    SCOPED_TIMER(export_functor);
-    EXPECT_FALSE(export_is_called);
-  }
-  EXPECT_TRUE(export_is_called);
+TEST(ScopedTimersTest, MoveConstructTimer) {
+  ExpectWellBehavedMoveContructor<ScopedTimers>();
+  ExpectWellBehavedMoveContructor<ScopedThreadTimers>();
 }
 
+TEST(ScopedTimersTest, MoveAssignTimer) {
+  ExpectWellBehavedMoveAssignment<ScopedTimers>();
+  ExpectWellBehavedMoveAssignment<ScopedThreadTimers>();
+}
+
+// Below are tests for macros in "time_measurements.h" -------------------------
+// TODO(pkalinnikov): Move these to a separate file?
+
 TEST(ScopedTimersTest, ScopedUmaHistogramMacros) {
   base::HistogramTester tester;
   {
@@ -99,11 +164,12 @@
     tester.ExpectTotalCount("ScopedTimers.MicroTimer", 0);
   }
 
-  const int expected_count = base::ThreadTicks::IsSupported() ? 1 : 0;
+  int expected_count = ScopedTimers::IsSupported() ? 1 : 0;
+  tester.ExpectTotalCount("ScopedTimers.MicroTimer", expected_count);
+
+  expected_count = ScopedThreadTimers::IsSupported() ? 1 : 0;
   tester.ExpectTotalCount("ScopedTimers.ThreadTimer", expected_count);
   tester.ExpectTotalCount("ScopedTimers.MicroThreadTimer", expected_count);
-
-  tester.ExpectTotalCount("ScopedTimers.MicroTimer", 1);
 }
 
 TEST(ScopedTimersTest, UmaHistogramMicroTimesFromExportFunctor) {
@@ -112,7 +178,7 @@
     UMA_HISTOGRAM_MICRO_TIMES("ScopedTimers.MicroTimes", delta);
   };
   {
-    SCOPED_TIMER(export_functor);
+    auto scoped_timer = ScopedTimers::Start(export_functor);
     tester.ExpectTotalCount("ScopedTimers.MicroTimes", 0);
   }
   tester.ExpectTotalCount("ScopedTimers.MicroTimes", 1);
diff --git a/components/subresource_filter/core/common/time_measurements.h b/components/subresource_filter/core/common/time_measurements.h
new file mode 100644
index 0000000..e682ffce
--- /dev/null
+++ b/components/subresource_filter/core/common/time_measurements.h
@@ -0,0 +1,150 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides tools for measuring time intervals and reporting them to
+// UMA histograms.
+// WARNING: *UMA_HISTOGRAM_* macros in this file are not thread-safe.
+// See also: "base/metrics/histogram_macros*.h".
+//
+// TODO(pkalinnikov): Consider moving content of this file to "base/metrics/*"
+// after some refactoring. Note that most of the code generated by the macros
+// below is not thread-safe.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_
+#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_
+
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "components/subresource_filter/core/common/scoped_timers.h"
+
+namespace subresource_filter {
+
+// Creates a scoped object that measures its lifetime using base::ThreadTicks,
+// and reports the result in milliseconds as a UMA statistic to a histogram with
+// the provided |name| which is expected to be a runtime constant. The histogram
+// collects times up to 10 seconds in 50 buckets.
+//
+// Under the hood there is a static base::HistogramBase* pointer initialized
+// right before the scoped object. The pointer is used by a specific
+// |export_functor| passed in to a SCOPED_THREAD_TIMER (see it above).
+//
+// Example:
+//   void Function() {
+//     SCOPED_UMA_HISTOGRAM_THREAD_TIMER("Component.FunctionTime");
+//     ... Useful things happen here ...
+//   }
+//
+// WARNING: The generated code is not thread-safe.
+#define SCOPED_UMA_HISTOGRAM_THREAD_TIMER(name)                             \
+  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                                 \
+      name, impl::ThreadTicksProvider, impl::ExportMillisecondsToHistogram, \
+      10 * 1000, __COUNTER__)
+
+// Similar to SCOPED_UMA_HISTOGRAM_THREAD_TIMER above, but the histogram
+// collects times in microseconds, up to 1 second, and using 50 buckets.
+//
+// WARNING: The generated code is not thread-safe.
+#define SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(name)                       \
+  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                                 \
+      name, impl::ThreadTicksProvider, impl::ExportMicrosecondsToHistogram, \
+      1000 * 1000, __COUNTER__)
+
+// Similar to SCOPED_UMA_HISTOGRAM_TIMER in "base/metrics/histogram_macros.h",
+// but the histogram stores times in microseconds, up to 1 second, in 50
+// buckets.
+//
+// WARNING: The generated code is not thread-safe.
+#define SCOPED_UMA_HISTOGRAM_MICRO_TIMER(name)                            \
+  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                               \
+      name, impl::TimeTicksProvider, impl::ExportMicrosecondsToHistogram, \
+      1000 * 1000, __COUNTER__)
+
+// Similar to UMA_HISTOGRAM_TIMES in "base/metrics/histogram_macros.h", but
+// the histogram stores times in microseconds, up to 1 second, in 50 buckets.
+//
+// WARNING: The generated code is not thread-safe.
+#define UMA_HISTOGRAM_MICRO_TIMES(name, sample)                          \
+  UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample,                         \
+                                   base::TimeDelta::FromMicroseconds(1), \
+                                   base::TimeDelta::FromSeconds(1), 50)
+
+// This can be used when the default ranges are not sufficient. This macro lets
+// the metric developer customize the min and max of the sampled range, as well
+// as the number of buckets recorded.
+#define UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample, min, max, bucket_count) \
+  IMPL_UMA_HISTOGRAM_ADD(name, sample.InMicroseconds(), min.InMicroseconds(),  \
+                         max.InMicroseconds(), bucket_count)
+
+// -----------------------------------------------------------------------------
+// Below are helpers used by other macros. Shouldn't be used directly. ---------
+
+// This is necessary to expand __COUNTER__ to an actual value.
+#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(               \
+    name, time_provider, histogram_exporter, max_value, suffix) \
+  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(                       \
+      name, time_provider, histogram_exporter, max_value, suffix)
+
+// Creates a static histogram pointer and a scoped object referring to it
+// throught the |histogram_exporter| functor. Both the pointer and the scoped
+// object are uniquely-named, using the unique |suffix| passed in.
+#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(                            \
+    name, time_provider, histogram_exporter, max_value, suffix)            \
+  IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, 1, max_value, 50, suffix) \
+  auto scoped_uma_histogram_timer_##suffix =                               \
+      impl::ScopedTimerImplFactory<time_provider>::Start(                  \
+          histogram_exporter(histogram_##suffix));
+
+// This is necessary to expand __COUNTER__ to an actual value.
+#define IMPL_UMA_HISTOGRAM_MICRO_TIMES_EXPANDER(name, max_value, suffix, \
+                                                sample)                  \
+  IMPL_UMA_HISTOGRAM_MICRO_TIMES_UNIQUE(name, max_value, suffix, sample)
+
+// Defines a static UMA histogram pointer and writes a |sample| to it.
+#define IMPL_UMA_HISTOGRAM_ADD(name, sample, min, max, bucket_count)          \
+  do {                                                                        \
+    IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, 0) \
+    histogram_0->Add(sample);                                                 \
+  } while (0)
+
+// Defines a static pointer to a UMA histogram.
+//
+// WARNING: Static local variable initialization is deliberately *not*
+// thread-safe in Chrome builds. See the "-fno-threadsafe-statics" flag in
+// "build/config/compiler/BUILD.gn" and "/Zc:threadSafeInit-" in
+// "build/config/win/BUILD.gn" for details.
+#define IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, \
+                                                 suffix)                       \
+  static base::HistogramBase* histogram_##suffix =                             \
+      base::Histogram::FactoryGet(                                             \
+          name, min, max, bucket_count,                                        \
+          base::HistogramBase::kUmaTargetedHistogramFlag);
+
+namespace impl {
+
+// ExportFunctor implementation that puts measurements into a UMA |histogram|.
+template <bool is_microsec_precision>
+class ExportTimeDeltaToHistogram {
+ public:
+  ExportTimeDeltaToHistogram(base::HistogramBase* histogram)
+      : histogram_(histogram) {}
+
+  void operator()(base::TimeDelta duration) {
+    if (is_microsec_precision)
+      histogram_->Add(duration.InMicroseconds());
+    else
+      histogram_->Add(duration.InMilliseconds());
+  }
+
+ private:
+  base::HistogramBase* histogram_;
+};
+
+using ExportMillisecondsToHistogram = ExportTimeDeltaToHistogram<false>;
+using ExportMicrosecondsToHistogram = ExportTimeDeltaToHistogram<true>;
+
+}  // namespace impl
+
+}  // namespace subresource_filter
+
+#endif  // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index e1504f7..929756b 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -155,9 +155,9 @@
 #endif
 
   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
-  feature_list->InitializeFromCommandLine(
-      command_line.GetSwitchValueASCII(switches::kEnableFeatures),
-      command_line.GetSwitchValueASCII(switches::kDisableFeatures));
+  base::FieldTrialList::CreateFeaturesFromCommandLine(
+      command_line, switches::kEnableFeatures, switches::kDisableFeatures,
+      feature_list.get());
   base::FeatureList::SetInstance(std::move(feature_list));
 }
 
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index c786b11..d311660a 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -219,19 +219,11 @@
 // static
 void BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(
     base::CommandLine* cmd_line) {
-  std::string enabled_features;
-  std::string disabled_features;
-  base::FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features,
-                                                        &disabled_features);
-  if (!enabled_features.empty())
-    cmd_line->AppendSwitchASCII(switches::kEnableFeatures, enabled_features);
-  if (!disabled_features.empty())
-    cmd_line->AppendSwitchASCII(switches::kDisableFeatures, disabled_features);
-
   // If we run base::FieldTrials, we want to pass to their state to the
   // child process so that it can act in accordance with each state.
-  base::FieldTrialList::CopyFieldTrialStateToFlags(switches::kFieldTrialHandle,
-                                                   cmd_line);
+  base::FieldTrialList::CopyFieldTrialStateToFlags(
+      switches::kFieldTrialHandle, switches::kEnableFeatures,
+      switches::kDisableFeatures, cmd_line);
 }
 
 void BrowserChildProcessHostImpl::Launch(
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index e5db5003..17d924e5 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -128,6 +128,9 @@
   basic_info->Append(NewDescriptionValuePair(
       "In-process GPU", new base::FundamentalValue(gpu_info.in_process_gpu)));
   basic_info->Append(NewDescriptionValuePair(
+      "Passthrough Command Decoder",
+      new base::FundamentalValue(gpu_info.passthrough_cmd_decoder)));
+  basic_info->Append(NewDescriptionValuePair(
       "Sandboxed", new base::FundamentalValue(gpu_info.sandboxed)));
   basic_info->Append(NewDescriptionValuePair(
       "GPU0", GPUDeviceToString(gpu_info.gpu)));
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 216d953..179b773 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -162,7 +162,8 @@
     switches::kGpuTestingGLVendor,
     switches::kGpuTestingGLRenderer,
     switches::kGpuTestingGLVersion,
-    switches::kDisableGpuDriverBugWorkarounds};
+    switches::kDisableGpuDriverBugWorkarounds,
+    switches::kUsePassthroughCmdDecoder};
 
 enum GPUProcessLifetimeEvent {
   LAUNCHED,
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index da25e25..c65b5cb 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -462,6 +462,9 @@
 
 #if defined(OS_ANDROID)
   prefs.device_supports_mouse = false;
+
+  prefs.video_fullscreen_orientation_lock_enabled =
+      base::FeatureList::IsEnabled(media::kVideoFullscreenOrientationLock);
 #endif
 
   prefs.pointer_events_max_touch_points = ui::MaxTouchPoints();
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 286c6276..29adfca 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -42,6 +42,8 @@
   // Android does not yet support switching of audio output devices
   WebRuntimeFeatures::enableAudioOutputDevices(false);
   WebRuntimeFeatures::enableAutoplayMutedVideos(true);
+  // Android does not yet support SystemMonitor.
+  WebRuntimeFeatures::enableOnDeviceChange(false);
 #else
   WebRuntimeFeatures::enableNavigatorContentUtils(true);
 #endif  // defined(OS_ANDROID)
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc
index 64eafb6..6a888d2 100644
--- a/content/public/common/web_preferences.cc
+++ b/content/public/common/web_preferences.cc
@@ -208,6 +208,7 @@
       resue_global_for_unowned_main_frame(false),
       progress_bar_completion(ProgressBarCompletion::LOAD_EVENT),
       spellcheck_enabled_by_default(true),
+      video_fullscreen_orientation_lock_enabled(false),
 #endif
 #if defined(OS_ANDROID)
       default_minimum_page_scale_factor(0.25f),
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h
index d7e3f80..e6ad3dcf 100644
--- a/content/public/common/web_preferences.h
+++ b/content/public/common/web_preferences.h
@@ -247,6 +247,8 @@
   // Specifies default setting for spellcheck when the spellcheck attribute is
   // not explicitly specified.
   bool spellcheck_enabled_by_default;
+  // If enabled, when a video goes fullscreen, the orientation should be locked.
+  bool video_fullscreen_orientation_lock_enabled;
 #endif
 
   // Default (used if the page or UA doesn't override these) values for page
diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc
index 7c1c929d..73e2b51 100644
--- a/content/renderer/media/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/media_stream_audio_processor_options.cc
@@ -134,32 +134,8 @@
   return the_default;
 }
 
-void SetIfNotSet(rtc::Optional<bool>* field, bool value) {
-  if (!*field) {
-    *field = rtc::Optional<bool>(value);
-  }
-}
-
 }  // namespace
 
-// TODO(xians): Remove this method after the APM in WebRtc is deprecated.
-void MediaAudioConstraints::ApplyFixedAudioConstraints(
-    cricket::AudioOptions* options) {
-  SetIfNotSet(&options->echo_cancellation, true);
-#if defined(OS_ANDROID)
-  SetIfNotSet(&options->extended_filter_aec, false);
-#else
-  // Enable the extended filter mode AEC on all non-mobile platforms.
-  SetIfNotSet(&options->extended_filter_aec, true);
-#endif
-  SetIfNotSet(&options->auto_gain_control, true);
-  SetIfNotSet(&options->experimental_agc, true);
-  SetIfNotSet(&options->noise_suppression, true);
-  SetIfNotSet(&options->highpass_filter, true);
-  SetIfNotSet(&options->typing_detection, true);
-  SetIfNotSet(&options->experimental_ns, true);
-}
-
 MediaAudioConstraints::MediaAudioConstraints(
     const blink::WebMediaConstraints& constraints, int effects)
     : constraints_(constraints),
diff --git a/content/renderer/media/media_stream_audio_processor_options.h b/content/renderer/media/media_stream_audio_processor_options.h
index cb7eb797..a7fe921 100644
--- a/content/renderer/media/media_stream_audio_processor_options.h
+++ b/content/renderer/media/media_stream_audio_processor_options.h
@@ -52,7 +52,6 @@
   // be added.
   // TODO(hta): Switch to an interface without "cricket::" when webrtc has one.
 
-  static void ApplyFixedAudioConstraints(cricket::AudioOptions* options);
 
   // |effects| is the bitmasks telling whether certain platform
   // hardware audio effects are enabled, like hardware echo cancellation. If
diff --git a/content/renderer/media/mock_media_stream_dispatcher.cc b/content/renderer/media/mock_media_stream_dispatcher.cc
index 77bea1f..db27d90 100644
--- a/content/renderer/media/mock_media_stream_dispatcher.cc
+++ b/content/renderer/media/mock_media_stream_dispatcher.cc
@@ -40,10 +40,10 @@
   video_array_.clear();
 
   if (controls.audio.requested) {
-    AddAudioInputDeviceToArray(false);
+    AddAudioInputDeviceToArray(false, controls.audio.device_id);
   }
   if (controls.video.requested) {
-    AddVideoDeviceToArray(true);
+    AddVideoDeviceToArray(true, controls.video.device_id);
   }
   ++request_stream_counter_;
 }
@@ -82,9 +82,10 @@
 }
 
 void MockMediaStreamDispatcher::AddAudioInputDeviceToArray(
-    bool matched_output) {
+    bool matched_output,
+    const std::string& device_id) {
   StreamDeviceInfo audio;
-  audio.device.id = test_same_id_ ? "test_id" : "audio_input_device_id";
+  audio.device.id = test_same_id_ ? "test_id" : device_id;
   audio.device.id = audio.device.id + base::IntToString(session_id_);
   audio.device.name = "microphone";
   audio.device.type = MEDIA_DEVICE_AUDIO_CAPTURE;
@@ -100,9 +101,11 @@
   audio_input_array_.push_back(audio);
 }
 
-void MockMediaStreamDispatcher::AddVideoDeviceToArray(bool facing_user) {
+void MockMediaStreamDispatcher::AddVideoDeviceToArray(
+    bool facing_user,
+    const std::string& device_id) {
   StreamDeviceInfo video;
-  video.device.id = test_same_id_ ? "test_id" : "video_device_id";
+  video.device.id = test_same_id_ ? "test_id" : device_id;
   video.device.id = video.device.id + base::IntToString(session_id_);
   video.device.name = "usb video camera";
   video.device.type = MEDIA_DEVICE_VIDEO_CAPTURE;
diff --git a/content/renderer/media/mock_media_stream_dispatcher.h b/content/renderer/media/mock_media_stream_dispatcher.h
index d23f551..6550031 100644
--- a/content/renderer/media/mock_media_stream_dispatcher.h
+++ b/content/renderer/media/mock_media_stream_dispatcher.h
@@ -48,8 +48,9 @@
   const StreamDeviceInfoArray& video_array() const { return video_array_; }
 
  private:
-  void AddAudioInputDeviceToArray(bool matched_output);
-  void AddVideoDeviceToArray(bool facing_user);
+  void AddAudioInputDeviceToArray(bool matched_output,
+                                  const std::string& device_id);
+  void AddVideoDeviceToArray(bool facing_user, const std::string& device_id);
 
   int audio_input_request_id_;
   base::WeakPtr<MediaStreamDispatcherEventHandler> event_handler_;
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index 2381c46..b7cccf7 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -240,6 +240,13 @@
   // webGetUserMedia.
   UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA);
   DCHECK(CalledOnValidThread());
+  DCHECK(!user_media_request.isNull());
+  // ownerDocument may be null if we are in a test.
+  // In that case, it's OK to not check frame().
+  DCHECK(user_media_request.ownerDocument().isNull() ||
+         render_frame()->GetWebFrame() ==
+             static_cast<blink::WebFrame*>(
+                 user_media_request.ownerDocument().frame()));
 
   if (RenderThreadImpl::current()) {
     RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia(
@@ -249,61 +256,41 @@
   int request_id = g_next_request_id++;
   std::unique_ptr<StreamControls> controls = base::MakeUnique<StreamControls>();
 
-  // |user_media_request| can't be mocked. So in order to test at all we check
-  // if it isNull.
-  // TODO(guidou): Remove this test-specific code path.
-  if (user_media_request.isNull()) {
-    // We are in a test.
-    controls->audio.requested = true;
-    controls->video.requested = true;
+  bool enable_automatic_output_device_selection = false;
+  bool request_audio_input_devices = false;
+  if (user_media_request.audio()) {
+    CopyConstraintsToTrackControls(user_media_request.audioConstraints(),
+                                   &controls->audio,
+                                   &request_audio_input_devices);
+    CopyHotwordAndLocalEchoToStreamControls(
+        user_media_request.audioConstraints(), controls.get());
+    // Check if this input device should be used to select a matching output
+    // device for audio rendering.
+    GetConstraintValueAsBoolean(
+        user_media_request.audioConstraints(),
+        &blink::WebMediaTrackConstraintSet::renderToAssociatedSink,
+        &enable_automatic_output_device_selection);
+  }
+  bool request_video_input_devices = false;
+  if (user_media_request.video()) {
+    CopyConstraintsToTrackControls(user_media_request.videoConstraints(),
+                                   &controls->video,
+                                   &request_video_input_devices);
+  }
+
+  url::Origin security_origin = user_media_request.getSecurityOrigin();
+  if (request_audio_input_devices || request_video_input_devices) {
+    GetMediaDevicesDispatcher()->EnumerateDevices(
+        request_audio_input_devices, request_video_input_devices,
+        false /* request_audio_output_devices */, security_origin,
+        base::Bind(&UserMediaClientImpl::SelectUserMediaDevice,
+                   weak_factory_.GetWeakPtr(), request_id, user_media_request,
+                   base::Passed(&controls),
+                   enable_automatic_output_device_selection, security_origin));
+  } else {
     FinalizeRequestUserMedia(
         request_id, user_media_request, std::move(controls),
-        false /* automatic_output_device_selection */, url::Origin());
-  } else {
-    // ownerDocument may be null if we are in a test.
-    // In that case, it's OK to not check frame().
-    DCHECK(user_media_request.ownerDocument().isNull() ||
-           render_frame()->GetWebFrame() ==
-               static_cast<blink::WebFrame*>(
-                   user_media_request.ownerDocument().frame()));
-
-    bool enable_automatic_output_device_selection = false;
-    bool request_audio_input_devices = false;
-    if (user_media_request.audio()) {
-      CopyConstraintsToTrackControls(user_media_request.audioConstraints(),
-                                     &controls->audio,
-                                     &request_audio_input_devices);
-      CopyHotwordAndLocalEchoToStreamControls(
-          user_media_request.audioConstraints(), controls.get());
-      // Check if this input device should be used to select a matching output
-      // device for audio rendering.
-      GetConstraintValueAsBoolean(
-          user_media_request.audioConstraints(),
-          &blink::WebMediaTrackConstraintSet::renderToAssociatedSink,
-          &enable_automatic_output_device_selection);
-    }
-    bool request_video_input_devices = false;
-    if (user_media_request.video()) {
-      CopyConstraintsToTrackControls(user_media_request.videoConstraints(),
-                                     &controls->video,
-                                     &request_video_input_devices);
-    }
-
-    url::Origin security_origin = user_media_request.getSecurityOrigin();
-    if (request_audio_input_devices || request_video_input_devices) {
-      GetMediaDevicesDispatcher()->EnumerateDevices(
-          request_audio_input_devices, request_video_input_devices,
-          false /* request_audio_output_devices */, security_origin,
-          base::Bind(&UserMediaClientImpl::SelectUserMediaDevice,
-                     weak_factory_.GetWeakPtr(), request_id, user_media_request,
-                     base::Passed(&controls),
-                     enable_automatic_output_device_selection,
-                     security_origin));
-    } else {
-      FinalizeRequestUserMedia(
-          request_id, user_media_request, std::move(controls),
-          enable_automatic_output_device_selection, security_origin);
-    }
+        enable_automatic_output_device_selection, security_origin);
   }
 }
 
@@ -445,29 +432,16 @@
   }
   request_info->generated = true;
 
-  // WebUserMediaRequest don't have an implementation in unit tests.
-  // Therefore we need to check for isNull here and initialize the
-  // constraints.
-  blink::WebUserMediaRequest* request = &(request_info->request);
-  blink::WebMediaConstraints audio_constraints;
-  blink::WebMediaConstraints video_constraints;
-  if (request->isNull()) {
-    audio_constraints.initialize();
-    video_constraints.initialize();
-  } else {
-    audio_constraints = request->audioConstraints();
-    video_constraints = request->videoConstraints();
-  }
-
+  DCHECK(!request_info->request.isNull());
   blink::WebVector<blink::WebMediaStreamTrack> audio_track_vector(
       audio_array.size());
-  CreateAudioTracks(audio_array, audio_constraints, &audio_track_vector,
-                    request_info);
+  CreateAudioTracks(audio_array, request_info->request.audioConstraints(),
+                    &audio_track_vector, request_info);
 
   blink::WebVector<blink::WebMediaStreamTrack> video_track_vector(
       video_array.size());
-  CreateVideoTracks(video_array, video_constraints, &video_track_vector,
-                    request_info);
+  CreateVideoTracks(video_array, request_info->request.videoConstraints(),
+                    &video_track_vector, request_info);
 
   blink::WebString webkit_id = base::UTF8ToUTF16(label);
   blink::WebMediaStream* web_stream = &(request_info->web_stream);
diff --git a/content/renderer/media/user_media_client_impl_unittest.cc b/content/renderer/media/user_media_client_impl_unittest.cc
index 437f3c2c..41f634f5 100644
--- a/content/renderer/media/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/user_media_client_impl_unittest.cc
@@ -36,6 +36,42 @@
 
 namespace content {
 
+blink::WebMediaConstraints CreateDefaultConstraints() {
+  MockConstraintFactory factory;
+  factory.AddAdvanced();
+  return factory.CreateWebMediaConstraints();
+}
+
+blink::WebMediaConstraints CreateDeviceConstraints(
+    const char* basic_exact_value,
+    const char* basic_ideal_value = nullptr,
+    const char* advanced_exact_value = nullptr,
+    const char* advanced_ideal_value = nullptr) {
+  MockConstraintFactory factory;
+  blink::WebMediaTrackConstraintSet basic;
+  if (basic_exact_value) {
+    factory.basic().deviceId.setExact(
+        blink::WebString::fromUTF8(basic_exact_value));
+  }
+  if (basic_ideal_value) {
+    blink::WebString value = blink::WebString::fromUTF8(basic_ideal_value);
+    factory.basic().deviceId.setIdeal(
+        blink::WebVector<blink::WebString>(&value, 1));
+  }
+
+  auto& advanced = factory.AddAdvanced();
+  if (advanced_exact_value) {
+    blink::WebString value = blink::WebString::fromUTF8(advanced_exact_value);
+    advanced.deviceId.setExact(value);
+  }
+  if (advanced_ideal_value) {
+    blink::WebString value = blink::WebString::fromUTF8(advanced_ideal_value);
+    advanced.deviceId.setIdeal(blink::WebVector<blink::WebString>(&value, 1));
+  }
+
+  return factory.CreateWebMediaConstraints();
+}
+
 class MockMediaStreamVideoCapturerSource : public MockMediaStreamVideoSource {
  public:
   MockMediaStreamVideoCapturerSource(
@@ -48,6 +84,13 @@
   }
 };
 
+const char kInvalidDeviceId[] = "invalid";
+const char kFakeAudioInputDeviceId1[] = "fake_audio_input 1";
+const char kFakeAudioInputDeviceId2[] = "fake_audio_input 2";
+const char kFakeVideoInputDeviceId1[] = "fake_video_input 1";
+const char kFakeVideoInputDeviceId2[] = "fake_video_input 2";
+const char kFakeAudioOutputDeviceId1[] = "fake_audio_output 1";
+
 class MockMediaDevicesDispatcherHost
     : public ::mojom::MediaDevicesDispatcherHost {
  public:
@@ -60,19 +103,19 @@
     std::vector<std::vector<MediaDeviceInfo>> result(NUM_MEDIA_DEVICE_TYPES);
     if (request_audio_input) {
       result[MEDIA_DEVICE_TYPE_AUDIO_INPUT].push_back(MediaDeviceInfo(
-          "fake_audio_input 1", "Fake Audio Input 1", "fake_group 1"));
+          kFakeAudioInputDeviceId1, "Fake Audio Input 1", "fake_group 1"));
       result[MEDIA_DEVICE_TYPE_AUDIO_INPUT].push_back(MediaDeviceInfo(
-          "fake_audio_input 2", "Fake Audio Input 2", "fake_group 2"));
+          kFakeAudioInputDeviceId2, "Fake Audio Input 2", "fake_group 2"));
     }
     if (request_video_input) {
       result[MEDIA_DEVICE_TYPE_VIDEO_INPUT].push_back(
-          MediaDeviceInfo("fake_video_input 1", "Fake Video Input 1", ""));
+          MediaDeviceInfo(kFakeVideoInputDeviceId1, "Fake Video Input 1", ""));
       result[MEDIA_DEVICE_TYPE_VIDEO_INPUT].push_back(
-          MediaDeviceInfo("fake_video_input 2", "Fake Video Input 2", ""));
+          MediaDeviceInfo(kFakeVideoInputDeviceId2, "Fake Video Input 2", ""));
     }
     if (request_audio_output) {
       result[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].push_back(MediaDeviceInfo(
-          "fake_audio_output 1", "Fake Audio Input 1", "fake_group 1"));
+          kFakeAudioOutputDeviceId1, "Fake Audio Input 1", "fake_group 1"));
     }
     callback.Run(result);
   }
@@ -114,7 +157,9 @@
   }
 
   void RequestUserMedia() {
-    blink::WebUserMediaRequest user_media_request;
+    blink::WebUserMediaRequest user_media_request =
+        blink::WebUserMediaRequest::createForTesting(
+            CreateDefaultConstraints(), CreateDefaultConstraints());
     RequestUserMedia(user_media_request);
   }
 
@@ -246,10 +291,10 @@
     child_process_.reset(new ChildProcess());
     dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
     ms_dispatcher_ = new MockMediaStreamDispatcher();
-    used_media_impl_.reset(new UserMediaClientImplUnderTest(
+    user_media_client_impl_.reset(new UserMediaClientImplUnderTest(
         dependency_factory_.get(),
         std::unique_ptr<MediaStreamDispatcher>(ms_dispatcher_)));
-    used_media_impl_->SetMediaDevicesDispatcherForTesting(
+    user_media_client_impl_->SetMediaDevicesDispatcherForTesting(
         binding_user_media.CreateInterfacePtrAndBind());
     base::WeakPtr<MediaDevicesEventDispatcher> event_dispatcher =
         MediaDevicesEventDispatcher::GetForRenderFrame(nullptr);
@@ -259,23 +304,24 @@
 
   void TearDown() override {
     MediaDevicesEventDispatcher::GetForRenderFrame(nullptr)->OnDestruct();
-    used_media_impl_.reset();
+    user_media_client_impl_.reset();
     blink::WebHeap::collectAllGarbageForTesting();
   }
 
   void LoadNewDocumentInFrame() {
-    used_media_impl_->WillCommitProvisionalLoad();
+    user_media_client_impl_->WillCommitProvisionalLoad();
   }
 
   blink::WebMediaStream RequestLocalMediaStream() {
-    used_media_impl_->RequestUserMedia();
+    user_media_client_impl_->RequestUserMedia();
     FakeMediaStreamDispatcherRequestUserMediaComplete();
     StartMockedVideoSource();
 
     EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_SUCCEEDED,
-              used_media_impl_->request_state());
+              user_media_client_impl_->request_state());
 
-    blink::WebMediaStream desc = used_media_impl_->last_generated_stream();
+    blink::WebMediaStream desc =
+        user_media_client_impl_->last_generated_stream();
     content::MediaStream* native_stream =
         content::MediaStream::GetMediaStream(desc);
     if (!native_stream) {
@@ -296,23 +342,22 @@
 
   void FakeMediaStreamDispatcherRequestUserMediaComplete() {
     // Audio request ID is used as the shared request ID.
-    used_media_impl_->OnStreamGenerated(
+    user_media_client_impl_->OnStreamGenerated(
         ms_dispatcher_->audio_input_request_id(),
-        ms_dispatcher_->stream_label(),
-        ms_dispatcher_->audio_input_array(),
+        ms_dispatcher_->stream_label(), ms_dispatcher_->audio_input_array(),
         ms_dispatcher_->video_array());
   }
 
   void StartMockedVideoSource() {
     MockMediaStreamVideoCapturerSource* video_source =
-        used_media_impl_->last_created_video_source();
+        user_media_client_impl_->last_created_video_source();
     if (video_source->SourceHasAttemptedToStart())
       video_source->StartMockedSource();
   }
 
   void FailToStartMockedVideoSource() {
     MockMediaStreamVideoCapturerSource* video_source =
-        used_media_impl_->last_created_video_source();
+        user_media_client_impl_->last_created_video_source();
     if (video_source->SourceHasAttemptedToStart())
       video_source->FailToStartMockedSource();
     blink::WebHeap::collectGarbageForTesting();
@@ -324,13 +369,41 @@
     blink::WebUserMediaRequest request =
         blink::WebUserMediaRequest::createForTesting(audio_constraints,
                                                      null_constraints);
-    used_media_impl_->RequestUserMedia(request);
-    bool result = used_media_impl_->UserMediaRequestHasAutomaticDeviceSelection(
+    user_media_client_impl_->RequestUserMedia(request);
+    bool result =
+        user_media_client_impl_->UserMediaRequestHasAutomaticDeviceSelection(
+            ms_dispatcher_->audio_input_request_id());
+    user_media_client_impl_->DeleteRequest(
         ms_dispatcher_->audio_input_request_id());
-    used_media_impl_->DeleteRequest(ms_dispatcher_->audio_input_request_id());
     return result;
   }
 
+  void TestValidRequestWithDeviceConstraints(
+      const blink::WebMediaConstraints& audio_constraints,
+      const blink::WebMediaConstraints& video_constraints,
+      const std::string& expected_audio_device_id,
+      const std::string& expected_video_device_id) {
+    DCHECK(!audio_constraints.isNull());
+    DCHECK(!video_constraints.isNull());
+    blink::WebUserMediaRequest request =
+        blink::WebUserMediaRequest::createForTesting(audio_constraints,
+                                                     video_constraints);
+    user_media_client_impl_->RequestUserMedia(request);
+    FakeMediaStreamDispatcherRequestUserMediaComplete();
+    StartMockedVideoSource();
+
+    EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_SUCCEEDED,
+              user_media_client_impl_->request_state());
+    EXPECT_EQ(1U, ms_dispatcher_->audio_input_array().size());
+    EXPECT_EQ(1U, ms_dispatcher_->video_array().size());
+    // MockMediaStreamDispatcher appends the session ID to its internal device
+    // IDs.
+    EXPECT_EQ(std::string(expected_audio_device_id) + "0",
+              ms_dispatcher_->audio_input_array()[0].device.id);
+    EXPECT_EQ(std::string(expected_video_device_id) + "0",
+              ms_dispatcher_->video_array()[0].device.id);
+  }
+
  protected:
   base::MessageLoop message_loop_;
   std::unique_ptr<ChildProcess> child_process_;
@@ -339,7 +412,7 @@
   mojo::Binding<::mojom::MediaDevicesDispatcherHost> binding_user_media;
   mojo::Binding<::mojom::MediaDevicesDispatcherHost> binding_event_dispatcher_;
 
-  std::unique_ptr<UserMediaClientImplUnderTest> used_media_impl_;
+  std::unique_ptr<UserMediaClientImplUnderTest> user_media_client_impl_;
   std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
 };
 
@@ -461,7 +534,7 @@
   RequestLocalMediaStream();
   // Makes sure the test itself don't hold a reference to the created
   // MediaStream.
-  used_media_impl_->ClearLastGeneratedStream();
+  user_media_client_impl_->ClearLastGeneratedStream();
   blink::WebHeap::collectAllGarbageForTesting();
 
   // Expect the sources to be stopped when the MediaStream goes out of scope.
@@ -483,13 +556,13 @@
 
 // This test what happens if a video source to a MediaSteam fails to start.
 TEST_F(UserMediaClientImplTest, MediaVideoSourceFailToStart) {
-  used_media_impl_->RequestUserMedia();
+  user_media_client_impl_->RequestUserMedia();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   FailToStartMockedVideoSource();
   EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_FAILED,
-            used_media_impl_->request_state());
+            user_media_client_impl_->request_state());
   EXPECT_EQ(MEDIA_DEVICE_TRACK_START_FAILURE,
-            used_media_impl_->error_reason());
+            user_media_client_impl_->error_reason());
   blink::WebHeap::collectAllGarbageForTesting();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
@@ -498,14 +571,14 @@
 
 // This test what happens if an audio source fail to initialize.
 TEST_F(UserMediaClientImplTest, MediaAudioSourceFailToInitialize) {
-  used_media_impl_->SetCreateSourceThatFails(true);
-  used_media_impl_->RequestUserMedia();
+  user_media_client_impl_->SetCreateSourceThatFails(true);
+  user_media_client_impl_->RequestUserMedia();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   StartMockedVideoSource();
   EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_FAILED,
-            used_media_impl_->request_state());
+            user_media_client_impl_->request_state());
   EXPECT_EQ(MEDIA_DEVICE_TRACK_START_FAILURE,
-            used_media_impl_->error_reason());
+            user_media_client_impl_->error_reason());
   blink::WebHeap::collectAllGarbageForTesting();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
@@ -515,37 +588,37 @@
 // This test what happens if UserMediaClientImpl is deleted before a source has
 // started.
 TEST_F(UserMediaClientImplTest, MediaStreamImplShutDown) {
-  used_media_impl_->RequestUserMedia();
+  user_media_client_impl_->RequestUserMedia();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_NOT_COMPLETE,
-            used_media_impl_->request_state());
-  used_media_impl_.reset();
+            user_media_client_impl_->request_state());
+  user_media_client_impl_.reset();
 }
 
 // This test what happens if a new document is loaded in the frame while the
 // MediaStream is being generated by the MediaStreamDispatcher.
 TEST_F(UserMediaClientImplTest, ReloadFrameWhileGeneratingStream) {
-  used_media_impl_->RequestUserMedia();
+  user_media_client_impl_->RequestUserMedia();
   LoadNewDocumentInFrame();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(0, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(0, ms_dispatcher_->stop_video_device_counter());
   EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_NOT_COMPLETE,
-            used_media_impl_->request_state());
+            user_media_client_impl_->request_state());
 }
 
 // This test what happens if a newdocument is loaded in the frame while the
 // sources are being started.
 TEST_F(UserMediaClientImplTest, ReloadFrameWhileGeneratingSources) {
-  used_media_impl_->RequestUserMedia();
+  user_media_client_impl_->RequestUserMedia();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   LoadNewDocumentInFrame();
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
   EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_NOT_COMPLETE,
-            used_media_impl_->request_state());
+            user_media_client_impl_->request_state());
 }
 
 // This test what happens if stop is called on a track after the frame has
@@ -572,16 +645,17 @@
 }
 
 TEST_F(UserMediaClientImplTest, EnumerateMediaDevices) {
-  used_media_impl_->RequestMediaDevices();
+  user_media_client_impl_->RequestMediaDevices();
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_SUCCEEDED,
-            used_media_impl_->request_state());
-  EXPECT_EQ(static_cast<size_t>(5), used_media_impl_->last_devices().size());
+            user_media_client_impl_->request_state());
+  EXPECT_EQ(static_cast<size_t>(5),
+            user_media_client_impl_->last_devices().size());
 
   // Audio input device with matched output ID.
   const blink::WebMediaDeviceInfo* device =
-      &used_media_impl_->last_devices()[0];
+      &user_media_client_impl_->last_devices()[0];
   EXPECT_FALSE(device->deviceId().isEmpty());
   EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput,
             device->kind());
@@ -589,7 +663,7 @@
   EXPECT_FALSE(device->groupId().isEmpty());
 
   // Audio input device without matched output ID.
-  device = &used_media_impl_->last_devices()[1];
+  device = &user_media_client_impl_->last_devices()[1];
   EXPECT_FALSE(device->deviceId().isEmpty());
   EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput,
             device->kind());
@@ -597,14 +671,14 @@
   EXPECT_FALSE(device->groupId().isEmpty());
 
   // Video input devices.
-  device = &used_media_impl_->last_devices()[2];
+  device = &user_media_client_impl_->last_devices()[2];
   EXPECT_FALSE(device->deviceId().isEmpty());
   EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput,
             device->kind());
   EXPECT_FALSE(device->label().isEmpty());
   EXPECT_TRUE(device->groupId().isEmpty());
 
-  device = &used_media_impl_->last_devices()[3];
+  device = &user_media_client_impl_->last_devices()[3];
   EXPECT_FALSE(device->deviceId().isEmpty());
   EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput,
             device->kind());
@@ -612,7 +686,7 @@
   EXPECT_TRUE(device->groupId().isEmpty());
 
   // Audio output device.
-  device = &used_media_impl_->last_devices()[4];
+  device = &user_media_client_impl_->last_devices()[4];
   EXPECT_FALSE(device->deviceId().isEmpty());
   EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioOutput,
             device->kind());
@@ -620,18 +694,20 @@
   EXPECT_FALSE(device->groupId().isEmpty());
 
   // Verfify group IDs.
-  EXPECT_TRUE(used_media_impl_->last_devices()[0].groupId().equals(
-                  used_media_impl_->last_devices()[4].groupId()));
-  EXPECT_FALSE(used_media_impl_->last_devices()[1].groupId().equals(
-                   used_media_impl_->last_devices()[4].groupId()));
+  EXPECT_TRUE(user_media_client_impl_->last_devices()[0].groupId().equals(
+      user_media_client_impl_->last_devices()[4].groupId()));
+  EXPECT_FALSE(user_media_client_impl_->last_devices()[1].groupId().equals(
+      user_media_client_impl_->last_devices()[4].groupId()));
 }
 
 TEST_F(UserMediaClientImplTest, RenderToAssociatedSinkConstraint) {
   // For a null UserMediaRequest (no audio requested), we expect false.
-  used_media_impl_->RequestUserMedia();
-  EXPECT_FALSE(used_media_impl_->UserMediaRequestHasAutomaticDeviceSelection(
-      ms_dispatcher_->audio_input_request_id()));
-  used_media_impl_->DeleteRequest(ms_dispatcher_->audio_input_request_id());
+  user_media_client_impl_->RequestUserMedia();
+  EXPECT_FALSE(
+      user_media_client_impl_->UserMediaRequestHasAutomaticDeviceSelection(
+          ms_dispatcher_->audio_input_request_id()));
+  user_media_client_impl_->DeleteRequest(
+      ms_dispatcher_->audio_input_request_id());
 
   // If audio is requested, but no constraint, it should be true.
   // Currently we expect it to be false due to a suspected bug in the
@@ -670,7 +746,7 @@
   EXPECT_CALL(
       media_devices_dispatcher_,
       SubscribeDeviceChangeNotifications(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, _, _));
-  used_media_impl_->SetMediaDeviceChangeObserver();
+  user_media_client_impl_->SetMediaDeviceChangeObserver();
   base::RunLoop().RunUntilIdle();
 
   base::WeakPtr<MediaDevicesEventDispatcher> event_dispatcher =
@@ -690,7 +766,7 @@
   EXPECT_CALL(
       media_devices_dispatcher_,
       UnsubscribeDeviceChangeNotifications(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, _));
-  used_media_impl_->RemoveMediaDeviceChangeObserver();
+  user_media_client_impl_->RemoveMediaDeviceChangeObserver();
   base::RunLoop().RunUntilIdle();
 }
 
@@ -720,4 +796,87 @@
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
 }
 
+TEST_F(UserMediaClientImplTest, CreateWithMandatoryInvalidAudioDeviceId) {
+  blink::WebMediaConstraints audio_constraints =
+      CreateDeviceConstraints(kInvalidDeviceId);
+  blink::WebUserMediaRequest request =
+      blink::WebUserMediaRequest::createForTesting(
+          audio_constraints, blink::WebMediaConstraints());
+  user_media_client_impl_->RequestUserMedia(request);
+  EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_FAILED,
+            user_media_client_impl_->request_state());
+}
+
+TEST_F(UserMediaClientImplTest, CreateWithMandatoryInvalidVideoDeviceId) {
+  blink::WebMediaConstraints video_constraints =
+      CreateDeviceConstraints(kInvalidDeviceId);
+  blink::WebUserMediaRequest request =
+      blink::WebUserMediaRequest::createForTesting(blink::WebMediaConstraints(),
+                                                   video_constraints);
+  user_media_client_impl_->RequestUserMedia(request);
+  EXPECT_EQ(UserMediaClientImplUnderTest::REQUEST_FAILED,
+            user_media_client_impl_->request_state());
+}
+
+TEST_F(UserMediaClientImplTest, CreateWithMandatoryValidDeviceIds) {
+  blink::WebMediaConstraints audio_constraints =
+      CreateDeviceConstraints(kFakeAudioInputDeviceId1);
+  blink::WebMediaConstraints video_constraints =
+      CreateDeviceConstraints(kFakeVideoInputDeviceId1);
+  TestValidRequestWithDeviceConstraints(audio_constraints, video_constraints,
+                                        kFakeAudioInputDeviceId1,
+                                        kFakeVideoInputDeviceId1);
+}
+
+TEST_F(UserMediaClientImplTest, CreateWithBasicIdealValidDeviceId) {
+  blink::WebMediaConstraints audio_constraints =
+      CreateDeviceConstraints(nullptr, kFakeAudioInputDeviceId1);
+  blink::WebMediaConstraints video_constraints =
+      CreateDeviceConstraints(nullptr, kFakeVideoInputDeviceId1);
+  TestValidRequestWithDeviceConstraints(audio_constraints, video_constraints,
+                                        kFakeAudioInputDeviceId1,
+                                        kFakeVideoInputDeviceId1);
+}
+
+TEST_F(UserMediaClientImplTest, CreateWithAdvancedExactValidDeviceId) {
+  blink::WebMediaConstraints audio_constraints =
+      CreateDeviceConstraints(nullptr, nullptr, kFakeAudioInputDeviceId1);
+  blink::WebMediaConstraints video_constraints = CreateDeviceConstraints(
+      nullptr, nullptr, kFakeVideoInputDeviceId1);
+  TestValidRequestWithDeviceConstraints(audio_constraints, video_constraints,
+                                        kFakeAudioInputDeviceId1,
+                                        kFakeVideoInputDeviceId1);
+}
+
+TEST_F(UserMediaClientImplTest, CreateWithAdvancedIdealValidDeviceId) {
+  blink::WebMediaConstraints audio_constraints = CreateDeviceConstraints(
+      nullptr, nullptr, nullptr, kFakeAudioInputDeviceId1);
+  blink::WebMediaConstraints video_constraints = CreateDeviceConstraints(
+      nullptr, nullptr, nullptr, kFakeVideoInputDeviceId1);
+  TestValidRequestWithDeviceConstraints(audio_constraints, video_constraints,
+                                        kFakeAudioInputDeviceId1,
+                                        kFakeVideoInputDeviceId1);
+}
+
+TEST_F(UserMediaClientImplTest, CreateWithAllOptionalInvalidDeviceId) {
+  blink::WebMediaConstraints audio_constraints = CreateDeviceConstraints(
+      nullptr, kInvalidDeviceId, kInvalidDeviceId, kInvalidDeviceId);
+  blink::WebMediaConstraints video_constraints = CreateDeviceConstraints(
+      nullptr, kInvalidDeviceId, kInvalidDeviceId, kInvalidDeviceId);
+  // MockMediaStreamDispatcher uses empty string as default device ID.
+  TestValidRequestWithDeviceConstraints(audio_constraints, video_constraints,
+                                        std::string(), std::string());
+}
+
+TEST_F(UserMediaClientImplTest,
+       CreateWithAdvancedIdealValidOtherOptionalInvalidDeviceId) {
+  blink::WebMediaConstraints audio_constraints = CreateDeviceConstraints(
+      nullptr, kInvalidDeviceId, kInvalidDeviceId, kFakeAudioInputDeviceId1);
+  blink::WebMediaConstraints video_constraints = CreateDeviceConstraints(
+      nullptr, kInvalidDeviceId, kInvalidDeviceId, kFakeVideoInputDeviceId1);
+  TestValidRequestWithDeviceConstraints(audio_constraints, video_constraints,
+                                        kFakeAudioInputDeviceId1,
+                                        kFakeVideoInputDeviceId1);
+}
+
 }  // namespace content
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index 53a92f7..85bfe6e1 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 #include "cc/output/buffer_to_texture_target_map.h"
 #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
 #include "components/discardable_memory/service/discardable_shared_memory_manager.h"
@@ -441,6 +442,9 @@
   EXPECT_FALSE(memory->Lock());
 }
 
+// Disable the test for the Android asan build.
+// See http://crbug.com/667837 for detail.
+#if !(defined(OS_ANDROID) && defined(ADDRESS_SANITIZER))
 IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
                        DiscardableMemoryAddressSpace) {
   const size_t kLargeSize = 4 * 1024 * 1024;   // 4MiB.
@@ -458,6 +462,7 @@
     instances.push_back(std::move(memory));
   }
 }
+#endif
 
 IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
                        ReleaseFreeDiscardableMemory) {
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 1c416ec0..26af975 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1040,6 +1040,9 @@
       base::SysInfo::IsLowEndDevice();
   // TODO(mlamouri): rename this setting "isLowEndDevice".
   settings->setForcePreloadNoneForMediaElements(is_low_end_device);
+
+  WebRuntimeFeatures::enableVideoFullscreenOrientationLock(
+      prefs.video_fullscreen_orientation_lock_enabled);
 #endif
 
   settings->setViewportEnabled(prefs.viewport_enabled);
diff --git a/content/test/gpu/gpu_tests/gpu_test_expectations.py b/content/test/gpu/gpu_tests/gpu_test_expectations.py
index 9df6f00..b8f4d0e6 100644
--- a/content/test/gpu/gpu_tests/gpu_test_expectations.py
+++ b/content/test/gpu/gpu_tests/gpu_test_expectations.py
@@ -5,7 +5,7 @@
 from gpu_tests import test_expectations
 
 ANGLE_CONDITIONS = ['d3d9', 'd3d11', 'opengl', 'no_angle']
-
+CMD_DECODER_CONDITIONS = ['passthrough', 'no_passthrough']
 GPU_CONDITIONS = ['amd', 'arm', 'broadcom', 'hisilicon', 'intel', 'imagination',
                   'nvidia', 'qualcomm', 'vivante']
 
@@ -15,6 +15,7 @@
     self.gpu_conditions = []
     self.device_id_conditions = []
     self.angle_conditions = []
+    self.cmd_decoder_conditions = []
     self.max_num_retries = max_num_retries
     assert self.max_num_retries == 0 or expectation == 'flaky'
     super(GpuExpectation, self).__init__(
@@ -67,6 +68,8 @@
         self.gpu_conditions.append(cl)
       elif cl in ANGLE_CONDITIONS:
         self.angle_conditions.append(cl)
+      elif cl in CMD_DECODER_CONDITIONS:
+        self.cmd_decoder_conditions.append(cl)
       else:
         # Delegate to superclass.
         super(GpuExpectation, self).ParseCondition(condition)
@@ -112,8 +115,12 @@
       angle_matches = (
         (not expectation.angle_conditions) or
         angle_renderer in expectation.angle_conditions)
+      cmd_decoder = self._GetCommandDecoder(gpu_info)
+      cmd_decoder_matches = (
+        (not expectation.cmd_decoder_conditions) or
+        cmd_decoder in expectation.cmd_decoder_conditions)
 
-    return gpu_matches and angle_matches
+    return gpu_matches and angle_matches and cmd_decoder_matches
 
   def _GetGpuVendorString(self, gpu_info):
     if gpu_info:
@@ -149,3 +156,9 @@
         elif 'OpenGL' in gl_renderer:
           return 'opengl'
     return 'no_angle'
+
+  def _GetCommandDecoder(self, gpu_info):
+    if gpu_info and gpu_info.aux_attributes and \
+        gpu_info.aux_attributes.get('passthrough_cmd_decoder', False):
+      return 'passthrough'
+    return 'no_passthrough'
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index e25370a..7a6d171 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -133,14 +133,14 @@
 
     # Win NVIDIA failures
     self.Flaky('conformance/textures/misc/texture-npot-video.html',
-        ['win', 'nvidia'], bug=626524)
+        ['win', 'nvidia', 'no_passthrough'], bug=626524)
     self.Flaky('conformance/textures/misc/texture-upload-size.html',
         ['win', 'nvidia'], bug=630860)
 
     # Win7 / Intel failures
     self.Fail('conformance/textures/misc/' +
               'copy-tex-image-and-sub-image-2d.html',
-              ['win7', 'intel'])
+              ['win7', 'intel', 'no_passthrough'])
 
     # Win / AMD flakiness seen on new tryservers.
     # It's unfortunate that this suppression needs to be so broad, but
@@ -214,6 +214,114 @@
     self.Fail('conformance/uniforms/uniform-default-values.html',
         ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
 
+    # Win / Passthrough command decoder
+    self.Fail('conformance/attribs/gl-vertexattribpointer.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/*', ['win', 'passthrough', 'd3d11'],
+        bug=1523) # angle bug ID
+    self.Fail('conformance/canvas/framebuffer-bindings-unaffected-on-' +
+        'resize.html', ['win', 'passthrough', 'd3d11'], bug=665521)
+    self.Fail('conformance/glsl/bugs/essl3-shaders-with-webgl1.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/attrib-location-length-limits.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/shader-varying-packing-restrictions.html',
+        ['win', 'passthrough', 'd3d11'], bug=1638) # angle bug ID
+    self.Fail('conformance/glsl/misc/shader-with-257-character-define.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/shader-with-257-character-identifier.' +
+        'frag.html', ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/shader-with-dfdx.frag.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/shaders-with-invariance.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/shaders-with-name-conflicts.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/misc/shaders-with-uniform-structs.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/glsl/variables/glsl-built-ins.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/limits/gl-line-width.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/misc/error-reporting.html',
+        ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/misc/invalid-passed-params.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/misc/object-deletion-behaviour.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/misc/type-conversion-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/misc/uninitialized-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=1635) # angle bug ID
+    self.Fail('conformance/misc/webgl-specific.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/conformance/quickCheckAPI-B2.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/more/conformance/quickCheckAPI-D_G.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/more/functions/copyTexImage2D.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/more/functions/copyTexImage2DBadArgs.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/functions/copyTexSubImage2D.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/more/functions/drawArrays.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/functions/drawArraysOutOfBounds.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/functions/drawElementsBadArgs.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/functions/texSubImage2DBadArgs.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/functions/texSubImage2DHTMLBadArgs.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/more/functions/vertexAttribPointerBadArgs.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/programs/get-active-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/programs/program-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/reading/read-pixels-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/renderbuffers/feedback-loop.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/renderbuffers/framebuffer-object-attachment.html',
+        ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/renderbuffers/framebuffer-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/rendering/draw-arrays-out-of-bounds.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/rendering/draw-elements-out-of-bounds.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/textures/misc/copy-tex-image-2d-formats.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/textures/misc/copy-tex-image-and-sub-image-2d.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/textures/misc/mipmap-fbo.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/textures/misc/tex-input-validation.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/textures/misc/texture-active-bind-2.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/textures/misc/texture-attachment-formats.html',
+        ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/textures/misc/texture-copying-feedback-loops.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/textures/misc/texture-fakeblack.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/textures/misc/texture-mips.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/textures/misc/texture-npot.html',
+        ['win', 'passthrough', 'd3d11'], bug=665518)
+    self.Fail('conformance/textures/misc/texture-npot-video.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/uniforms/uniform-samplers-test.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('WebglExtension_OES_texture_float_linear',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('WebglExtension_OES_texture_half_float_linear',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+
     # Mac failures
     self.Flaky('conformance/extensions/oes-texture-float-with-video.html',
         ['mac'], bug=599272)
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations_unittest.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations_unittest.py
index 08961fe..71661466a 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations_unittest.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations_unittest.py
@@ -107,6 +107,7 @@
         (
           set(e.os_conditions),
           set(e.browser_conditions),
+          set(e.cmd_decoder_conditions),
           set(e.angle_conditions),
         ),
         set(e.gpu_conditions),
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 3af11f6..6ce2d78 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -76,6 +76,7 @@
       sandboxed(false),
       process_crash_count(0),
       in_process_gpu(true),
+      passthrough_cmd_decoder(false),
       basic_info_state(kCollectInfoNone),
       context_info_state(kCollectInfoNone),
 #if defined(OS_WIN)
@@ -125,6 +126,7 @@
     bool sandboxed;
     int process_crash_count;
     bool in_process_gpu;
+    bool passthrough_cmd_decoder;
     CollectInfoResult basic_info_state;
     CollectInfoResult context_info_state;
 #if defined(OS_WIN)
@@ -188,6 +190,7 @@
   enumerator->AddBool("sandboxed", sandboxed);
   enumerator->AddInt("processCrashCount", process_crash_count);
   enumerator->AddBool("inProcessGpu", in_process_gpu);
+  enumerator->AddBool("passthroughCmdDecoder", passthrough_cmd_decoder);
   enumerator->AddInt("basicInfoState", basic_info_state);
   enumerator->AddInt("contextInfoState", context_info_state);
 #if defined(OS_WIN)
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
index fbb6aac..86479489 100644
--- a/gpu/config/gpu_info.h
+++ b/gpu/config/gpu_info.h
@@ -224,6 +224,9 @@
   // True if the GPU is running in the browser process instead of its own.
   bool in_process_gpu;
 
+  // True if the GPU process is using the passthrough command decoder.
+  bool passthrough_cmd_decoder;
+
   // The state of whether the basic/context/DxDiagnostics info is collected and
   // if the collection fails or not.
   CollectInfoResult basic_info_state;
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc
index 79ae1c8..2d2dfe2 100644
--- a/gpu/config/gpu_info_collector.cc
+++ b/gpu/config/gpu_info_collector.cc
@@ -228,6 +228,8 @@
   basic_gpu_info->sandboxed = context_gpu_info.sandboxed;
   basic_gpu_info->direct_rendering = context_gpu_info.direct_rendering;
   basic_gpu_info->in_process_gpu = context_gpu_info.in_process_gpu;
+  basic_gpu_info->passthrough_cmd_decoder =
+      context_gpu_info.passthrough_cmd_decoder;
   basic_gpu_info->context_info_state = context_gpu_info.context_info_state;
   basic_gpu_info->initialization_time = context_gpu_info.initialization_time;
   basic_gpu_info->video_decode_accelerator_capabilities =
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom
index 2dc02806..0e950cb 100644
--- a/gpu/ipc/common/gpu_info.mojom
+++ b/gpu/ipc/common/gpu_info.mojom
@@ -102,6 +102,7 @@
   bool sandboxed;
   int32 process_crash_count;
   bool in_process_gpu;
+  bool passthrough_cmd_decoder;
   CollectInfoResult basic_info_state;
   CollectInfoResult context_info_state;
   CollectInfoResult dx_diagnostics_info_state;
diff --git a/gpu/ipc/common/gpu_info_struct_traits.cc b/gpu/ipc/common/gpu_info_struct_traits.cc
index 6c2bd820..b64e110 100644
--- a/gpu/ipc/common/gpu_info_struct_traits.cc
+++ b/gpu/ipc/common/gpu_info_struct_traits.cc
@@ -224,6 +224,7 @@
   out->direct_rendering = data.direct_rendering();
   out->sandboxed = data.sandboxed();
   out->in_process_gpu = data.in_process_gpu();
+  out->passthrough_cmd_decoder = data.passthrough_cmd_decoder();
   out->process_crash_count = data.process_crash_count();
   out->jpeg_decode_accelerator_supported =
       data.jpeg_decode_accelerator_supported();
diff --git a/gpu/ipc/common/gpu_info_struct_traits.h b/gpu/ipc/common/gpu_info_struct_traits.h
index ea76e0e..6dfa12c 100644
--- a/gpu/ipc/common/gpu_info_struct_traits.h
+++ b/gpu/ipc/common/gpu_info_struct_traits.h
@@ -243,6 +243,10 @@
     return input.in_process_gpu;
   }
 
+  static bool passthrough_cmd_decoder(const gpu::GPUInfo& input) {
+    return input.passthrough_cmd_decoder;
+  }
+
   static gpu::CollectInfoResult basic_info_state(const gpu::GPUInfo& input) {
     return input.basic_info_state;
   }
diff --git a/gpu/ipc/common/gpu_param_traits_macros.h b/gpu/ipc/common/gpu_param_traits_macros.h
index e6409d2..64248121 100644
--- a/gpu/ipc/common/gpu_param_traits_macros.h
+++ b/gpu/ipc/common/gpu_param_traits_macros.h
@@ -91,6 +91,7 @@
   IPC_STRUCT_TRAITS_MEMBER(sandboxed)
   IPC_STRUCT_TRAITS_MEMBER(process_crash_count)
   IPC_STRUCT_TRAITS_MEMBER(in_process_gpu)
+  IPC_STRUCT_TRAITS_MEMBER(passthrough_cmd_decoder)
   IPC_STRUCT_TRAITS_MEMBER(basic_info_state)
   IPC_STRUCT_TRAITS_MEMBER(context_info_state)
 #if defined(OS_WIN)
diff --git a/gpu/ipc/common/struct_traits_unittest.cc b/gpu/ipc/common/struct_traits_unittest.cc
index 70ddbc0a..07931e8 100644
--- a/gpu/ipc/common/struct_traits_unittest.cc
+++ b/gpu/ipc/common/struct_traits_unittest.cc
@@ -148,6 +148,7 @@
   const bool sandboxed = true;
   const int process_crash_count = 0xdead;
   const bool in_process_gpu = true;
+  const bool passthrough_cmd_decoder = true;
   const gpu::CollectInfoResult basic_info_state =
       gpu::CollectInfoResult::kCollectInfoSuccess;
   const gpu::CollectInfoResult context_info_state =
@@ -197,6 +198,7 @@
   input.sandboxed = sandboxed;
   input.process_crash_count = process_crash_count;
   input.in_process_gpu = in_process_gpu;
+  input.passthrough_cmd_decoder = passthrough_cmd_decoder;
   input.basic_info_state = basic_info_state;
   input.context_info_state = context_info_state;
 #if defined(OS_WIN)
@@ -259,6 +261,7 @@
   EXPECT_EQ(sandboxed, output.sandboxed);
   EXPECT_EQ(process_crash_count, output.process_crash_count);
   EXPECT_EQ(in_process_gpu, output.in_process_gpu);
+  EXPECT_EQ(passthrough_cmd_decoder, output.passthrough_cmd_decoder);
   EXPECT_EQ(basic_info_state, output.basic_info_state);
   EXPECT_EQ(context_info_state, output.context_info_state);
 #if defined(OS_WIN)
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 1e912037..98620c4 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -158,6 +158,9 @@
 #endif
   gpu_info_.in_process_gpu = false;
 
+  gpu_info_.passthrough_cmd_decoder =
+      command_line.HasSwitch(switches::kUsePassthroughCmdDecoder);
+
   sandbox_helper_->PreSandboxStartup();
 
 #if defined(OS_LINUX)
diff --git a/ios/DEPS b/ios/DEPS
index 8f13cb93e..94f7f2c 100644
--- a/ios/DEPS
+++ b/ios/DEPS
@@ -3,11 +3,13 @@
   # directories in ios/ so we disallow all of them.
   "-ios",
 
-  # To avoid ODR violation, direct import of ios/third_party/ochamcrest
-  # is forbidden in ios/DEPS and code should instead use import as if
-  # OCHamcrest was in a framework (i.e. #import <OCHamcrest/OCHamcrest.h>).
+  # To avoid ODR violation, direct import of these libraries is forbidden in
+  # ios/DEPS and code should instead use import as if they were in a framework
+  # (i.e. #import <OCHamcrest/OCHamcrest.h>).
+  "-ios/third_party/earl_grey",
   "-ios/third_party/ochamcrest",
 
   # For unit tests.
+  "+ios/testing",
   "+third_party/ocmock",
 ]
diff --git a/ios/chrome/BUILD.gn b/ios/chrome/BUILD.gn
index 5044c7b..05ccaa8 100644
--- a/ios/chrome/BUILD.gn
+++ b/ios/chrome/BUILD.gn
@@ -50,6 +50,7 @@
     "//ios/chrome/browser/web_resource:unit_tests",
     "//ios/chrome/common:unit_tests",
     "//ios/chrome/test:unit_tests",
+    "//ios/chrome/test/base:unit_tests",
   ]
 
   assert_no_deps = ios_assert_no_deps
diff --git a/ios/chrome/DEPS b/ios/chrome/DEPS
index 6503a0e..0637ee1d 100644
--- a/ios/chrome/DEPS
+++ b/ios/chrome/DEPS
@@ -1,5 +1,20 @@
 include_rules = [
+  "+crypto",
+  "+net",
+  "+sql",
+  "+ui/base",
+  "+ui/gfx",
+
+  # Only parts of skia are compiled on iOS, so we explicitly list the
+  # files that can be included to avoid bringing in more code.
+  "+skia/ext/skia_utils_ios.h",
+  "+third_party/skia/include/core/SkBitmap.h",
+  "+third_party/skia/include/core/SkColor.h",
+  "+third_party/skia/include/core/SkGraphics.h",
+
   # The subdirectories in ios/chrome/ will manually allow their own include
   # directories in ios/chrome/ so we disallow all of them.
   "-ios/chrome",
+  "+ios/chrome/common",
+  "+ios/chrome/test",
 ]
diff --git a/ios/chrome/app/DEPS b/ios/chrome/app/DEPS
index 9074a78..a2e43b0 100644
--- a/ios/chrome/app/DEPS
+++ b/ios/chrome/app/DEPS
@@ -1,3 +1,25 @@
 include_rules = [
   "+ios/chrome/browser",
+
+  "+breakpad/src/client/ios",
+  "+components/bookmarks/browser",
+  "+components/bookmarks/test",
+  "+components/browser_sync",
+  "+components/browsing_data/core",
+  "+components/component_updater",
+  "+components/content_settings",
+  "+components/crash/core/common",
+  "+components/favicon/core",
+  "+components/favicon_base",
+  "+components/handoff",
+  "+components/history/core/browser",
+  "+components/metrics",
+  "+components/prefs",
+  "+components/reading_list/core",
+  "+components/signin/core/browser",
+  "+components/strings",
+  "+components/suggestions",
+  "+components/url_formatter",
+  "+components/web_resource",
+  "+mojo/edk/embedder/embedder.h",
 ]
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 4dd9065..eb62cf5 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -155,6 +155,7 @@
       <message name="IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SYNC_DESCRIPTION" desc="Description of the Chrome sync section on the sign-in confirmation screen. [Length: unlimited]">
         Your bookmarks, history, passwords, and other settings will be synced to your Google Account so you can use them on all your devices.
       </message>
+      <!-- "Chrome sync" is the Google Cloud Based service used for sync. Thus this string resource is set to "Chrome sync" even for Chromium builds. -->
       <message name="IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SYNC_TITLE" desc="Title of the Chrome sync section on the sign-in confirmation screen. [Length: 50em]">
         Chrome Sync
       </message>
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index c49fd45..6b5b985 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -15,7 +15,9 @@
   "+components/crash/core/common",
   "+components/dom_distiller/core",
   "+components/dom_distiller/ios",
+  "+components/error_page/common",
   "+components/favicon/core",
+  "+components/favicon/ios",
   "+components/favicon_base",
   "+components/flags_ui",
   "+components/gcm_driver",
@@ -51,6 +53,7 @@
   "+components/prefs",
   "+components/profile_metrics",
   "+components/proxy_config",
+  "+components/query_parser",
   "+components/rappor",
   "+components/reading_list",
   "+components/rlz",
@@ -64,11 +67,13 @@
   "+components/signin/core/common",
   "+components/signin/ios/browser",
   "+components/ssl_config",
+  "+components/ssl_errors",
   "+components/suggestions",
   "+components/sync",
   "+components/sync_sessions",
   "+components/sync_preferences",
   "+components/task_scheduler_util",
+  "+components/toolbar",
   "+components/translate/core",
   "+components/translate/ios",
   "+components/undo",
@@ -83,7 +88,6 @@
   "+components/webp_transcode",
   "+crypto",
   "+google_apis",
-  "+ios/chrome/common",
   "+ios/net",
   "+ios/public/provider/chrome",
   "+ios/public/provider/components",
@@ -101,12 +105,6 @@
   # For tests.
   "+ios/chrome/test",
   "+ios/public/test",
-  "+ios/testing",
-
-  # Only parts of skia are compiled on iOS, so we explicitly list the
-  # files that can be included to avoid bringing in more code.
-  "+skia/ext/skia_utils_ios.h",
-  "+third_party/skia/include/core/SkBitmap.h",
 
   # Strings and resources.
   "+components/grit",
diff --git a/ios/chrome/browser/interstitials/BUILD.gn b/ios/chrome/browser/interstitials/BUILD.gn
index dd91059..ef500ba 100644
--- a/ios/chrome/browser/interstitials/BUILD.gn
+++ b/ios/chrome/browser/interstitials/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("interstitials") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "ios_chrome_controller_client.h",
     "ios_chrome_controller_client.mm",
diff --git a/ios/chrome/browser/interstitials/ios_chrome_controller_client.mm b/ios/chrome/browser/interstitials/ios_chrome_controller_client.mm
index 9b6732f..6b5c5c67 100644
--- a/ios/chrome/browser/interstitials/ios_chrome_controller_client.mm
+++ b/ios/chrome/browser/interstitials/ios_chrome_controller_client.mm
@@ -13,6 +13,10 @@
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/web_state/web_state.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 IOSChromeControllerClient::IOSChromeControllerClient(
     web::WebState* web_state,
     std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper)
diff --git a/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm b/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm
index 878e8df..f575e16 100644
--- a/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm
+++ b/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm
@@ -12,6 +12,10 @@
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/web/public/web_state/web_state.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 IOSChromeMetricsHelper::IOSChromeMetricsHelper(
     web::WebState* web_state,
     const GURL& request_url,
diff --git a/ios/chrome/browser/interstitials/ios_security_interstitial_page.mm b/ios/chrome/browser/interstitials/ios_security_interstitial_page.mm
index 496e9e9..e438275c 100644
--- a/ios/chrome/browser/interstitials/ios_security_interstitial_page.mm
+++ b/ios/chrome/browser/interstitials/ios_security_interstitial_page.mm
@@ -17,6 +17,10 @@
 #include "ui/base/webui/jstemplate_builder.h"
 #include "ui/base/webui/web_ui_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 IOSSecurityInterstitialPage::IOSSecurityInterstitialPage(
     web::WebState* web_state,
     const GURL& request_url)
diff --git a/ios/chrome/browser/itunes_links/BUILD.gn b/ios/chrome/browser/itunes_links/BUILD.gn
index a4abdb08..16a4cc95 100644
--- a/ios/chrome/browser/itunes_links/BUILD.gn
+++ b/ios/chrome/browser/itunes_links/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("itunes_links") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "itunes_links_observer.h",
     "itunes_links_observer.mm",
diff --git a/ios/chrome/browser/itunes_links/itunes_links_observer.mm b/ios/chrome/browser/itunes_links/itunes_links_observer.mm
index c881fd69..3ba6a354 100644
--- a/ios/chrome/browser/itunes_links/itunes_links_observer.mm
+++ b/ios/chrome/browser/itunes_links/itunes_links_observer.mm
@@ -7,13 +7,16 @@
 #include <memory>
 
 #include "base/logging.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/storekit_launcher.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "ios/web/public/web_state/web_state.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface ITunesLinksObserver ()
 
 // If |URL| points to a product on itunes.apple.com, returns the product ID.
@@ -25,7 +28,7 @@
 @end
 
 @implementation ITunesLinksObserver {
-  base::WeakNSProtocol<id<StoreKitLauncher>> _storeKitLauncher;
+  __weak id<StoreKitLauncher> _storeKitLauncher;
   std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
 }
 
@@ -64,7 +67,7 @@
 }
 
 - (void)setStoreKitLauncher:(id<StoreKitLauncher>)storeKitLauncher {
-  _storeKitLauncher.reset(storeKitLauncher);
+  _storeKitLauncher = storeKitLauncher;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
index 5e70f81..a1958f5 100644
--- a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
+++ b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
@@ -20,6 +20,7 @@
 }
 
 source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "action_sheet_coordinator_unittest.mm",
diff --git a/ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator_unittest.mm b/ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator_unittest.mm
index ee05e1d..83eb94e 100644
--- a/ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator_unittest.mm
@@ -9,27 +9,29 @@
 #import "base/mac/foundation_util.h"
 #include "testing/platform_test.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 // Tests that if there is a popover, it uses the CGRect passed in init.
 TEST(ActionSheetCoordinatorTest, CGRectUsage) {
   // Setup.
-  UIWindow* window = [[[UIWindow alloc]
-      initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
+  UIWindow* window =
+      [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   [window makeKeyAndVisible];
-  UIViewController* viewController =
-      [[[UIViewController alloc] init] autorelease];
+  UIViewController* viewController = [[UIViewController alloc] init];
   [window setRootViewController:viewController];
 
-  UIView* view =
-      [[[UIView alloc] initWithFrame:viewController.view.bounds] autorelease];
+  UIView* view = [[UIView alloc] initWithFrame:viewController.view.bounds];
 
   [viewController.view addSubview:view];
   CGRect rect = CGRectMake(124, 432, 126, 63);
-  AlertCoordinator* alertCoordinator = [[[ActionSheetCoordinator alloc]
-      initWithBaseViewController:viewController
-                           title:@"title"
-                         message:nil
-                            rect:rect
-                            view:view] autorelease];
+  AlertCoordinator* alertCoordinator =
+      [[ActionSheetCoordinator alloc] initWithBaseViewController:viewController
+                                                           title:@"title"
+                                                         message:nil
+                                                            rect:rect
+                                                            view:view];
 
   // Action.
   [alertCoordinator start];
diff --git a/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm b/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm
index 8933d79..ba8ad71 100644
--- a/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/alert_coordinator_unittest.mm
@@ -7,23 +7,25 @@
 #import <UIKit/UIKit.h>
 
 #import "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "third_party/ocmock/gtest_support.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/strings/grit/ui_strings.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 #pragma mark - Fixture.
 
 // Fixture to test AlertCoordinator.
 class AlertCoordinatorTest : public PlatformTest {
  protected:
   AlertCoordinatorTest() {
-    window_.reset(
-        [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]);
+    window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
     [window_ makeKeyAndVisible];
-    view_controller_.reset([[UIViewController alloc] init]);
+    view_controller_ = [[UIViewController alloc] init];
     [window_ setRootViewController:view_controller_];
   }
 
@@ -38,17 +40,17 @@
   AlertCoordinator* getAlertCoordinator(UIViewController* viewController,
                                         NSString* title,
                                         NSString* message) {
-    alert_coordinator_.reset([[AlertCoordinator alloc]
-        initWithBaseViewController:viewController
-                             title:title
-                           message:message]);
+    alert_coordinator_ =
+        [[AlertCoordinator alloc] initWithBaseViewController:viewController
+                                                       title:title
+                                                     message:message];
     return alert_coordinator_;
   }
 
  private:
-  base::scoped_nsobject<AlertCoordinator> alert_coordinator_;
-  base::scoped_nsobject<UIWindow> window_;
-  base::scoped_nsobject<UIViewController> view_controller_;
+  AlertCoordinator* alert_coordinator_;
+  UIWindow* window_;
+  UIViewController* view_controller_;
 };
 
 #pragma mark - Tests.
@@ -80,10 +82,9 @@
 // visible view.
 TEST_F(AlertCoordinatorTest, ValidateIsNotVisible) {
   // Setup.
-  base::scoped_nsobject<UIWindow> window(
-      [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]);
-  base::scoped_nsobject<UIViewController> viewController(
-      [[UIViewController alloc] init]);
+  UIWindow* window =
+      [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+  UIViewController* viewController = [[UIViewController alloc] init];
   [window setRootViewController:viewController];
 
   AlertCoordinator* alertCoordinator = getAlertCoordinator(viewController);
@@ -161,8 +162,7 @@
     @"testCancel" : @(UIAlertActionStyleCancel),
   };
 
-  base::scoped_nsobject<NSMutableDictionary> remainingActions(
-      [actions mutableCopy]);
+  NSMutableDictionary* remainingActions = [actions mutableCopy];
 
   // Action.
   for (id key in actions) {
diff --git a/ios/chrome/browser/ui/alert_coordinator/input_alert_coordinator_unittest.mm b/ios/chrome/browser/ui/alert_coordinator/input_alert_coordinator_unittest.mm
index 4aebd39..5cbf932 100644
--- a/ios/chrome/browser/ui/alert_coordinator/input_alert_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/input_alert_coordinator_unittest.mm
@@ -8,14 +8,17 @@
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "third_party/ocmock/gtest_support.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 TEST(InputAlertCoordinatorTest, AddTextField) {
   // Setup.
-  UIViewController* viewController =
-      [[[UIViewController alloc] init] autorelease];
-  InputAlertCoordinator* alertCoordinator = [[[InputAlertCoordinator alloc]
-      initWithBaseViewController:viewController
-                           title:@"Test"
-                         message:nil] autorelease];
+  UIViewController* viewController = [[UIViewController alloc] init];
+  InputAlertCoordinator* alertCoordinator =
+      [[InputAlertCoordinator alloc] initWithBaseViewController:viewController
+                                                          title:@"Test"
+                                                        message:nil];
 
   void (^emptyHandler)(UITextField* textField) = ^(UITextField* textField) {
   };
@@ -32,12 +35,11 @@
 
 TEST(InputAlertCoordinatorTest, GetTextFields) {
   // Setup.
-  UIViewController* viewController =
-      [[[UIViewController alloc] init] autorelease];
-  InputAlertCoordinator* alertCoordinator = [[[InputAlertCoordinator alloc]
-      initWithBaseViewController:viewController
-                           title:@"Test"
-                         message:nil] autorelease];
+  UIViewController* viewController = [[UIViewController alloc] init];
+  InputAlertCoordinator* alertCoordinator =
+      [[InputAlertCoordinator alloc] initWithBaseViewController:viewController
+                                                          title:@"Test"
+                                                        message:nil];
 
   NSArray<UITextField*>* array = [NSArray array];
   id alert =
diff --git a/ios/chrome/test/base/BUILD.gn b/ios/chrome/test/base/BUILD.gn
new file mode 100644
index 0000000..9c7dffc
--- /dev/null
+++ b/ios/chrome/test/base/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("base") {
+  testonly = true
+  sources = [
+    "scoped_block_swizzler.h",
+    "scoped_block_swizzler.mm",
+  ]
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "scoped_block_swizzler_unittest.mm",
+  ]
+  deps = [
+    ":base",
+    "//base",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/test/base/scoped_block_swizzler.h b/ios/chrome/test/base/scoped_block_swizzler.h
new file mode 100644
index 0000000..ecfea71
--- /dev/null
+++ b/ios/chrome/test/base/scoped_block_swizzler.h
@@ -0,0 +1,40 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#ifndef IOS_CHROME_TEST_BASE_SCOPED_BLOCK_SWIZZLER_H_
+#define IOS_CHROME_TEST_BASE_SCOPED_BLOCK_SWIZZLER_H_
+
+#include <objc/runtime.h>
+
+#include "base/macros.h"
+
+// Helper class that replaces a method implementation with a given block.
+// ScopedBlockSwizzler automatically swizzles when it is constructed and
+// reinstalls the original method implementation when it goes out of scope.
+class ScopedBlockSwizzler {
+ public:
+  // Constructs a new ScopedBlockSwizzler object and replaces the implementation
+  // of |selector| on the |target| class with the given |block|.
+  // ScopedBlockSwizzler first tries to swizzle a class method; if one is not
+  // found, it tries to swizzle an instance method.  It is an error to pass a
+  // |selector| that does not exist on the |target| class.
+  ScopedBlockSwizzler(Class target, SEL selector, id block);
+
+  // Destroys the ScopedBlockSwizzler object, removing the swizzled method and
+  // reinstalling the original method implementation.
+  virtual ~ScopedBlockSwizzler();
+
+ private:
+  // The method that is to be swizzled.  Can be either a class method or an
+  // instance method.
+  Method method_;
+
+  // The original implementation of the swizzled method, saved so that it can be
+  // reinstalled when this object goes out of scope.
+  IMP original_imp_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedBlockSwizzler);
+};
+
+#endif  // IOS_CHROME_TEST_BASE_SCOPED_BLOCK_SWIZZLER_H_
diff --git a/ios/chrome/test/base/scoped_block_swizzler.mm b/ios/chrome/test/base/scoped_block_swizzler.mm
new file mode 100644
index 0000000..312de59
--- /dev/null
+++ b/ios/chrome/test/base/scoped_block_swizzler.mm
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/test/base/scoped_block_swizzler.h"
+
+#include "base/logging.h"
+
+ScopedBlockSwizzler::ScopedBlockSwizzler(Class target, SEL selector, id block) {
+  method_ = class_getInstanceMethod(target, selector);
+  if (!method_) {
+    // Try swizzling a class method instead.
+    method_ = class_getClassMethod(target, selector);
+  }
+  DCHECK(method_);
+
+  IMP block_imp = imp_implementationWithBlock(block);
+  original_imp_ = method_setImplementation(method_, block_imp);
+}
+
+ScopedBlockSwizzler::~ScopedBlockSwizzler() {
+  IMP block_imp = method_setImplementation(method_, original_imp_);
+  DCHECK(imp_removeBlock(block_imp));
+}
diff --git a/ios/chrome/test/base/scoped_block_swizzler_unittest.mm b/ios/chrome/test/base/scoped_block_swizzler_unittest.mm
new file mode 100644
index 0000000..2e390a4
--- /dev/null
+++ b/ios/chrome/test/base/scoped_block_swizzler_unittest.mm
@@ -0,0 +1,103 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/foundation_util.h"
+#import "base/mac/scoped_nsobject.h"
+#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+// Class containing two methods that will be swizzled by the unittests.
+@interface ScopedBlockSwizzlerTestClass : NSObject
+
+// An NSString property that will be accessed by one of the swizzled methods.
+@property(nonatomic, copy) NSString* value;
+
++ (NSString*)classMethodToSwizzle;
+- (NSString*)instanceMethodToSwizzle;
+@end
+
+namespace {
+
+NSString* const kOriginalClassValue = @"Bar";
+NSString* const kSwizzledClassValue = @"Foo";
+NSString* const kOriginalInstanceValue = @"Bizz";
+NSString* const kSwizzledInstanceValue = @"Buzz";
+
+// Tests that swizzling a class method works properly.
+TEST(ScopedBlockSwizzlerTest, SwizzlingClassMethods) {
+  EXPECT_NSEQ(kOriginalClassValue,
+              [ScopedBlockSwizzlerTestClass classMethodToSwizzle]);
+
+  {
+    id block = ^NSString*(id self) { return kSwizzledClassValue; };
+    ScopedBlockSwizzler swizzler([ScopedBlockSwizzlerTestClass class],
+                                 @selector(classMethodToSwizzle), block);
+    EXPECT_NSEQ(kSwizzledClassValue,
+                [ScopedBlockSwizzlerTestClass classMethodToSwizzle]);
+  }
+
+  EXPECT_NSEQ(kOriginalClassValue,
+              [ScopedBlockSwizzlerTestClass classMethodToSwizzle]);
+}
+
+// Tests that swizzling an instance method works properly.
+TEST(ScopedBlockSwizzlerTest, SwizzlingInstanceMethod) {
+  base::scoped_nsobject<ScopedBlockSwizzlerTestClass> target(
+      [[ScopedBlockSwizzlerTestClass alloc] init]);
+  target.get().value = kSwizzledInstanceValue;
+
+  EXPECT_NSEQ(kOriginalInstanceValue, [target instanceMethodToSwizzle]);
+  EXPECT_FALSE([[target instanceMethodToSwizzle]
+      isEqualToString:kSwizzledInstanceValue]);
+
+  {
+    id block = ^NSString*(id self) {
+      return base::mac::ObjCCastStrict<ScopedBlockSwizzlerTestClass>(self)
+          .value;
+    };
+    ScopedBlockSwizzler swizzler([ScopedBlockSwizzlerTestClass class],
+                                 @selector(instanceMethodToSwizzle), block);
+    EXPECT_NSEQ(kSwizzledInstanceValue, [target instanceMethodToSwizzle]);
+  }
+
+  EXPECT_NSEQ(kOriginalInstanceValue, [target instanceMethodToSwizzle]);
+}
+
+// Tests that calling |ScopedBlockSwizzler::reset()| properly unswizzles the
+// method.
+TEST(ScopedBlockSwizzlerTest, TestReset) {
+  EXPECT_NSEQ(kOriginalClassValue,
+              [ScopedBlockSwizzlerTestClass classMethodToSwizzle]);
+
+  id block = ^NSString*(id self) { return kSwizzledClassValue; };
+  std::unique_ptr<ScopedBlockSwizzler> swizzler(
+      new ScopedBlockSwizzler([ScopedBlockSwizzlerTestClass class],
+                              @selector(classMethodToSwizzle), block));
+  EXPECT_NSEQ(kSwizzledClassValue,
+              [ScopedBlockSwizzlerTestClass classMethodToSwizzle]);
+
+  swizzler.reset();
+  EXPECT_NSEQ(kOriginalClassValue,
+              [ScopedBlockSwizzlerTestClass classMethodToSwizzle]);
+}
+
+}  // namespace
+
+#pragma mark - ScopedBlockSwizzlerTestClass
+
+@implementation ScopedBlockSwizzlerTestClass
+
+@synthesize value = _value;
+
++ (NSString*)classMethodToSwizzle {
+  return kOriginalClassValue;
+}
+
+- (NSString*)instanceMethodToSwizzle {
+  return kOriginalInstanceValue;
+}
+
+@end
diff --git a/ios/chrome/test/data/OWNERS b/ios/chrome/test/data/OWNERS
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/ios/chrome/test/data/OWNERS
@@ -0,0 +1 @@
+*
diff --git a/ios/chrome/test/data/favicon/test_favicon.png b/ios/chrome/test/data/favicon/test_favicon.png
new file mode 100644
index 0000000..e9100dae
--- /dev/null
+++ b/ios/chrome/test/data/favicon/test_favicon.png
Binary files differ
diff --git a/ios/chrome/test/data/omnibox/selected_ranges.txt b/ios/chrome/test/data/omnibox/selected_ranges.txt
new file mode 100644
index 0000000..8e918f0
--- /dev/null
+++ b/ios/chrome/test/data/omnibox/selected_ranges.txt
@@ -0,0 +1,13 @@
+This is some example text
+이것은 몇 가지 예를 들어 텍스트입니다
+यह कुछ उदाहरण पाठ है
+这是一些示例文本。
+這是一些示例文本
+הנה טקסט ירושלים.
+これは、いくつかの例では、テキストです
+
+I really like selecting ranges
+私は実際に選択範囲を好き
+我真的很喜欢选择范围
+정말 선택 범위를 좋아
+אני באמת אוהב את טווחי בחירה
diff --git a/ios/chrome/test/data/sessions/corrupted.plist b/ios/chrome/test/data/sessions/corrupted.plist
new file mode 100644
index 0000000..3f6938b
--- /dev/null
+++ b/ios/chrome/test/data/sessions/corrupted.plist
Binary files differ
diff --git a/ios/chrome/test/data/testbadpass.pkpass b/ios/chrome/test/data/testbadpass.pkpass
new file mode 100644
index 0000000..3f59060b
--- /dev/null
+++ b/ios/chrome/test/data/testbadpass.pkpass
@@ -0,0 +1 @@
+This isn't even a zip file, it's so bad!
diff --git a/ios/chrome/test/data/testpass.pkpass b/ios/chrome/test/data/testpass.pkpass
new file mode 100644
index 0000000..098e4cd
--- /dev/null
+++ b/ios/chrome/test/data/testpass.pkpass
Binary files differ
diff --git a/ios/chrome/test/data/webmock/somedomain.com/index.html b/ios/chrome/test/data/webmock/somedomain.com/index.html
new file mode 100644
index 0000000..c974b66
--- /dev/null
+++ b/ios/chrome/test/data/webmock/somedomain.com/index.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+	<title>Some domain homepage</title>
+</head>
+<body>
+    <h1>Some domain's homepage - Testing http protocol website interception</h1>
+<body>
+</html>
\ No newline at end of file
diff --git a/ios/consumer/base/debugger.mm b/ios/consumer/base/debugger.mm
new file mode 100644
index 0000000..ff2e2b3
--- /dev/null
+++ b/ios/consumer/base/debugger.mm
@@ -0,0 +1,14 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/debugger.h"
+#include "ios/public/consumer/base/debugger.h"
+
+namespace ios {
+
+bool BeingDebugged() {
+  return base::debug::BeingDebugged();
+}
+
+}  // namespace ios
diff --git a/ios/public/consumer/base/BUILD.gn b/ios/public/consumer/base/BUILD.gn
index cc242cf..eb83ba2 100644
--- a/ios/public/consumer/base/BUILD.gn
+++ b/ios/public/consumer/base/BUILD.gn
@@ -3,9 +3,8 @@
 # found in the LICENSE file.
 
 source_set("base") {
-  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "//ios/consumer/base/debugger.cc",
+    "//ios/consumer/base/debugger.mm",
     "//ios/public/consumer/base/debugger.h",
   ]
   deps = [
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index f2abbbc..cebb17f 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -246,6 +246,7 @@
     "//components/url_formatter",
     "//ios/net",
     "//ios/third_party/blink:html_tokenizer",
+    "//ios/web/public/image_fetcher",
     "//mojo/public/cpp/system",
     "//mojo/public/js",
     "//net",
diff --git a/ios/web/public/web_state/ui/crw_web_delegate.h b/ios/web/public/web_state/ui/crw_web_delegate.h
index 2a7b2b0..71befcc9 100644
--- a/ios/web/public/web_state/ui/crw_web_delegate.h
+++ b/ios/web/public/web_state/ui/crw_web_delegate.h
@@ -103,11 +103,6 @@
     onFormResubmissionForRequest:(NSURLRequest*)request
                    continueBlock:(ProceduralBlock)continueBlock
                      cancelBlock:(ProceduralBlock)cancelBlock;
-// Returns the unique id of the download request and starts downloading the
-// image at |url| without sending the cookies. Invokes |callback| on completion.
-- (int)downloadImageAtUrl:(const GURL&)url
-    maxBitmapSize:(uint32_t)maxBitmapSize
-         callback:(const web::WebState::ImageDownloadCallback&)callback;
 
 // ---------------------------------------------------------------------
 // TODO(rohitrao): Eliminate as many of the following delegate methods as
diff --git a/ios/web/web_state/DEPS b/ios/web/web_state/DEPS
new file mode 100644
index 0000000..eaa61126
--- /dev/null
+++ b/ios/web/web_state/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+skia/ext/skia_utils_ios.h",
+  "+third_party/skia/include/core/SkBitmap.h",
+]
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index d75eb41..5f01b93 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -43,6 +43,7 @@
 struct FaviconURL;
 struct LoadCommittedDetails;
 class NavigationManager;
+class ImageDataFetcher;
 class WebInterstitialImpl;
 class WebStateDelegate;
 class WebStateFacadeDelegate;
@@ -369,6 +370,9 @@
   // Mojo interface registry for this WebState.
   std::unique_ptr<service_manager::InterfaceRegistry> mojo_interface_registry_;
 
+  // Image Fetcher used to images.
+  std::unique_ptr<ImageDataFetcher> image_fetcher_;
+
   DISALLOW_COPY_AND_ASSIGN(WebStateImpl);
 };
 
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 2be7234..9d2dfa4 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -16,6 +16,7 @@
 #include "ios/web/navigation/navigation_item_impl.h"
 #include "ios/web/net/request_group_util.h"
 #include "ios/web/public/browser_state.h"
+#import "ios/web/public/image_fetcher/image_data_fetcher.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
 #include "ios/web/public/navigation_item.h"
 #include "ios/web/public/url_util.h"
@@ -33,7 +34,10 @@
 #import "ios/web/webui/web_ui_ios_controller_factory_registry.h"
 #import "ios/web/webui/web_ui_ios_impl.h"
 #include "net/http/http_response_headers.h"
+#include "net/url_request/url_fetcher.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
+#include "skia/ext/skia_utils_ios.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 
 namespace web {
 
@@ -63,6 +67,8 @@
       weak_factory_(this) {
   GlobalWebStateEventTracker::GetInstance()->OnWebStateCreated(this);
   web_controller_.reset([[CRWWebController alloc] initWithWebState:this]);
+  image_fetcher_.reset(new ImageDataFetcher(web::WebThread::GetBlockingPool()));
+  image_fetcher_->SetRequestContextGetter(browser_state->GetRequestContext());
 }
 
 WebStateImpl::~WebStateImpl() {
@@ -538,9 +544,28 @@
   // cookies or not. Currently, only downloads without cookies are supported.
   // |bypass_cache| is ignored since the downloads never go through a cache.
   DCHECK(is_favicon);
-  return [[web_controller_ delegate] downloadImageAtUrl:url
-                                          maxBitmapSize:max_bitmap_size
-                                              callback:callback];
+
+  static int downloaded_image_count = 0;
+  int local_download_id = ++downloaded_image_count;
+  __block web::WebState::ImageDownloadCallback local_image_callback = callback;
+  __block GURL local_url(url);
+  ImageFetchedCallback local_callback =
+      ^(const GURL&, const int response_code, NSData* data) {
+        std::vector<SkBitmap> frames;
+        std::vector<gfx::Size> sizes;
+        if (data) {
+          frames = skia::ImageDataToSkBitmaps(data);
+          for (auto& frame : frames) {
+            sizes.push_back(gfx::Size(frame.width(), frame.height()));
+          }
+        }
+        if (response_code != net::URLFetcher::RESPONSE_CODE_INVALID) {
+          local_image_callback.Run(local_download_id, response_code, local_url,
+                                   frames, sizes);
+        }
+      };
+  image_fetcher_->StartDownload(url, local_callback);
+  return downloaded_image_count;
 }
 
 service_manager::InterfaceRegistry* WebStateImpl::GetMojoInterfaceRegistry() {
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index eb21168..eda4275 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -186,6 +186,10 @@
 // Replaces WPMA by the MediaPlayerRenderer for HLS and fallback playback.
 const base::Feature kAndroidMediaPlayerRenderer{
     "android-media-player-renderer", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Lock the screen orientation when a video goes fullscreen.
+const base::Feature kVideoFullscreenOrientationLock{
+    "VideoFullscreenOrientationLock", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
 }  // namespace media
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 9dd347b..781e927 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -94,6 +94,7 @@
 
 #if defined(OS_ANDROID)
 MEDIA_EXPORT extern const base::Feature kAndroidMediaPlayerRenderer;
+MEDIA_EXPORT extern const base::Feature kVideoFullscreenOrientationLock;
 #endif  // defined(OS_ANDROID)
 }  // namespace media
 
diff --git a/net/http/http_security_headers_unittest.cc b/net/http/http_security_headers_unittest.cc
index 2d816f14..9203781 100644
--- a/net/http/http_security_headers_unittest.cc
+++ b/net/http/http_security_headers_unittest.cc
@@ -789,9 +789,9 @@
   std::string failure_log;
 
   // Damage the hashes to cause a pin validation failure.
-  new_static_pkp_state2.spki_hashes[0].data()[0] ^= 0x80;
-  new_static_pkp_state2.spki_hashes[1].data()[0] ^= 0x80;
-  new_static_pkp_state2.spki_hashes[2].data()[0] ^= 0x80;
+  for (size_t i = 0; i < new_static_pkp_state2.spki_hashes.size(); i++) {
+    new_static_pkp_state2.spki_hashes[i].data()[0] ^= 0x80;
+  }
 
   const bool is_issued_by_known_root = true;
   HostPortPair domain_port(domain, 443);
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h
index d9bdee5..ddb0ada 100644
--- a/net/http/transport_security_state_static.h
+++ b/net/http/transport_security_state_static.h
@@ -604,6 +604,7 @@
     kSPKIHash_GoogleBackup2048,
     kSPKIHash_GoogleG2,
     kSPKIHash_GeoTrustGlobal,
+    kSPKIHash_GlobalSignRootCA_R2,
     NULL,
 };
 static const char kGoogleReportURI[] = "http://clients3.google.com/cert_upload_json";
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 8805be8..367ef139 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -60,7 +60,8 @@
       "static_spki_hashes": [
         "GoogleBackup2048",
         "GoogleG2",
-        "GeoTrustGlobal"
+        "GeoTrustGlobal",
+        "GlobalSignRootCA_R2"
       ],
       "report_uri": "http://clients3.google.com/cert_upload_json"
     },
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 28f8a1ee..7520a48 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -1485,12 +1485,25 @@
                                    total_time);
       }
     }
+
+    UMA_HISTOGRAM_CUSTOM_COUNTS("Net.HttpJob.PrefilterBytesRead",
+                                prefilter_bytes_read(), 1, 50000000, 50);
     if (response_info_->was_cached) {
       UMA_HISTOGRAM_TIMES("Net.HttpJob.TotalTimeCached", total_time);
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Net.HttpJob.PrefilterBytesRead.Cache",
+                                  prefilter_bytes_read(), 1, 50000000, 50);
+
       if (response_info_->unused_since_prefetch)
         UMA_HISTOGRAM_COUNTS("Net.Prefetch.HitBytes", prefilter_bytes_read());
     } else {
       UMA_HISTOGRAM_TIMES("Net.HttpJob.TotalTimeNotCached", total_time);
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Net.HttpJob.PrefilterBytesRead.Net",
+                                  prefilter_bytes_read(), 1, 50000000, 50);
+
+      if (request_info_.load_flags & LOAD_PREFETCH) {
+        UMA_HISTOGRAM_COUNTS("Net.Prefetch.PrefilterBytesReadFromNetwork",
+                             prefilter_bytes_read());
+      }
       if (is_https_google) {
         if (used_quic) {
           UMA_HISTOGRAM_MEDIUM_TIMES(
@@ -1503,10 +1516,6 @@
     }
   }
 
-  if (request_info_.load_flags & LOAD_PREFETCH && !request_->was_cached())
-    UMA_HISTOGRAM_COUNTS("Net.Prefetch.PrefilterBytesReadFromNetwork",
-                         prefilter_bytes_read());
-
   start_time_ = base::TimeTicks();
 }
 
diff --git a/services/navigation/navigation.h b/services/navigation/navigation.h
index a48a91b6..af242e3 100644
--- a/services/navigation/navigation.h
+++ b/services/navigation/navigation.h
@@ -15,10 +15,6 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 
-namespace content {
-class BrowserContext;
-}
-
 namespace navigation {
 
 std::unique_ptr<service_manager::Service> CreateNavigationService();
diff --git a/services/preferences/public/cpp/pref_observer_store.h b/services/preferences/public/cpp/pref_observer_store.h
index 16f9fe5d..1e408e1 100644
--- a/services/preferences/public/cpp/pref_observer_store.h
+++ b/services/preferences/public/cpp/pref_observer_store.h
@@ -13,10 +13,6 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/preferences/public/interfaces/preferences.mojom.h"
 
-namespace shell {
-class Connector;
-}
-
 class PrefObserverStoreTest;
 
 // An implementation of PrefStore which uses prefs::mojom::PreferenceManager as
diff --git a/services/ui/gpu/gpu_service_internal.h b/services/ui/gpu/gpu_service_internal.h
index f6b3c60..8fd4603 100644
--- a/services/ui/gpu/gpu_service_internal.h
+++ b/services/ui/gpu/gpu_service_internal.h
@@ -25,7 +25,6 @@
 #include "ui/gfx/native_widget_types.h"
 
 namespace gpu {
-class GpuChannelHost;
 class GpuMemoryBufferFactory;
 class GpuWatchdogThread;
 class SyncPointManager;
diff --git a/services/ui/public/cpp/context_provider.h b/services/ui/public/cpp/context_provider.h
index c4949635..348d7cd 100644
--- a/services/ui/public/cpp/context_provider.h
+++ b/services/ui/public/cpp/context_provider.h
@@ -17,10 +17,6 @@
 class GpuChannelHost;
 }
 
-namespace service_manager {
-class Connector;
-}
-
 namespace ui {
 
 class GLES2Context;
diff --git a/services/ui/public/cpp/gpu/mojo_gpu_memory_buffer_manager.h b/services/ui/public/cpp/gpu/mojo_gpu_memory_buffer_manager.h
index 217783f..d1f5e339 100644
--- a/services/ui/public/cpp/gpu/mojo_gpu_memory_buffer_manager.h
+++ b/services/ui/public/cpp/gpu/mojo_gpu_memory_buffer_manager.h
@@ -19,10 +19,6 @@
 class WaitableEvent;
 }
 
-namespace service_manager {
-class Connector;
-}
-
 namespace ui {
 
 namespace mojom {
diff --git a/services/ui/public/cpp/tests/test_window_tree_client_setup.h b/services/ui/public/cpp/tests/test_window_tree_client_setup.h
index e7146f9..c2527aa 100644
--- a/services/ui/public/cpp/tests/test_window_tree_client_setup.h
+++ b/services/ui/public/cpp/tests/test_window_tree_client_setup.h
@@ -9,10 +9,6 @@
 
 #include "base/macros.h"
 
-namespace display {
-class Display;
-}
-
 namespace ui {
 
 class TestWindowTree;
diff --git a/services/ui/public/cpp/window.h b/services/ui/public/cpp/window.h
index d2d6638..015540c 100644
--- a/services/ui/public/cpp/window.h
+++ b/services/ui/public/cpp/window.h
@@ -21,10 +21,6 @@
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 
-namespace gfx {
-class Size;
-}
-
 namespace gpu {
 class GpuMemoryBufferManager;
 }
@@ -32,11 +28,9 @@
 namespace ui {
 
 class InputEventHandler;
-class ServiceProviderImpl;
 class SurfaceIdHandler;
 class WindowCompositorFrameSinkBinding;
 class WindowObserver;
-class WindowSurface;
 class WindowDropTarget;
 class WindowTreeClient;
 class WindowTreeClientPrivate;
diff --git a/services/ui/public/cpp/window_compositor_frame_sink.cc b/services/ui/public/cpp/window_compositor_frame_sink.cc
index 4c2e86352..2d89373 100644
--- a/services/ui/public/cpp/window_compositor_frame_sink.cc
+++ b/services/ui/public/cpp/window_compositor_frame_sink.cc
@@ -115,7 +115,6 @@
 
 void WindowCompositorFrameSink::WillDrawSurface() {
   // TODO(fsamuel, staraz): Implement this.
-  NOTIMPLEMENTED();
 }
 
 void WindowCompositorFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) {
diff --git a/services/ui/public/cpp/window_compositor_frame_sink.h b/services/ui/public/cpp/window_compositor_frame_sink.h
index 8eac511a..1ea8e8c3 100644
--- a/services/ui/public/cpp/window_compositor_frame_sink.h
+++ b/services/ui/public/cpp/window_compositor_frame_sink.h
@@ -14,10 +14,6 @@
 #include "cc/surfaces/surface_id_allocator.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
-namespace gpu {
-class GpuChannelHost;
-}
-
 namespace ui {
 
 class WindowCompositorFrameSinkBinding;
diff --git a/services/ui/public/cpp/window_tree_client_delegate.h b/services/ui/public/cpp/window_tree_client_delegate.h
index 3664ac28..6f52db0 100644
--- a/services/ui/public/cpp/window_tree_client_delegate.h
+++ b/services/ui/public/cpp/window_tree_client_delegate.h
@@ -11,10 +11,6 @@
 #include "services/ui/public/interfaces/window_tree.mojom.h"
 
 namespace ui {
-class Event;
-}
-
-namespace ui {
 
 class Window;
 class WindowTreeClient;
diff --git a/services/ui/service.h b/services/ui/service.h
index 30606d8..e8b250b 100644
--- a/services/ui/service.h
+++ b/services/ui/service.h
@@ -44,10 +44,6 @@
 class ScreenManager;
 }
 
-namespace gfx {
-class Rect;
-}
-
 namespace service_manager {
 class Connector;
 }
@@ -57,7 +53,6 @@
 class PlatformEventSource;
 
 namespace ws {
-class ForwardingWindowManager;
 class WindowServer;
 }
 
diff --git a/services/ui/ws/event_dispatcher.h b/services/ui/ws/event_dispatcher.h
index bcb7053f..67572d0d 100644
--- a/services/ui/ws/event_dispatcher.h
+++ b/services/ui/ws/event_dispatcher.h
@@ -33,7 +33,6 @@
 class DragTargetConnection;
 class EventDispatcherDelegate;
 class ServerWindow;
-class WindowTree;
 
 namespace test {
 class EventDispatcherTestApi;
diff --git a/services/ui/ws/focus_controller.h b/services/ui/ws/focus_controller.h
index 190f60e..7cf76e6c 100644
--- a/services/ui/ws/focus_controller.h
+++ b/services/ui/ws/focus_controller.h
@@ -20,7 +20,6 @@
 class FocusControllerObserver;
 class ServerWindow;
 class ServerWindowDrawnTracker;
-struct WindowId;
 
 // Describes the source of the change.
 enum class FocusControllerChangeSource {
diff --git a/services/ui/ws/frame_generator.cc b/services/ui/ws/frame_generator.cc
index fab84683..0c869ea 100644
--- a/services/ui/ws/frame_generator.cc
+++ b/services/ui/ws/frame_generator.cc
@@ -127,7 +127,6 @@
 
 void FrameGenerator::WillDrawSurface() {
   // TODO(fsamuel, staraz): Implement this.
-  NOTIMPLEMENTED();
 }
 
 cc::CompositorFrame FrameGenerator::GenerateCompositorFrame(
diff --git a/services/ui/ws/frame_generator.h b/services/ui/ws/frame_generator.h
index e2272f4..a9e822e4 100644
--- a/services/ui/ws/frame_generator.h
+++ b/services/ui/ws/frame_generator.h
@@ -27,10 +27,6 @@
 class SurfaceId;
 }
 
-namespace gpu {
-class GpuChannelHost;
-}
-
 namespace ui {
 
 class DisplayCompositor;
diff --git a/services/ui/ws/gpu_service_proxy_delegate.h b/services/ui/ws/gpu_service_proxy_delegate.h
index 6f18646..c7b5a94 100644
--- a/services/ui/ws/gpu_service_proxy_delegate.h
+++ b/services/ui/ws/gpu_service_proxy_delegate.h
@@ -7,10 +7,6 @@
 
 #include "base/memory/ref_counted.h"
 
-namespace gpu {
-class GpuChannelHost;
-}
-
 namespace ui {
 namespace ws {
 
diff --git a/services/ui/ws/platform_display.h b/services/ui/ws/platform_display.h
index 9b64222e..96b1ca9 100644
--- a/services/ui/ws/platform_display.h
+++ b/services/ui/ws/platform_display.h
@@ -18,10 +18,6 @@
 class Rect;
 }
 
-namespace gpu {
-class GpuChannelHost;
-}
-
 namespace ui {
 
 struct TextInputState;
diff --git a/services/ui/ws/platform_display_delegate.h b/services/ui/ws/platform_display_delegate.h
index e6eb597..98b944e2 100644
--- a/services/ui/ws/platform_display_delegate.h
+++ b/services/ui/ws/platform_display_delegate.h
@@ -5,10 +5,6 @@
 #ifndef SERVICES_UI_WS_PLATFORM_DISPLAY_DELEGATE_H_
 #define SERVICES_UI_WS_PLATFORM_DISPLAY_DELEGATE_H_
 
-namespace gfx {
-class Size;
-}
-
 namespace ui {
 
 class Event;
diff --git a/services/ui/ws/server_window.h b/services/ui/ws/server_window.h
index dd508642..6adfb97 100644
--- a/services/ui/ws/server_window.h
+++ b/services/ui/ws/server_window.h
@@ -23,10 +23,6 @@
 #include "ui/gfx/transform.h"
 #include "ui/platform_window/text_input_state.h"
 
-namespace gpu {
-class GpuMemoryBufferManager;
-}
-
 namespace ui {
 namespace ws {
 
diff --git a/services/ui/ws/server_window_compositor_frame_sink_manager.h b/services/ui/ws/server_window_compositor_frame_sink_manager.h
index d2db13e..8461ebb 100644
--- a/services/ui/ws/server_window_compositor_frame_sink_manager.h
+++ b/services/ui/ws/server_window_compositor_frame_sink_manager.h
@@ -14,10 +14,6 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
 
-namespace gpu {
-class GpuMemoryBufferManager;
-}
-
 namespace ui {
 namespace ws {
 
diff --git a/services/ui/ws/server_window_delegate.h b/services/ui/ws/server_window_delegate.h
index 08db68f..26784fa 100644
--- a/services/ui/ws/server_window_delegate.h
+++ b/services/ui/ws/server_window_delegate.h
@@ -21,9 +21,7 @@
 
 namespace ws {
 
-struct ClientWindowId;
 class ServerWindow;
-struct WindowId;
 
 class ServerWindowDelegate {
  public:
diff --git a/services/ui/ws/test_server_window_delegate.h b/services/ui/ws/test_server_window_delegate.h
index 8d63b62..4c8d851 100644
--- a/services/ui/ws/test_server_window_delegate.h
+++ b/services/ui/ws/test_server_window_delegate.h
@@ -18,8 +18,6 @@
 
 namespace ws {
 
-struct WindowId;
-
 class TestServerWindowDelegate : public ServerWindowDelegate {
  public:
   TestServerWindowDelegate();
diff --git a/services/ui/ws/user_display_manager_delegate.h b/services/ui/ws/user_display_manager_delegate.h
index 0bbeb78..671d0b7 100644
--- a/services/ui/ws/user_display_manager_delegate.h
+++ b/services/ui/ws/user_display_manager_delegate.h
@@ -11,9 +11,6 @@
 namespace ui {
 namespace ws {
 
-class Display;
-class WindowManagerState;
-
 class UserDisplayManagerDelegate {
  public:
   // Gets the frame decorations for the specified user. Returns true if the
diff --git a/services/ui/ws/window_manager_display_root.h b/services/ui/ws/window_manager_display_root.h
index 279982fe..2ba39a0 100644
--- a/services/ui/ws/window_manager_display_root.h
+++ b/services/ui/ws/window_manager_display_root.h
@@ -19,10 +19,6 @@
 class WindowManagerState;
 class WindowServer;
 
-namespace test {
-class WindowManagerDisplayRootTestApi;
-}
-
 // Owns the root window of a window manager for one display. Each window manager
 // has one WindowManagerDisplayRoot for each Display. The root window is
 // parented to the root of a Display.
diff --git a/services/ui/ws/window_manager_window_tree_factory.h b/services/ui/ws/window_manager_window_tree_factory.h
index 252fdf5..df5c4b0e 100644
--- a/services/ui/ws/window_manager_window_tree_factory.h
+++ b/services/ui/ws/window_manager_window_tree_factory.h
@@ -14,7 +14,6 @@
 namespace ui {
 namespace ws {
 
-class ServerWindow;
 class WindowManagerWindowTreeFactorySet;
 class WindowServer;
 class WindowTree;
diff --git a/services/ui/ws/window_server_delegate.h b/services/ui/ws/window_server_delegate.h
index 802dbd7..717c512 100644
--- a/services/ui/ws/window_server_delegate.h
+++ b/services/ui/ws/window_server_delegate.h
@@ -17,14 +17,12 @@
 namespace ui {
 
 namespace mojom {
-class WindowManagerFactory;
 class WindowTree;
 }
 
 namespace ws {
 
 class Display;
-class ServerWindow;
 class WindowServer;
 class WindowTree;
 class WindowTreeBinding;
diff --git a/services/ui/ws/window_server_test_impl.h b/services/ui/ws/window_server_test_impl.h
index ffeb550..9a6dc78 100644
--- a/services/ui/ws/window_server_test_impl.h
+++ b/services/ui/ws/window_server_test_impl.h
@@ -12,8 +12,6 @@
 
 class ServerWindow;
 class WindowServer;
-class WindowTree;
-struct WindowId;
 
 class WindowServerTestImpl : public mojom::WindowServerTest {
  public:
diff --git a/services/ui/ws/window_tree.h b/services/ui/ws/window_tree.h
index 5204624b..0331e1b 100644
--- a/services/ui/ws/window_tree.h
+++ b/services/ui/ws/window_tree.h
@@ -43,13 +43,11 @@
 class DisplayManager;
 class Display;
 class DragTargetConnection;
-class EventMatcher;
 class ServerWindow;
 class TargetedEvent;
 class WindowManagerDisplayRoot;
 class WindowManagerState;
 class WindowServer;
-class WindowTreeTest;
 
 namespace test {
 class WindowTreeTestApi;
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 6a54c4ae..c9d2be46 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -505,8 +505,6 @@
 
 crbug.com/659123 [ Mac ] fast/css/text-overflow-ellipsis-button.html [ Pass Failure ]
 
-crbug.com/670295 virtual/mojo-loading/http/tests/preload/external_css_import_preload.html [ Pass Failure ]
-
 # TODO(oshima): Mac Android are currently not supported.
 crbug.com/567837 [ Mac Android ] virtual/scalefactor200withzoom/fast/hidpi/static [ Skip ]
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-2d-isPointInPath-isPointInStroke.html b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-2d-isPointInPath-isPointInStroke.html
new file mode 100644
index 0000000..de0e1efb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-2d-isPointInPath-isPointInStroke.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+test(function() {
+    var offscreenCanvas = new OffscreenCanvas(100, 50);
+    var ctx = offscreenCanvas.getContext('2d');
+
+    ctx.rect(0, 0, 20, 20);
+    assert_true(ctx.isPointInPath(10, 10));
+    assert_false(ctx.isPointInPath(30, 10));
+
+    var path = new Path2D();
+    path.rect(20, 20, 100, 100);
+    assert_true(ctx.isPointInPath(path, 20, 20));
+    assert_false(ctx.isPointInPath(path, 130, 20));
+}, 'Test the behavior of isPointInPath APIs for OffscreenCanvas');
+
+
+test(function() {
+    var offscreenCanvas = new OffscreenCanvas(100, 50);
+    var ctx = offscreenCanvas.getContext('2d');
+
+    ctx.strokeStyle = '#0f0';
+    ctx.beginPath();
+    ctx.rect(0, 0, 20, 20);
+    assert_true(ctx.isPointInStroke(0, 0));
+    assert_false(ctx.isPointInStroke(30, 10));
+
+    var path = new Path2D();
+    path.rect(20, 20, 100, 100);
+    assert_true(ctx.isPointInStroke(path, 20, 20));
+    assert_true(ctx.isPointInStroke(path, 120, 20));
+}, 'Test the behavior of isPointInStroke APIs for OffscreenCanvas');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/autoscroll-disabled-user-select-none.html b/third_party/WebKit/LayoutTests/fast/events/autoscroll-disabled-user-select-none.html
new file mode 100644
index 0000000..689cdbe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/autoscroll-disabled-user-select-none.html
@@ -0,0 +1,33 @@
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<div id="container" style="height: 400px; overflow: auto;">
+  <div style="-webkit-user-select:none; height: 2000px; background-color: yellow">
+    Hello World! Hello Woorld! Hello Wooorld! Hello Woooorld!<br />
+    Hello World! Hello Woorld! Hello Wooorld! Hello Woooorld!<br />
+    Hello World! Hello Woorld! Hello Wooorld! Hello Woooorld!<br />
+    Hello World! Hello Woorld! Hello Wooorld! Hello Woooorld!<br />
+    Hello World! Hello Woorld! Hello Wooorld! Hello Woooorld!<br />
+  </div>
+</div>
+<script>
+var testSelectNone = async_test("Selection-autoscroll should not be triggered when the selection happens in an element with user-select:none");
+testSelectNone.step(function() {
+    if (!window.eventSender)
+        return;
+    var dragStartX = 50;
+    var dragStartY = 50;
+    var dragEndX = 60;
+    var dragEndY = 400;
+    var container = document.getElementById("container");
+
+    eventSender.dragMode = false;
+    eventSender.mouseMoveTo(dragStartX, dragStartY);
+    eventSender.mouseDown();
+    eventSender.mouseMoveTo(dragEndX, dragEndY);
+
+    requestAnimationFrame(function() {
+        assert_equals(container.scrollTop, 0);
+        testSelectNone.done();
+    });
+});
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/forms/text/input-readonly-autoscroll.html b/third_party/WebKit/LayoutTests/fast/forms/text/input-readonly-autoscroll.html
index f8ef7b87..8106051c 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/text/input-readonly-autoscroll.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/text/input-readonly-autoscroll.html
@@ -17,7 +17,7 @@
                     eventSender.dragMode = false;
                     eventSender.mouseMoveTo(20, h);
                     eventSender.mouseDown();
-                    eventSender.mouseMoveTo(20, h);
+                    eventSender.mouseMoveTo(40, h);
                     eventSender.mouseMoveTo(300, h);
                 }
                 setTimeout(autoscrollTestPart2, 100);
diff --git a/third_party/WebKit/LayoutTests/media/preload-conditions.html b/third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html
similarity index 97%
rename from third_party/WebKit/LayoutTests/media/preload-conditions.html
rename to third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html
index 8e550820..2d015eac 100644
--- a/third_party/WebKit/LayoutTests/media/preload-conditions.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/preload-conditions.html
@@ -2,7 +2,7 @@
 <title>Test media preloading behaviour with different conditions.</title>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="media-file.js"></script>
+<script src="../../media-resources/media-file.js"></script>
 <script>
 var tests = [
   {
@@ -135,7 +135,7 @@
   [ '', 'none', 'metadata', 'auto' ].forEach(preload => {
     var media = document.createElement('video');
     media.preload = preload;
-    media.src = findMediaFile('video', 'content/test');
+    media.src = findMediaFile('video', 'resources/test');
     assert_equals(media.readyState, HTMLMediaElement.HAVE_NOTHING);
 
     switch (media.preload) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html b/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html
index e02af246..a4ace4f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html
@@ -1,27 +1,29 @@
 <!DOCTYPE html>
 <title>Overflow menu updates properly.</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../../media-resources/media-controls.js"></script>
-<script src="../../media-resources/media-file.js"></script>
-<script src="../../media-resources/overflow-menu.js"></script>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script src='../../media-resources/media-controls.js'></script>
+<script src='../../media-resources/media-file.js'></script>
+<script src='../../media-resources/overflow-menu.js'></script>
 
 <!--Padding ensures the overflow menu is visible for the tests. -->
-<body style="padding-top: 200px; padding-left: 100px">
+<body style='padding-top: 200px; padding-left: 100px'>
 <video controls></video>
 <script>
-async_test(function(t) {
+async_test(t => {
+  assert_true('internals' in window, 'window.internals must be available');
+
   // Set up video
-  var video = document.querySelector("video");
-  video.src = findMediaFile("video", "resources/test");
-  video.setAttribute("width", "60");
+  var video = document.querySelector('video');
+  video.src = findMediaFile('video', 'resources/test');
+  video.setAttribute('width', '60');
   // Add captions
-  var trackElement = document.createElement("track");
+  var trackElement = document.createElement('track');
   video.appendChild(trackElement);
   // Pretend we have a cast device
   internals.mediaPlayerRemoteRouteAvailabilityChanged(video, true);
 
-  video.onloadeddata = t.step_func_done(function() {
+  video.onloadeddata = t.step_func(_ => {
     var overflowList = getOverflowList(video);
 
     // Remove cast device and ensure the overflow menu updates as expected
@@ -35,19 +37,22 @@
     // Ensure that all of the buttons are visible in the right order
     for (var i = 0; i < children.length; i++) {
       var child = children[i];
-      if (buttonsWithoutCast[i]) {
-        assert_not_equals(getComputedStyle(child).display, "none");
-      } else {
-        assert_equals(getComputedStyle(child).display, "none");
-      }
+      if (buttonsWithoutCast[i])
+        assert_not_equals(getComputedStyle(child).display, 'none');
+      else
+        assert_equals(getComputedStyle(child).display, 'none');
     }
     internals.mediaPlayerRemoteRouteAvailabilityChanged(video, true);
-    assert_not_equals(getComputedStyle(children[OverflowMenuButtons.CAST]).display, "none");
+    assert_not_equals(getComputedStyle(children[OverflowMenuButtons.CAST]).display, 'none');
 
     // Removing closed captions hides button in overflow menu
-    assert_not_equals(getComputedStyle(children[OverflowMenuButtons.CLOSED_CAPTIONS]).display, "none");
+    assert_not_equals(getComputedStyle(children[OverflowMenuButtons.CLOSED_CAPTIONS]).display, 'none');
     video.removeChild(trackElement);
-    assert_equals(getComputedStyle(children[OverflowMenuButtons.CLOSED_CAPTIONS]).display, "none");
+
+    // The controls are updated asynchronously.
+    setTimeout(t.step_func_done(_ => {
+      assert_equals(getComputedStyle(children[OverflowMenuButtons.CLOSED_CAPTIONS]).display, 'none');
+    }));
   });
 });
 </script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 27fa54e8..9e7b6d96 100644
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -525,6 +525,8 @@
     method fillRect
     method getImageData
     method getLineDash
+    method isPointInPath
+    method isPointInStroke
     method lineTo
     method moveTo
     method putImageData
diff --git a/third_party/WebKit/LayoutTests/media/controls/closed-captions-dynamic-update.html b/third_party/WebKit/LayoutTests/media/controls/closed-captions-dynamic-update.html
new file mode 100644
index 0000000..dc9d80d2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/controls/closed-captions-dynamic-update.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<title>Tests that the closed captions button enables track switching.</title>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../media-file.js'></script>
+<script src='../media-controls.js'></script>
+<video controls>
+  <track src='../track/captions-webvtt/captions-fast.vtt' kind='captions'>
+  <track src='../track/captions-webvtt/captions-rtl.vtt' kind='captions'>
+</video>
+<script>
+async_test(t => {
+  var video = document.querySelector('video');
+
+  video.oncanplaythrough = t.step_func(_ => {
+    assert_true(isClosedCaptionsButtonVisible(video));
+
+    // The captions track should be listed in textTracks, but not yet loaded.
+    assert_equals(video.textTracks.length, 2);
+    assert_equals(video.textTracks[0].mode, 'disabled');
+    assert_equals(video.textTracks[1].mode, 'disabled');
+    assert_equals(textTrackContainerElement(video), null);
+
+    var tracks = document.querySelectorAll('track');
+    tracks[0].onload = t.step_func(_ => {
+      assert_equals(textTrackDisplayElement(video).innerText, 'Lorem');
+
+      // Captions should not be visible after Off is clicked.
+      turnClosedCaptionsOff(video);
+      assert_equals(textTrackDisplayElement(video), null);
+
+      // Remove the track elements.
+      tracks[1].remove();
+      tracks[0].remove();
+
+      // The controls are updated asynchronously.
+      setTimeout(t.step_func(_ => {
+        assert_false(isClosedCaptionsButtonVisible(video));
+
+        // Add non-default text track through HTML with unloadable URI.
+        var track = document.createElement('track');
+        track.setAttribute('src', 'invalid.vtt');
+
+        track.onerror = t.step_func(_ => {
+          // Track failed to load.
+          assert_false(isClosedCaptionsButtonVisible(video));
+          // Add a text track through JS to the video element.
+          var newTrack = video.addTextTrack('captions', 'English', 'en');
+          setTimeout(t.step_func_done(_ => {
+            assert_true(isClosedCaptionsButtonVisible(video));
+          }));
+        });
+
+        video.appendChild(track);
+        assert_equals(track.readyState, HTMLTrackElement.NONE);
+        assert_equals(track.track.mode, 'disabled');
+        assert_equals(video.textTracks.length, 1);
+
+        setTimeout(t.step_func(_ => {
+          assert_true(isClosedCaptionsButtonVisible(video));
+          clickTextTrackAtIndex(video, 0);
+        }));
+      }));
+    });
+
+    // Captions track should load and captions should become visible after a track is selected.
+    clickTextTrackAtIndex(video, 0);
+  });
+
+  video.src = findMediaFile('video', '../content/counting');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/media/controls/closed-captions-on-off.html b/third_party/WebKit/LayoutTests/media/controls/closed-captions-on-off.html
new file mode 100644
index 0000000..8558d03
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/controls/closed-captions-on-off.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Tests that tracks can be turned on and off through the track selection menu.</title>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../media-controls.js'></script>
+<script src='../media-file.js'></script>
+<video controls></video>
+<script>
+async_test(t => {
+  var captions = ['First', 'Second', 'Third'];
+  var video = document.querySelector('video');
+
+  video.oncanplaythrough = t.step_func(_ => {
+    var track1 = video.addTextTrack('captions');
+    var track2 = video.addTextTrack('captions');
+
+    for (var i = 0; i < captions.length; ++i) {
+      track1.addCue(new VTTCue(0, 120, captions[i]));
+      track2.addCue(new VTTCue(0, 120, captions[i]));
+    }
+
+    // The controls are updated asynchronously.
+    assert_false(isClosedCaptionsButtonVisible(video));
+
+    setTimeout(t.step_func_done(_ => {
+      assert_true(isClosedCaptionsButtonVisible(video));
+
+      // The captions track should be listed in textTracks, but not yet loaded.
+      assert_equals(video.textTracks.length, 2);
+      assert_equals(video.textTracks[0].mode, 'hidden');
+      assert_equals(video.textTracks[1].mode, 'hidden');
+      checkCaptionsHidden(video);
+
+      // Captions track should become visible after the track is selected.
+      clickTextTrackAtIndex(video, 0);
+      checkCaptionsVisible(video, captions);
+
+      // Captions should not be visible after they're turned off through the menu.
+      turnClosedCaptionsOff(video);
+      checkCaptionsHidden(video);
+
+      // Captions track should become visible after the track is selected again.
+      clickTextTrackAtIndex(video, 0);
+      checkCaptionsVisible(video, captions);
+    }));
+  });
+
+  video.src = findMediaFile('video', '../content/counting');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/media/controls/closed-captions-single-track.html b/third_party/WebKit/LayoutTests/media/controls/closed-captions-single-track.html
new file mode 100644
index 0000000..4c58267
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/controls/closed-captions-single-track.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>A single track should not show overflow on caption button-press, instead just toggle.</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<script src="../media-file.js"></script>
+<video controls></video>
+<script>
+async_test(t => {
+  var captions = ["Caption"];
+  var video = document.querySelector("video");
+
+  video.oncanplaythrough = t.step_func(_ => {
+    var track1 = video.addTextTrack("captions");
+
+    for (var i = 0; i < captions.length; ++i)
+      track1.addCue(new VTTCue(0, 120, captions[i]));
+
+    // The controls are updated asynchronously.
+    assert_false(isClosedCaptionsButtonVisible(video));
+
+    setTimeout(t.step_func_done(_ => {
+      assert_true(isClosedCaptionsButtonVisible(video));
+
+      // The captions track should be listed in textTracks, but not yet loaded.
+      assert_equals(video.textTracks.length, 1);
+      assert_equals(video.textTracks[0].mode, "hidden");
+      checkCaptionsHidden(video);
+
+      // Get the menu that displays the list of text tracks.
+      var captionsList = mediaControlsElement(internals.shadowRoot(video).firstChild,
+        "-internal-media-controls-text-track-list");
+
+      clickCaptionButton(video);
+      assert_equals(getComputedStyle(captionsList).display, "none");
+
+      // Captions track should become visible after the closed caption button is pressed.
+      checkCaptionsVisible(video, captions);
+
+      // Click the closed captions button again and make sure the menu does not appear.
+      clickCaptionButton(video);
+      assert_equals(getComputedStyle(captionsList).display, "none");
+
+      // Captions track should become invisible after the closed caption button is pressed.
+      checkCaptionsHidden(video);
+    }));
+  });
+
+  video.src = findMediaFile("video", "../content/counting");
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/media/controls/closed-captions-switch-track.html b/third_party/WebKit/LayoutTests/media/controls/closed-captions-switch-track.html
new file mode 100644
index 0000000..2915e3f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/controls/closed-captions-switch-track.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>Test that we can add multiple tracks and select between them from the track selection menu.</title>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../media-file.js'></script>
+<script src='../media-controls.js'></script>
+<!-- Width should be large enough to display closed captions button. -->
+<video controls style='width: 500px'></video>
+<script>
+async_test(t => {
+  var video = document.querySelector('video');
+  var trackLanguages = ['en', 'ru', 'fr', 'jp', 'de'];
+  var trackCueText = ['English', 'Russian', 'French', 'Japanese', 'German'];
+
+  video.oncanplaythrough = t.step_func(_ => {
+    for (var i = 0; i < trackLanguages.length; i++) {
+      var track = video.addTextTrack('captions', trackCueText[i], trackLanguages[i]);
+      track.addCue(new VTTCue(0, 1, trackCueText[i]));
+      track.mode = 'disabled';
+    }
+
+    // The controls are updated asynchronously.
+    assert_false(isClosedCaptionsButtonVisible(video));
+
+    setTimeout(t.step_func_done(_ => {
+      assert_true(isClosedCaptionsButtonVisible(video));
+      assert_equals(video.textTracks.length, trackLanguages.length);
+
+      for (var i = 0; i < trackLanguages.length; i++) {
+        clickTextTrackAtIndex(video, i);
+        assert_equals(video.textTracks[i].mode, 'showing');
+        assert_equals(textTrackDisplayElement(video).innerText, trackCueText[i]);
+        for (var j = 0; j < trackLanguages.length; j++) {
+          if (j != i)
+            assert_equals(video.textTracks[j].mode, 'disabled');
+        }
+      }
+    }));
+  });
+
+  video.src = findMediaFile('video', '../content/test');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks.html b/third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks.html
deleted file mode 100644
index bd3e75fb..0000000
--- a/third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<title>Test that we can add multiple tracks and select between them from the track selection menu.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../media-file.js"></script>
-<script src="../media-controls.js"></script>
-<!-- Width should be large enough to display closed captions button. -->
-<video controls style="width: 500px"></video>
-<script>
-async_test(function(t) {
-    var video = document.querySelector("video");
-    var trackLanguages = ["en", "ru", "fr", "jp", "de"];
-    var trackCueText = ["English", "Russian", "French", "Japanese", "German"];
-
-    video.oncanplaythrough = t.step_func_done(function() {
-        for (var i = 0; i < trackLanguages.length; i++) {
-            var track = video.addTextTrack("captions", trackCueText[i], trackLanguages[i]);
-            track.addCue(new VTTCue(0, 1, trackCueText[i]));
-            track.mode = "disabled";
-        }
-        assert_true(isClosedCaptionsButtonVisible(video));
-        assert_equals(video.textTracks.length, trackLanguages.length);
-
-        for (var i = 0; i < trackLanguages.length; i++) {
-            clickTextTrackAtIndex(video, i);
-            assert_equals(video.textTracks[i].mode, "showing");
-            assert_equals(textTrackDisplayElement(video).innerText, trackCueText[i]);
-            for (var j = 0; j < trackLanguages.length; j++) {
-                if (j != i)
-                    assert_equals(video.textTracks[j].mode, "disabled");
-            }
-        }
-    });
-
-    video.src = findMediaFile("video", "../content/test");
-});
-</script>
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-caption-single-track.html b/third_party/WebKit/LayoutTests/media/video-controls-caption-single-track.html
deleted file mode 100644
index daf4f20..0000000
--- a/third_party/WebKit/LayoutTests/media/video-controls-caption-single-track.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<title>A single track should not show overflow on caption button-press, instead just toggle.</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="media-controls.js"></script>
-<script src="media-file.js"></script>
-<video controls></video>
-<script>
-async_test(function(t) {
-    var captions = ["Caption"];
-    var video = document.querySelector("video");
-
-    video.oncanplaythrough = t.step_func_done(function() {
-        var track1 = video.addTextTrack("captions");
-
-        for (var i = 0; i < captions.length; i++) {
-            track1.addCue(new VTTCue(0, 120, captions[i]));
-        }
-        assert_true(isClosedCaptionsButtonVisible(video));
-
-        // The captions track should be listed in textTracks, but not yet loaded.
-        assert_equals(video.textTracks.length, 1);
-        assert_equals(video.textTracks[0].mode, "hidden");
-        checkCaptionsHidden(video);
-
-        // Get the menu that displays the list of text tracks.
-        var captionsList = mediaControlsElement(internals.shadowRoot(video).firstChild,
-            "-internal-media-controls-text-track-list");
-
-        clickCaptionButton(video);
-        assert_equals(getComputedStyle(captionsList).display, "none");
-
-        // Captions track should become visible after the closed caption button is pressed.
-        checkCaptionsVisible(video, captions);
-
-        // Click the closed captions button again and make sure the menu does not appear.
-        clickCaptionButton(video);
-        assert_equals(getComputedStyle(captionsList).display, "none");
-
-        // Captions track should become visible after the closed caption button is pressed.
-        checkCaptionsHidden(video);
-    });
-
-    video.src = findMediaFile("video", "content/counting");
-});
-</script>
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-captions-on-off.html b/third_party/WebKit/LayoutTests/media/video-controls-captions-on-off.html
deleted file mode 100644
index 49906b1..0000000
--- a/third_party/WebKit/LayoutTests/media/video-controls-captions-on-off.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<title>Tests that tracks can be turned on and off through the track selection menu.</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="media-controls.js"></script>
-<script src="media-file.js"></script>
-<video controls></video>
-<script>
-async_test(function(t) {
-    var captions = ["First", "Second", "Third"];
-    var video = document.querySelector("video");
-
-    video.oncanplaythrough = t.step_func_done(function() {
-        var track1 = video.addTextTrack("captions");
-        var track2 = video.addTextTrack("captions");
-
-        for (var i = 0; i < captions.length; i++) {
-            track1.addCue(new VTTCue(0, 120, captions[i]));
-            track2.addCue(new VTTCue(0, 120, captions[i]));
-        }
-        assert_true(isClosedCaptionsButtonVisible(video));
-
-        // The captions track should be listed in textTracks, but not yet loaded.
-        assert_equals(video.textTracks.length, 2);
-        assert_equals(video.textTracks[0].mode, "hidden");
-        checkCaptionsHidden(video);
-
-        // Captions track should become visible after the track is selected.
-        clickTextTrackAtIndex(video, 0);
-        checkCaptionsVisible(video, captions);
-
-        // Captions should not be visible after they're turned off through the menu.
-        turnClosedCaptionsOff(video);
-        checkCaptionsHidden(video);
-
-        // Captions track should become visible after the track is selected again.
-        clickTextTrackAtIndex(video, 0);
-        checkCaptionsVisible(video, captions);
-    });
-
-    video.src = findMediaFile("video", "content/counting");
-});
-</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-captions.html b/third_party/WebKit/LayoutTests/media/video-controls-captions.html
deleted file mode 100644
index 608bcbe..0000000
--- a/third_party/WebKit/LayoutTests/media/video-controls-captions.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!DOCTYPE html>
-<title>Tests that the closed captions button enables track switching.</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="media-file.js"></script>
-<script src="media-controls.js"></script>
-<video controls>
-    <track src="track/captions-webvtt/captions-fast.vtt" kind="captions">
-    <track src="track/captions-webvtt/captions-rtl.vtt" kind="captions">
-</video>
-<script>
-async_test(function(t) {
-    var video = document.querySelector("video");
-
-    video.oncanplaythrough = t.step_func(function() {
-        assert_true(isClosedCaptionsButtonVisible(video));
-
-        // The captions track should be listed in textTracks, but not yet loaded.
-        assert_equals(video.textTracks.length, 2);
-        assert_equals(video.textTracks[0].mode, "disabled");
-        assert_equals(textTrackContainerElement(video), null);
-
-        var tracks = document.querySelectorAll("track");
-        tracks[0].onload = t.step_func(function() {
-            assert_equals(textTrackDisplayElement(video).innerText, "Lorem");
-
-            // Captions should not be visible after Off is clicked.
-            turnClosedCaptionsOff(video);
-            assert_equals(textTrackDisplayElement(video), null);
-
-            // Remove DOM node representing the track element.
-            tracks[1].remove();
-            tracks[0].remove();
-            assert_false(isClosedCaptionsButtonVisible(video));
-
-            addUnloadableHTMLTrackElement();
-            assert_true(isClosedCaptionsButtonVisible(video));
-
-            clickTextTrackAtIndex(video, 0);
-        });
-
-        // Captions track should load and captions should become visible after a track is selected.
-        clickTextTrackAtIndex(video, 0);
-    });
-
-    function addUnloadableHTMLTrackElement() {
-        // Add non-default text track through HTML with unloadable URI.
-        var track = document.createElement("track");
-        track.setAttribute("kind", "captions");
-        track.setAttribute("srclang", "en");
-        track.setAttribute("src", "invalid.vtt");
-
-        track.onerror = t.step_func_done(function() {
-            // Track failed to load.
-            assert_false(isClosedCaptionsButtonVisible(video));
-            // Add a text track through JS to the video element.
-            var newTrack = video.addTextTrack("captions", "English", "en");
-            assert_true(isClosedCaptionsButtonVisible(video));
-        });
-
-        video.appendChild(track);
-        assert_equals(track.readyState, HTMLTrackElement.NONE);
-        assert_equals(track.track.mode, "disabled");
-        assert_equals(video.textTracks.length, 1);
-    }
-
-    video.src = findMediaFile("video", "content/counting");
-});
-</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
index 66baf38..bba6d777 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3171,10 +3171,12 @@
     method toJSON
 interface MediaDevices : EventTarget
     attribute @@toStringTag
+    getter ondevicechange
     method constructor
     method enumerateDevices
     method getSupportedConstraints
     method getUserMedia
+    setter ondevicechange
 interface MediaElementAudioSourceNode : AudioSourceNode
     attribute @@toStringTag
     getter mediaElement
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 3c39f18..a995e225 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -532,6 +532,8 @@
     method fillRect
     method getImageData
     method getLineDash
+    method isPointInPath
+    method isPointInStroke
     method lineTo
     method moveTo
     method putImageData
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 449dcc0..b87c7a49 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3228,10 +3228,12 @@
     method toJSON
 interface MediaDevices : EventTarget
     attribute @@toStringTag
+    getter ondevicechange
     method constructor
     method enumerateDevices
     method getSupportedConstraints
     method getUserMedia
+    setter ondevicechange
 interface MediaElementAudioSourceNode : AudioSourceNode
     attribute @@toStringTag
     getter mediaElement
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 07db00e7..0fe55af 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -544,6 +544,8 @@
 [Worker]     method fillRect
 [Worker]     method getImageData
 [Worker]     method getLineDash
+[Worker]     method isPointInPath
+[Worker]     method isPointInStroke
 [Worker]     method lineTo
 [Worker]     method moveTo
 [Worker]     method putImageData
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 62cb2013..99dd9093 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -4400,6 +4400,8 @@
     method fillRect
     method getImageData
     method getLineDash
+    method isPointInPath
+    method isPointInStroke
     method lineTo
     method moveTo
     method putImageData
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
index 3f2c35f6..48a586b 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -539,6 +539,8 @@
 [Worker]     method fillRect
 [Worker]     method getImageData
 [Worker]     method getLineDash
+[Worker]     method isPointInPath
+[Worker]     method isPointInStroke
 [Worker]     method lineTo
 [Worker]     method moveTo
 [Worker]     method putImageData
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
index 403c3d14..f0c1db8d 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
@@ -61,7 +61,17 @@
   visitor->trace(m_code);
 }
 
-ScheduledAction::~ScheduledAction() {}
+ScheduledAction::~ScheduledAction() {
+  // Verify that owning DOMTimer has eagerly disposed.
+  DCHECK(m_info.IsEmpty());
+}
+
+void ScheduledAction::dispose() {
+  m_code.dispose();
+  m_info.Clear();
+  m_function.clear();
+  m_scriptState.clear();
+}
 
 void ScheduledAction::execute(ExecutionContext* context) {
   if (context->isDocument()) {
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.h b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.h
index 28a80e4..b65d36ee 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.h
@@ -56,6 +56,8 @@
   static ScheduledAction* create(ScriptState*, const String& handler);
 
   ~ScheduledAction();
+  void dispose();
+
   DECLARE_TRACE();
 
   void execute(ExecutionContext*);
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
index 9c7f78f..ef9de04 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
@@ -36,6 +36,13 @@
 
 ScriptSourceCode::~ScriptSourceCode() {}
 
+void ScriptSourceCode::dispose() {
+  m_source = String();
+  m_resource = nullptr;
+  m_streamer = nullptr;
+  m_url = KURL();
+}
+
 DEFINE_TRACE(ScriptSourceCode) {
   visitor->trace(m_resource);
   visitor->trace(m_streamer);
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h
index 3af0431..15651270 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h
@@ -56,6 +56,7 @@
   ScriptSourceCode(ScriptStreamer*, ScriptResource*);
 
   ~ScriptSourceCode();
+  void dispose();
   DECLARE_TRACE();
 
   bool isEmpty() const { return m_source.isEmpty(); }
@@ -65,13 +66,13 @@
   bool isNull() const { return m_source.isNull(); }
 
   const String& source() const { return m_source; }
-  ScriptResource* resource() const { return m_resource.get(); }
+  ScriptResource* resource() const { return m_resource; }
   const KURL& url() const;
   int startLine() const { return m_startPosition.m_line.oneBasedInt(); }
   const TextPosition& startPosition() const { return m_startPosition; }
   String sourceMapUrl() const;
 
-  ScriptStreamer* streamer() const { return m_streamer.get(); }
+  ScriptStreamer* streamer() const { return m_streamer; }
 
  private:
   void treatNullSourceAsEmpty();
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
index dcb07ce..5f3cbb3 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -37,7 +37,6 @@
 #include "bindings/core/v8/V8ErrorEvent.h"
 #include "bindings/core/v8/V8ErrorHandler.h"
 #include "bindings/core/v8/V8GCController.h"
-#include "bindings/core/v8/V8History.h"
 #include "bindings/core/v8/V8IdleTaskRunner.h"
 #include "bindings/core/v8/V8Location.h"
 #include "bindings/core/v8/V8PerContextData.h"
@@ -81,9 +80,6 @@
     return V8Window::toImpl(windowWrapper)->frame();
   }
 
-  if (V8History::wrapperTypeInfo.equals(type))
-    return V8History::toImpl(host)->frame();
-
   if (V8Location::wrapperTypeInfo.equals(type))
     return V8Location::toImpl(host)->frame();
 
diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.cpp b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
index f3cd7206..a6140804 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimer.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
@@ -107,7 +107,10 @@
     startRepeating(intervalMilliseconds, BLINK_FROM_HERE);
 }
 
-DOMTimer::~DOMTimer() {}
+DOMTimer::~DOMTimer() {
+  if (m_action)
+    m_action->dispose();
+}
 
 void DOMTimer::stop() {
   InspectorInstrumentation::asyncTaskCanceled(getExecutionContext(), this);
@@ -115,6 +118,8 @@
   // Need to release JS objects potentially protected by ScheduledAction
   // because they can form circular references back to the ExecutionContext
   // which will cause a memory leak.
+  if (m_action)
+    m_action->dispose();
   m_action = nullptr;
   SuspendableTimer::stop();
 }
@@ -172,6 +177,8 @@
   executionContext->timers()->setTimerNestingLevel(0);
   // Eagerly unregister as ExecutionContext observer.
   clearContext();
+  // Eagerly clear out |action|'s resources.
+  action->dispose();
 }
 
 WebTaskRunner* DOMTimer::timerTaskRunner() const {
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 653415b..4419357 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -3547,12 +3547,11 @@
   const TopDocumentRootScrollerController& controller =
       m_frame->host()->globalRootScrollerController();
 
-  if (!controller.globalRootScroller())
+  if (!layoutViewportScrollableArea())
     return false;
 
   return RootScrollerUtil::scrollableAreaForRootScroller(
-             *controller.globalRootScroller()) ==
-         layoutViewportScrollableArea();
+             controller.globalRootScroller()) == layoutViewportScrollableArea();
 }
 
 AXObjectCache* FrameView::axObjectCache() const {
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
index 6449cf95..c6784bf0f 100644
--- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
@@ -565,8 +565,9 @@
   m_hashAlgorithmsUsed |= algorithm;
 }
 
-void SourceListDirective::addSourceToMap(HashMap<String, CSPSource*>& hashMap,
-                                         CSPSource* source) {
+void SourceListDirective::addSourceToMap(
+    HeapHashMap<String, Member<CSPSource>>& hashMap,
+    CSPSource* source) {
   hashMap.add(source->getScheme(), source);
   if (source->getScheme() == "http")
     hashMap.add("https", source);
@@ -648,16 +649,17 @@
   return CSPSource::firstSubsumesSecond(normalizedA, normalizedB);
 }
 
-HashMap<String, CSPSource*> SourceListDirective::getIntersectSchemesOnly(
+HeapHashMap<String, Member<CSPSource>>
+SourceListDirective::getIntersectSchemesOnly(
     HeapVector<Member<CSPSource>> other) {
-  HashMap<String, CSPSource*> schemesA;
+  HeapHashMap<String, Member<CSPSource>> schemesA;
   for (const auto& sourceA : m_list) {
     if (sourceA->isSchemeOnly())
       addSourceToMap(schemesA, sourceA);
   }
   // Add schemes only sources if they are present in both `this` and `other`,
   // allowing upgrading `http` to `https` and `ws` to `wss`.
-  HashMap<String, CSPSource*> intersect;
+  HeapHashMap<String, Member<CSPSource>> intersect;
   for (const auto& sourceB : other) {
     if (sourceB->isSchemeOnly()) {
       if (schemesA.contains(sourceB->getScheme()))
@@ -674,14 +676,14 @@
 
 HeapVector<Member<CSPSource>> SourceListDirective::getIntersectCSPSources(
     HeapVector<Member<CSPSource>> other) {
-  HashMap<String, CSPSource*> schemesMap = getIntersectSchemesOnly(other);
+  auto schemesMap = getIntersectSchemesOnly(other);
   HeapVector<Member<CSPSource>> normalized;
   // Add all normalized scheme source expressions.
-  for (auto it = schemesMap.begin(); it != schemesMap.end(); ++it) {
+  for (const auto& it : schemesMap) {
     // We do not add secure versions if insecure schemes are present.
-    if ((it->key != "https" || !schemesMap.contains("http")) &&
-        (it->key != "wss" || !schemesMap.contains("ws"))) {
-      normalized.append(it->value);
+    if ((it.key != "https" || !schemesMap.contains("http")) &&
+        (it.key != "wss" || !schemesMap.contains("ws"))) {
+      normalized.append(it.value);
     }
   }
 
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
index fd4aa311..aba676215 100644
--- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
+++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
@@ -93,12 +93,13 @@
   void addSourceHash(const ContentSecurityPolicyHashAlgorithm&,
                      const DigestValue& hash);
 
-  static void addSourceToMap(HashMap<String, CSPSource*>&, CSPSource*);
+  static void addSourceToMap(HeapHashMap<String, Member<CSPSource>>&,
+                             CSPSource*);
 
   bool hasSourceMatchInList(const KURL&, ResourceRequest::RedirectStatus) const;
   HeapVector<Member<CSPSource>> getIntersectCSPSources(
       HeapVector<Member<CSPSource>> other);
-  HashMap<String, CSPSource*> getIntersectSchemesOnly(
+  HeapHashMap<String, Member<CSPSource>> getIntersectSchemesOnly(
       HeapVector<Member<CSPSource>> other);
 
   Member<ContentSecurityPolicy> m_policy;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index 49afa31..3e66e71 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -1251,9 +1251,14 @@
     // clicking the captions button. In this case, a check whether all the
     // resources have failed loading should be done in order to hide the CC
     // button.
+    // TODO(mlamouri): when an HTMLTrackElement fails to load, it is not
+    // propagated to the TextTrack object in a web exposed fashion. We have to
+    // keep relying on a custom glue to the controls while this is taken care
+    // of on the web side. See https://crbug.com/669977
     if (mediaControls() &&
-        track->getReadinessState() == TextTrack::FailedToLoad)
-      mediaControls()->refreshClosedCaptionsButtonVisibility();
+        track->getReadinessState() == TextTrack::FailedToLoad) {
+      mediaControls()->onTrackElementFailedToLoad();
+    }
   }
 }
 
@@ -1702,11 +1707,8 @@
     shouldUpdateDisplayState = true;
   }
 
-  if (shouldUpdateDisplayState) {
+  if (shouldUpdateDisplayState)
     updateDisplayState();
-    if (mediaControls())
-      mediaControls()->refreshClosedCaptionsButtonVisibility();
-  }
 
   updatePlayState();
   cueTimeline().updateActiveCues(currentTime());
@@ -2070,10 +2072,13 @@
     return WebMediaPlayer::PreloadNone;
   }
 
-  // Force preload to 'none' on Data Saver and for low end devices.
+  // If the source scheme is requires network, force preload to 'none' on Data
+  // Saver and for low end devices.
   if (document().settings() &&
       (document().settings()->dataSaverEnabled() ||
-       document().settings()->forcePreloadNoneForMediaElements())) {
+       document().settings()->forcePreloadNoneForMediaElements()) &&
+      (m_currentSrc.protocol() != "blob" && m_currentSrc.protocol() != "data" &&
+       m_currentSrc.protocol() != "file")) {
     UseCounter::count(document(),
                       UseCounter::HTMLMediaElementPreloadForcedNone);
     return WebMediaPlayer::PreloadNone;
@@ -2609,7 +2614,7 @@
   // cancelable, and that uses the TrackEvent interface, with the track
   // attribute initialized to the text track's TextTrack object, at the media
   // element's textTracks attribute's TextTrackList object.
-  addTextTrack(textTrack);
+  textTracks()->append(textTrack);
 }
 
 void HTMLMediaElement::removeTextTrack(WebInbandTextTrack* webTrack) {
@@ -2623,24 +2628,7 @@
   if (!textTrack)
     return;
 
-  removeTextTrack(textTrack);
-}
-
-void HTMLMediaElement::textTracksChanged() {
-  if (mediaControls())
-    mediaControls()->refreshClosedCaptionsButtonVisibility();
-}
-
-void HTMLMediaElement::addTextTrack(TextTrack* track) {
-  textTracks()->append(track);
-
-  textTracksChanged();
-}
-
-void HTMLMediaElement::removeTextTrack(TextTrack* track) {
-  m_textTracks->remove(track);
-
-  textTracksChanged();
+  m_textTracks->remove(textTrack);
 }
 
 void HTMLMediaElement::forgetResourceSpecificTracks() {
@@ -2650,7 +2638,6 @@
   if (m_textTracks) {
     TrackDisplayUpdateScope scope(this->cueTimeline());
     m_textTracks->removeAllInbandTracks();
-    textTracksChanged();
   }
 
   m_audioTracks->removeAll();
@@ -2683,7 +2670,7 @@
   //    interface, with the track attribute initialised to the new text
   //    track's TextTrack object, at the media element's textTracks
   //    attribute's TextTrackList object.
-  addTextTrack(textTrack);
+  textTracks()->append(textTrack);
 
   // Note: Due to side effects when changing track parameters, we have to
   // first append the track to the text track list.
@@ -2715,7 +2702,7 @@
   if (!textTrack)
     return;
 
-  addTextTrack(textTrack);
+  textTracks()->append(textTrack);
 
   // Do not schedule the track loading until parsing finishes so we don't start
   // before all tracks in the markup have been added.
@@ -2741,7 +2728,7 @@
   // When a track element's parent element changes and the old parent was a
   // media element, then the user agent must remove the track element's
   // corresponding text track from the media element's list of text tracks.
-  removeTextTrack(textTrack);
+  m_textTracks->remove(textTrack);
 
   size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack);
   if (index != kNotFound)
@@ -2768,8 +2755,6 @@
 
   AutomaticTrackSelection trackSelection(configuration);
   trackSelection.perform(*m_textTracks);
-
-  textTracksChanged();
 }
 
 bool HTMLMediaElement::havePotentialSourceChild() {
@@ -3432,12 +3417,14 @@
 }
 
 bool HTMLMediaElement::hasClosedCaptions() const {
-  if (m_textTracks) {
-    for (unsigned i = 0; i < m_textTracks->length(); ++i) {
-      if (m_textTracks->anonymousIndexedGetter(i)->canBeRendered())
-        return true;
-    }
+  if (!m_textTracks)
+    return false;
+
+  for (unsigned i = 0; i < m_textTracks->length(); ++i) {
+    if (m_textTracks->anonymousIndexedGetter(i)->canBeRendered())
+      return true;
   }
+
   return false;
 }
 
@@ -3644,9 +3631,6 @@
   if (!haveVisibleTextTrack && !mediaControls())
     return;
 
-  if (mediaControls())
-    mediaControls()->changedClosedCaptionsVisibility();
-
   cueTimeline().updateActiveCues(currentTime());
 
   // Note: The "time marches on" algorithm (updateActiveCues) runs the "rules
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
index 9ced65c9..36a4468 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -724,6 +724,7 @@
   friend class Internals;
   friend class TrackDisplayUpdateScope;
   friend class MediaControlsTest;
+  friend class HTMLMediaElementTest;
   friend class HTMLVideoElementTest;
 
   Member<AutoplayUmaHelper> m_autoplayUmaHelper;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp
index 7b04d525..1187ce6 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElementTest.cpp
@@ -4,8 +4,10 @@
 
 #include "core/html/HTMLMediaElement.h"
 
+#include "core/frame/Settings.h"
 #include "core/html/HTMLAudioElement.h"
 #include "core/html/HTMLVideoElement.h"
+#include "core/page/NetworkStateNotifier.h"
 #include "core/testing/DummyPageHolder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,6 +27,10 @@
   }
 
   HTMLMediaElement* media() { return m_media.get(); }
+  void setCurrentSrc(const String& src) {
+    KURL url(ParsedURLString, src);
+    media()->m_currentSrc = url;
+  }
 
  private:
   std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
@@ -55,4 +61,81 @@
   }
 }
 
+enum class TestURLScheme {
+  kHttp,
+  kHttps,
+  kFtp,
+  kFile,
+  kData,
+  kBlob,
+};
+
+String srcSchemeToURL(TestURLScheme scheme) {
+  switch (scheme) {
+    case TestURLScheme::kHttp:
+      return "http://example.com/foo.mp4";
+    case TestURLScheme::kHttps:
+      return "https://example.com/foo.mp4";
+    case TestURLScheme::kFtp:
+      return "ftp://example.com/foo.mp4";
+    case TestURLScheme::kFile:
+      return "file:///foo/bar.mp4";
+    case TestURLScheme::kData:
+      return "data:video/mp4;base64,XXXXXXX";
+    case TestURLScheme::kBlob:
+      return "blob:http://example.com/00000000-0000-0000-0000-000000000000";
+    default:
+      NOTREACHED();
+  }
+  return emptyString();
+}
+
+TEST_P(HTMLMediaElementTest, preloadType) {
+  struct TestData {
+    bool dataSaverEnabled;
+    bool forcePreloadNoneForMediaElements;
+    bool isCellular;
+    TestURLScheme srcScheme;
+    AtomicString preloadToSet;
+    AtomicString preloadExpected;
+  } testData[] = {
+      // Tests for conditions in which preload type should be overriden to
+      // "none".
+      {false, true, false, TestURLScheme::kHttp, "auto", "none"},
+      {true, true, false, TestURLScheme::kHttps, "auto", "none"},
+      {true, true, false, TestURLScheme::kFtp, "metadata", "none"},
+      {false, false, false, TestURLScheme::kHttps, "auto", "auto"},
+      {false, true, false, TestURLScheme::kFile, "auto", "auto"},
+      {false, true, false, TestURLScheme::kData, "metadata", "metadata"},
+      {false, true, false, TestURLScheme::kBlob, "auto", "auto"},
+      {false, true, false, TestURLScheme::kFile, "none", "none"},
+      // Tests for conditions in which preload type should be overriden to
+      // "metadata".
+      {false, false, true, TestURLScheme::kHttp, "auto", "metadata"},
+      {false, false, true, TestURLScheme::kHttp, "scheme", "metadata"},
+      {false, false, true, TestURLScheme::kHttp, "none", "none"},
+      // Tests that the preload is overriden to "auto"
+      {false, false, false, TestURLScheme::kHttp, "foo", "auto"},
+  };
+
+  int index = 0;
+  for (const auto& data : testData) {
+    media()->document().settings()->setDataSaverEnabled(data.dataSaverEnabled);
+    media()->document().settings()->setForcePreloadNoneForMediaElements(
+        data.forcePreloadNoneForMediaElements);
+    if (data.isCellular) {
+      networkStateNotifier().setOverride(
+          true, WebConnectionType::WebConnectionTypeCellular3G, 2.0);
+    } else {
+      networkStateNotifier().clearOverride();
+    }
+    setCurrentSrc(srcSchemeToURL(data.srcScheme));
+    media()->setPreload(data.preloadToSet);
+
+    EXPECT_EQ(data.preloadExpected, media()->preload())
+        << "preload type differs at index" << index;
+    ++index;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
index 920d5b80..217d3898 100644
--- a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
@@ -320,6 +320,18 @@
 }
 
 void CSSPreloaderResourceClient::clearResource() {
+  // Do not remove the client for unused, speculative markup preloads. This will
+  // trigger cancellation of the request and potential removal from memory
+  // cache. Link preloads are an exception because they support dynamic removal
+  // cancelling the request (and have their own passive resource client).
+  // Note: Speculative preloads which remain unused for their lifetime will
+  // never have this client removed. This should be fine because we only hold
+  // weak references to the resource.
+  if (m_resource && m_resource->isUnusedPreload() &&
+      !m_resource->isLinkPreload()) {
+    return;
+  }
+
   if (m_resource)
     m_resource->removeClient(this);
   m_resource.clear();
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
index ab7ea8b..290dd313 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
@@ -887,11 +887,11 @@
     if (mediaElement().isFullscreen()) {
       Platform::current()->recordAction(
           UserMetricsAction("Media.Controls.ExitFullscreen"));
-      mediaElement().exitFullscreen();
+      mediaControls().exitFullscreen();
     } else {
       Platform::current()->recordAction(
           UserMetricsAction("Media.Controls.EnterFullscreen"));
-      mediaElement().enterFullscreen();
+      mediaControls().enterFullscreen();
     }
     event->setDefaultHandled();
   }
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
index 5b4b0b7..81c0e66b 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
@@ -339,8 +339,7 @@
   m_timeline->setPosition(mediaElement().currentTime());
 
   onVolumeChange();
-
-  refreshClosedCaptionsButtonVisibility();
+  onTextTracksAddedOrRemoved();
 
   m_fullscreenButton->setIsWanted(shouldShowFullscreenButton(mediaElement()));
 
@@ -454,15 +453,6 @@
   m_currentTimeDisplay->setCurrentValue(now);
 }
 
-void MediaControls::changedClosedCaptionsVisibility() {
-  m_toggleClosedCaptionsButton->updateDisplayType();
-}
-
-void MediaControls::refreshClosedCaptionsButtonVisibility() {
-  m_toggleClosedCaptionsButton->setIsWanted(mediaElement().hasClosedCaptions());
-  BatchedControlUpdate batch(this);
-}
-
 void MediaControls::toggleTextTrackList() {
   if (!mediaElement().hasClosedCaptions()) {
     m_textTrackList->setVisible(false);
@@ -539,6 +529,16 @@
   resetHideMediaControlsTimer();
 }
 
+void MediaControls::enterFullscreen() {
+  // TODO(foolip): switch to Fullscreen::UnprefixedRequest when the unprefixed
+  // Fullscreen API has launched.
+  Fullscreen::requestFullscreen(mediaElement(), Fullscreen::PrefixedRequest);
+}
+
+void MediaControls::exitFullscreen() {
+  Fullscreen::exitFullscreen(document());
+}
+
 void MediaControls::enteredFullscreen() {
   m_fullscreenButton->setIsFullscreen(true);
   stopHideMediaControlsTimer();
@@ -724,6 +724,15 @@
   stopHideMediaControlsTimer();
 }
 
+void MediaControls::onTextTracksAddedOrRemoved() {
+  m_toggleClosedCaptionsButton->setIsWanted(mediaElement().hasClosedCaptions());
+  BatchedControlUpdate batch(this);
+}
+
+void MediaControls::onTextTracksChanged() {
+  m_toggleClosedCaptionsButton->updateDisplayType();
+}
+
 void MediaControls::notifyPanelWidthChanged(const LayoutUnit& newWidth) {
   // Don't bother to do any work if this matches the most recent panel
   // width, since we're called after layout.
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.h b/third_party/WebKit/Source/core/html/shadow/MediaControls.h
index f21bf2c..10a1c10 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControls.h
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.h
@@ -53,12 +53,14 @@
 
   void updateCurrentTimeDisplay();
 
-  void changedClosedCaptionsVisibility();
-  void refreshClosedCaptionsButtonVisibility();
   void toggleTextTrackList();
   void showTextTrackAtIndex(unsigned indexToEnable);
   void disableShowingTextTracks();
 
+  // Called by the fullscreen buttons to toggle fulllscreen on/off.
+  void enterFullscreen();
+  void exitFullscreen();
+
   void enteredFullscreen();
   void exitedFullscreen();
 
@@ -95,6 +97,11 @@
 
   bool overflowMenuVisible();
 
+  // TODO(mlamouri): this is temporary to notify the controls that an
+  // HTMLTrackElement failed to load because there is no web exposed way to
+  // be notified on the TextTrack object. See https://crbug.com/669977
+  void onTrackElementFailedToLoad() { onTextTracksAddedOrRemoved(); }
+
   DECLARE_VIRTUAL_TRACE();
 
  private:
@@ -148,6 +155,8 @@
   void onTimeUpdate();
   void onPlay();
   void onPause();
+  void onTextTracksAddedOrRemoved();
+  void onTextTracksChanged();
 
   Member<HTMLMediaElement> m_mediaElement;
 
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
index 154591d..9b6dae1c 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
@@ -7,6 +7,7 @@
 #include "core/events/Event.h"
 #include "core/html/HTMLMediaElement.h"
 #include "core/html/shadow/MediaControls.h"
+#include "core/html/track/TextTrackList.h"
 
 namespace blink {
 
@@ -23,6 +24,12 @@
                                                     false);
   m_mediaControls->m_mediaElement->addEventListener(EventTypeNames::pause, this,
                                                     false);
+
+  // TextTracks events.
+  TextTrackList* textTracks = m_mediaControls->m_mediaElement->textTracks();
+  textTracks->addEventListener(EventTypeNames::addtrack, this, false);
+  textTracks->addEventListener(EventTypeNames::change, this, false);
+  textTracks->addEventListener(EventTypeNames::removetrack, this, false);
 }
 
 bool MediaControlsMediaEventListener::operator==(
@@ -54,6 +61,17 @@
     return;
   }
 
+  // TextTracks events.
+  if (event->type() == EventTypeNames::addtrack ||
+      event->type() == EventTypeNames::removetrack) {
+    m_mediaControls->onTextTracksAddedOrRemoved();
+    return;
+  }
+  if (event->type() == EventTypeNames::change) {
+    m_mediaControls->onTextTracksChanged();
+    return;
+  }
+
   NOTREACHED();
 }
 
diff --git a/third_party/WebKit/Source/core/input/MouseEventManager.cpp b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
index 1810c69b..80529431 100644
--- a/third_party/WebKit/Source/core/input/MouseEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
@@ -732,8 +732,13 @@
 
   m_mouseDownMayStartDrag = false;
 
+  m_frame->eventHandler().selectionController().handleMouseDraggedEvent(
+      event, m_mouseDownPos, m_dragStartPos, m_mousePressNode.get(),
+      m_lastKnownMousePosition);
+
   if (m_mouseDownMayStartAutoscroll &&
-      !m_scrollManager->middleClickAutoscrollInProgress()) {
+      !m_scrollManager->middleClickAutoscrollInProgress() &&
+      !m_frame->selection().selectedHTMLForClipboard().isEmpty()) {
     if (AutoscrollController* controller =
             m_scrollManager->autoscrollController()) {
       controller->startAutoscrollForSelection(layoutObject);
@@ -741,9 +746,6 @@
     }
   }
 
-  m_frame->eventHandler().selectionController().handleMouseDraggedEvent(
-      event, m_mouseDownPos, m_dragStartPos, m_mousePressNode.get(),
-      m_lastKnownMousePosition);
   return WebInputEventResult::HandledSystem;
 }
 
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp
index e9e73cf..d288022 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp
@@ -126,7 +126,7 @@
   if (!element.layoutObject())
     return false;
 
-  if (!RootScrollerUtil::scrollableAreaForRootScroller(element))
+  if (!RootScrollerUtil::scrollableAreaForRootScroller(&element))
     return false;
 
   if (!fillsViewport(element))
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.cpp b/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.cpp
index 6d80002a..76b11843 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.cpp
@@ -16,23 +16,26 @@
 
 namespace RootScrollerUtil {
 
-ScrollableArea* scrollableAreaForRootScroller(const Element& element) {
-  if (&element == element.document().documentElement()) {
-    if (!element.document().view())
+ScrollableArea* scrollableAreaForRootScroller(const Element* element) {
+  if (!element)
+    return nullptr;
+
+  if (element == element->document().documentElement()) {
+    if (!element->document().view())
       return nullptr;
 
     // For a FrameView, we use the layoutViewport rather than the
     // getScrollableArea() since that could be the RootFrameViewport. The
     // rootScroller's ScrollableArea will be swapped in as the layout viewport
     // in RootFrameViewport so we need to ensure we get the layout viewport.
-    return element.document().view()->layoutViewportScrollableArea();
+    return element->document().view()->layoutViewportScrollableArea();
   }
 
-  if (!element.layoutObject() || !element.layoutObject()->isBox())
+  if (!element->layoutObject() || !element->layoutObject()->isBox())
     return nullptr;
 
   return static_cast<PaintInvalidationCapableScrollableArea*>(
-      toLayoutBoxModelObject(element.layoutObject())->getScrollableArea());
+      toLayoutBoxModelObject(element->layoutObject())->getScrollableArea());
 }
 
 PaintLayer* paintLayerForRootScroller(const Element* element) {
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.h b/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.h
index dc76681..56615c8 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.h
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerUtil.h
@@ -16,7 +16,7 @@
 // Returns the ScrollableArea that's associated with the root scroller element.
 // For the <html> element this will be the FrameView or root
 // PaintLayerScrollableArea.
-ScrollableArea* scrollableAreaForRootScroller(const Element&);
+ScrollableArea* scrollableAreaForRootScroller(const Element*);
 
 // Returns the PaintLayer that'll be used as the root scrolling layer. For the
 // <html> element, this returns the LayoutView's PaintLayer rather than
diff --git a/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp b/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp
index 0552d56..2ea0be3 100644
--- a/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp
@@ -43,11 +43,9 @@
 
 void TopDocumentRootScrollerController::mainFrameViewResized() {
   Element* rootScroller = globalRootScroller();
-  if (!rootScroller)
-    return;
 
   ScrollableArea* area =
-      RootScrollerUtil::scrollableAreaForRootScroller(*rootScroller);
+      RootScrollerUtil::scrollableAreaForRootScroller(rootScroller);
 
   if (!area)
     return;
@@ -90,11 +88,11 @@
     return;
 
   Element* target = findGlobalRootScrollerElement();
-  if (!target || target == m_globalRootScroller)
+  if (target == m_globalRootScroller)
     return;
 
   ScrollableArea* targetScroller =
-      RootScrollerUtil::scrollableAreaForRootScroller(*target);
+      RootScrollerUtil::scrollableAreaForRootScroller(target);
 
   if (!targetScroller)
     return;
@@ -117,9 +115,7 @@
   setNeedsCompositingInputsUpdateOnGlobalRootScroller();
 
   ScrollableArea* oldRootScrollerArea =
-      m_globalRootScroller ? RootScrollerUtil::scrollableAreaForRootScroller(
-                                 *m_globalRootScroller.get())
-                           : nullptr;
+      RootScrollerUtil::scrollableAreaForRootScroller(m_globalRootScroller);
 
   m_globalRootScroller = target;
 
@@ -195,11 +191,8 @@
 }
 
 GraphicsLayer* TopDocumentRootScrollerController::rootScrollerLayer() const {
-  if (!m_globalRootScroller)
-    return nullptr;
-
   ScrollableArea* area =
-      RootScrollerUtil::scrollableAreaForRootScroller(*m_globalRootScroller);
+      RootScrollerUtil::scrollableAreaForRootScroller(m_globalRootScroller);
 
   if (!area)
     return nullptr;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 8e0b0ca..be91e8d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -1780,11 +1780,8 @@
   const TopDocumentRootScrollerController& controller =
       layoutBox()->document().frameHost()->globalRootScrollerController();
 
-  if (!controller.globalRootScroller())
-    return false;
-
   return RootScrollerUtil::scrollableAreaForRootScroller(
-             *controller.globalRootScroller()) == this;
+             controller.globalRootScroller()) == this;
 }
 
 Widget* PaintLayerScrollableArea::getWidget() {
diff --git a/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp b/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp
index e861800..6e672b2 100644
--- a/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp
@@ -23,6 +23,7 @@
 
 #include "core/dom/Document.h"
 #include "core/inspector/ConsoleMessage.h"
+#include "core/layout/svg/LayoutSVGResourceContainer.h"
 #include "core/svg/SVGSVGElement.h"
 #include "core/svg/animation/SMILTimeContainer.h"
 #include "wtf/AutoReset.h"
diff --git a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.idl b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.idl
index bb5bce5..7224955 100644
--- a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.idl
+++ b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.idl
@@ -60,6 +60,10 @@
     void stroke(Path2D path);
     void clip();
     void clip(Path2D path);
+    boolean isPointInPath(unrestricted double x, unrestricted double y, optional CanvasFillRule winding);
+    boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional CanvasFillRule winding);
+    boolean isPointInStroke(unrestricted double x, unrestricted double y);
+    boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
 
     // drawing images
     [CallWith=ExecutionContext, RaisesException] void drawImage(CanvasImageSource image, unrestricted double x, unrestricted double y);
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index 6aaddde..bf45bbd 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -155,7 +155,7 @@
 NotificationContentImage status=stable
 NotificationInlineReplies status=experimental
 Notifications status=stable
-OnDeviceChange status=experimental
+OnDeviceChange status=stable
 OrientationEvent
 OriginTrials status=stable
 // Define a sample API for testing integration with the Origin Trials Framework.
@@ -254,6 +254,7 @@
 WebVTTRegions status=experimental
 V8BasedStructuredClone status=stable
 V8IdleTasks
+VideoFullscreenOrientationLock
 VisibilityChangeOnUnload status=stable
 XSLT status=stable
 smil status=stable
diff --git a/third_party/WebKit/Source/platform/geometry/LayoutRect.cpp b/third_party/WebKit/Source/platform/geometry/LayoutRect.cpp
index ba9589cdf..d3421c4 100644
--- a/third_party/WebKit/Source/platform/geometry/LayoutRect.cpp
+++ b/third_party/WebKit/Source/platform/geometry/LayoutRect.cpp
@@ -152,13 +152,6 @@
   return result;
 }
 
-IntRect enclosingIntRect(const LayoutRect& rect) {
-  IntPoint location = flooredIntPoint(rect.minXMinYCorner());
-  IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner());
-
-  return IntRect(location, maxPoint - location);
-}
-
 LayoutRect enclosingLayoutRect(const FloatRect& rect) {
   LayoutPoint location = flooredLayoutPoint(rect.minXMinYCorner());
   LayoutPoint maxPoint = ceiledLayoutPoint(rect.maxXMaxYCorner());
diff --git a/third_party/WebKit/Source/platform/geometry/LayoutRect.h b/third_party/WebKit/Source/platform/geometry/LayoutRect.h
index 8a5fe24..545802e 100644
--- a/third_party/WebKit/Source/platform/geometry/LayoutRect.h
+++ b/third_party/WebKit/Source/platform/geometry/LayoutRect.h
@@ -278,7 +278,12 @@
                          snapSizeToPixel(rect.height(), rect.y())));
 }
 
-PLATFORM_EXPORT IntRect enclosingIntRect(const LayoutRect&);
+inline IntRect enclosingIntRect(const LayoutRect& rect) {
+  IntPoint location = flooredIntPoint(rect.minXMinYCorner());
+  IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner());
+  return IntRect(location, maxPoint - location);
+}
+
 PLATFORM_EXPORT LayoutRect enclosingLayoutRect(const FloatRect&);
 
 inline IntRect pixelSnappedIntRect(LayoutUnit left,
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
index 0bc3ea179..99960457 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
@@ -304,6 +304,57 @@
   return true;
 }
 
+void ImageDecoder::correctAlphaWhenFrameBufferSawNoAlpha(size_t index) {
+  DCHECK(index < m_frameBufferCache.size());
+  ImageFrame& buffer = m_frameBufferCache[index];
+
+  // When this frame spans the entire image rect we can set hasAlpha to false,
+  // since there are logically no transparent pixels outside of the frame rect.
+  if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
+    buffer.setHasAlpha(false);
+    buffer.setRequiredPreviousFrameIndex(kNotFound);
+  } else if (buffer.requiredPreviousFrameIndex() != kNotFound) {
+    // When the frame rect does not span the entire image rect, and it does
+    // *not* have a required previous frame, the pixels outside of the frame
+    // rect will be fully transparent, so we shoudn't set hasAlpha to false.
+    //
+    // It is a tricky case when the frame does have a required previous frame.
+    // The frame does not have alpha only if everywhere outside its rect
+    // doesn't have alpha.  To know whether this is true, we check the start
+    // state of the frame -- if it doesn't have alpha, we're safe.
+    //
+    // We first check that the required previous frame does not have
+    // DisposeOverWritePrevious as its disposal method - this should never
+    // happen, since the required frame should in that case be the required
+    // frame of this frame's required frame.
+    //
+    // If |prevBuffer| is DisposeNotSpecified or DisposeKeep, |buffer| has no
+    // alpha if |prevBuffer| had no alpha. Since initFrameBuffer() already
+    // copied the alpha state, there's nothing to do here.
+    //
+    // The only remaining case is a DisposeOverwriteBgcolor frame.  If
+    // it had no alpha, and its rect is contained in the current frame's
+    // rect, we know the current frame has no alpha.
+    //
+    // For DisposeNotSpecified, DisposeKeep and DisposeOverwriteBgcolor there
+    // is one situation that is not taken into account - when |prevBuffer|
+    // *does* have alpha, but only in the frame rect of |buffer|, we can still
+    // say that this frame has no alpha. However, to determine this, we
+    // potentially need to analyze all image pixels of |prevBuffer|, which is
+    // too computationally expensive.
+    const ImageFrame* prevBuffer =
+        &m_frameBufferCache[buffer.requiredPreviousFrameIndex()];
+    DCHECK(prevBuffer->getDisposalMethod() !=
+           ImageFrame::DisposeOverwritePrevious);
+
+    if ((prevBuffer->getDisposalMethod() ==
+         ImageFrame::DisposeOverwriteBgcolor) &&
+        !prevBuffer->hasAlpha() &&
+        buffer.originalFrameRect().contains(prevBuffer->originalFrameRect()))
+      buffer.setHasAlpha(false);
+  }
+}
+
 bool ImageDecoder::initFrameBuffer(size_t frameIndex) {
   DCHECK(frameIndex < m_frameBufferCache.size());
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
index 9124bb8..815c946c 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
@@ -370,6 +370,18 @@
   //         false otherwise.
   bool postDecodeProcessing(size_t);
 
+  // The GIF and PNG decoders set the default alpha setting of the ImageFrame to
+  // true. When the frame rect does not contain any (semi-) transparent pixels,
+  // this may need to be changed to false. This depends on whether the required
+  // previous frame adds transparency to the image, outside of the frame rect.
+  // This methods corrects the alpha setting of the frame buffer to false when
+  // the whole frame is opaque.
+  //
+  // This method should be called by the GIF and PNG decoder when the pixels in
+  // the frame rect do *not* contain any transparent pixels. Before calling
+  // this method, the caller must verify that the frame exists.
+  void correctAlphaWhenFrameBufferSawNoAlpha(size_t);
+
   RefPtr<SegmentReader> m_data;  // The encoded data.
   Vector<ImageFrame, 1> m_frameBufferCache;
   const bool m_premultiplyAlpha;
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
index 2693402..54399fc 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -192,43 +192,12 @@
 bool GIFImageDecoder::frameComplete(size_t frameIndex) {
   // Initialize the frame if necessary.  Some GIFs insert do-nothing frames,
   // in which case we never reach haveDecodedRow() before getting here.
-  ImageFrame& buffer = m_frameBufferCache[frameIndex];
   if (!initFrameBuffer(frameIndex))
     return false;  // initFrameBuffer() has already called setFailed().
 
-  buffer.setStatus(ImageFrame::FrameComplete);
-
-  if (!m_currentBufferSawAlpha) {
-    // The whole frame was non-transparent, so it's possible that the entire
-    // resulting buffer was non-transparent, and we can setHasAlpha(false).
-    if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
-      buffer.setHasAlpha(false);
-      buffer.setRequiredPreviousFrameIndex(kNotFound);
-    } else if (buffer.requiredPreviousFrameIndex() != kNotFound) {
-      // Tricky case.  This frame does not have alpha only if everywhere
-      // outside its rect doesn't have alpha.  To know whether this is
-      // true, we check the start state of the frame -- if it doesn't have
-      // alpha, we're safe.
-      const ImageFrame* prevBuffer =
-          &m_frameBufferCache[buffer.requiredPreviousFrameIndex()];
-      ASSERT(prevBuffer->getDisposalMethod() !=
-             ImageFrame::DisposeOverwritePrevious);
-
-      // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then
-      // we can say we have no alpha if that frame had no alpha.  But
-      // since in initFrameBuffer() we already copied that frame's alpha
-      // state into the current frame's, we need do nothing at all here.
-      //
-      // The only remaining case is a DisposeOverwriteBgcolor frame.  If
-      // it had no alpha, and its rect is contained in the current frame's
-      // rect, we know the current frame has no alpha.
-      if ((prevBuffer->getDisposalMethod() ==
-           ImageFrame::DisposeOverwriteBgcolor) &&
-          !prevBuffer->hasAlpha() &&
-          buffer.originalFrameRect().contains(prevBuffer->originalFrameRect()))
-        buffer.setHasAlpha(false);
-    }
-  }
+  m_frameBufferCache[frameIndex].setStatus(ImageFrame::FrameComplete);
+  if (!m_currentBufferSawAlpha)
+    correctAlphaWhenFrameBufferSawNoAlpha(frameIndex);
 
   return true;
 }
diff --git a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
index 5d947a1..cdfd51ed 100644
--- a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
@@ -178,6 +178,10 @@
   RuntimeEnabledFeatures::setNetworkInformationEnabled(enable);
 }
 
+void WebRuntimeFeatures::enableOnDeviceChange(bool enable) {
+  RuntimeEnabledFeatures::setOnDeviceChangeEnabled(enable);
+}
+
 void WebRuntimeFeatures::enableOrientationEvent(bool enable) {
   RuntimeEnabledFeatures::setOrientationEventEnabled(enable);
 }
@@ -356,4 +360,8 @@
   RuntimeEnabledFeatures::setRemotePlaybackEnabled(enable);
 }
 
+void WebRuntimeFeatures::enableVideoFullscreenOrientationLock(bool enable) {
+  RuntimeEnabledFeatures::setVideoFullscreenOrientationLockEnabled(enable);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/wtf/HashCountedSet.h b/third_party/WebKit/Source/wtf/HashCountedSet.h
index fc60257d..6e0db4e 100644
--- a/third_party/WebKit/Source/wtf/HashCountedSet.h
+++ b/third_party/WebKit/Source/wtf/HashCountedSet.h
@@ -53,7 +53,13 @@
   typedef typename ImplType::const_iterator const_iterator;
   typedef typename ImplType::AddResult AddResult;
 
-  HashCountedSet() {}
+  HashCountedSet() {
+    static_assert(Allocator::isGarbageCollected ||
+                      !IsPointerToGarbageCollectedType<Value>::value,
+                  "Cannot put raw pointers to garbage-collected classes into "
+                  "an off-heap HashCountedSet. Use "
+                  "HeapHashCountedSet<Member<T>> instead.");
+  }
 
   void swap(HashCountedSet& other) { m_impl.swap(other.m_impl); }
 
diff --git a/third_party/WebKit/Source/wtf/HashMap.h b/third_party/WebKit/Source/wtf/HashMap.h
index b6f381d..b503ec6 100644
--- a/third_party/WebKit/Source/wtf/HashMap.h
+++ b/third_party/WebKit/Source/wtf/HashMap.h
@@ -78,12 +78,22 @@
   class HashMapValuesProxy;
 
  public:
+  HashMap() {
+    static_assert(Allocator::isGarbageCollected ||
+                      !IsPointerToGarbageCollectedType<KeyArg>::value,
+                  "Cannot put raw pointers to garbage-collected classes into "
+                  "an off-heap HashMap.  Use HeapHashMap<> instead.");
+    static_assert(Allocator::isGarbageCollected ||
+                      !IsPointerToGarbageCollectedType<MappedArg>::value,
+                  "Cannot put raw pointers to garbage-collected classes into "
+                  "an off-heap HashMap.  Use HeapHashMap<> instead.");
+  }
+
   typedef HashTableIteratorAdapter<HashTableType, ValueType> iterator;
   typedef HashTableConstIteratorAdapter<HashTableType, ValueType>
       const_iterator;
   typedef typename HashTableType::AddResult AddResult;
 
- public:
   void swap(HashMap& ref) { m_impl.swap(ref.m_impl); }
 
   unsigned size() const;
diff --git a/third_party/WebKit/Source/wtf/HashSet.h b/third_party/WebKit/Source/wtf/HashSet.h
index 72b97fc..268ec6a3 100644
--- a/third_party/WebKit/Source/wtf/HashSet.h
+++ b/third_party/WebKit/Source/wtf/HashSet.h
@@ -63,7 +63,12 @@
       const_iterator;
   typedef typename HashTableType::AddResult AddResult;
 
-  HashSet() = default;
+  HashSet() {
+    static_assert(Allocator::isGarbageCollected ||
+                      !IsPointerToGarbageCollectedType<ValueArg>::value,
+                  "Cannot put raw pointers to garbage-collected classes into "
+                  "an off-heap HashSet. Use HeapHashSet<Member<T>> instead.");
+  }
   HashSet(const HashSet&) = default;
   HashSet& operator=(const HashSet&) = default;
   HashSet(HashSet&&) = default;
diff --git a/third_party/WebKit/Source/wtf/LinkedHashSet.h b/third_party/WebKit/Source/wtf/LinkedHashSet.h
index 65f5100..0a2235a 100644
--- a/third_party/WebKit/Source/wtf/LinkedHashSet.h
+++ b/third_party/WebKit/Source/wtf/LinkedHashSet.h
@@ -598,8 +598,14 @@
   friend class LinkedHashSet;
 };
 
-template <typename T, typename U, typename V, typename W>
-inline LinkedHashSet<T, U, V, W>::LinkedHashSet() {}
+template <typename T, typename U, typename V, typename Allocator>
+inline LinkedHashSet<T, U, V, Allocator>::LinkedHashSet() {
+  static_assert(
+      Allocator::isGarbageCollected ||
+          !IsPointerToGarbageCollectedType<T>::value,
+      "Cannot put raw pointers to garbage-collected classes into "
+      "an off-heap LinkedHashSet. Use HeapLinkedHashSet<Member<T>> instead.");
+}
 
 template <typename T, typename U, typename V, typename W>
 inline LinkedHashSet<T, U, V, W>::LinkedHashSet(const LinkedHashSet& other)
diff --git a/third_party/WebKit/Source/wtf/ListHashSet.h b/third_party/WebKit/Source/wtf/ListHashSet.h
index c939359f..8522861 100644
--- a/third_party/WebKit/Source/wtf/ListHashSet.h
+++ b/third_party/WebKit/Source/wtf/ListHashSet.h
@@ -733,9 +733,15 @@
   }
 };
 
-template <typename T, size_t inlineCapacity, typename U, typename V>
-inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet()
-    : m_head(nullptr), m_tail(nullptr) {}
+template <typename T, size_t inlineCapacity, typename U, typename Allocator>
+inline ListHashSet<T, inlineCapacity, U, Allocator>::ListHashSet()
+    : m_head(nullptr), m_tail(nullptr) {
+  static_assert(
+      Allocator::isGarbageCollected ||
+          !IsPointerToGarbageCollectedType<T>::value,
+      "Cannot put raw pointers to garbage-collected classes into "
+      "an off-heap ListHashSet. Use HeapListHashSet<Member<T>> instead.");
+}
 
 template <typename T, size_t inlineCapacity, typename U, typename V>
 inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet(
diff --git a/third_party/WebKit/public/web/WebRuntimeFeatures.h b/third_party/WebKit/public/web/WebRuntimeFeatures.h
index fe4b0e0..2f448c9 100644
--- a/third_party/WebKit/public/web/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/web/WebRuntimeFeatures.h
@@ -92,6 +92,7 @@
   BLINK_EXPORT static void enableNotificationConstructor(bool);
   BLINK_EXPORT static void enableNotificationContentImage(bool);
   BLINK_EXPORT static void enableNotifications(bool);
+  BLINK_EXPORT static void enableOnDeviceChange(bool);
   BLINK_EXPORT static void enableOrientationEvent(bool);
   BLINK_EXPORT static void enableOverlayScrollbars(bool);
   BLINK_EXPORT static void enablePagePopup(bool);
@@ -137,6 +138,7 @@
   BLINK_EXPORT static void enableCanvas2dDynamicRenderingModeSwitching(bool);
   BLINK_EXPORT static void enableSendBeaconThrowForBlobWithNonSimpleType(bool);
   BLINK_EXPORT static void enableBackgroundVideoTrackOptimization(bool);
+  BLINK_EXPORT static void enableVideoFullscreenOrientationLock(bool);
 
  private:
   WebRuntimeFeatures();
diff --git a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
index 25ee24b..b1b336d4 100644
--- a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
+++ b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
@@ -109,17 +109,11 @@
                     break;
                 case CustomTabsCallback.NAVIGATION_FINISHED:
                     mPageLoadFinishedMs = SystemClock.uptimeMillis();
-                    if (mIntentSentMs != NONE && mPageLoadStartedMs != NONE) {
-                        if (mFirstContentfulPaintMs != NONE) {
-                            logMetricsAndFinish();
-                        } else {
-                            logMetricsAndFinishDelayed(3000);
-                        }
-                    }
                     break;
                 default:
                     break;
             }
+            if (allSet()) logMetricsAndFinish();
         }
 
         @Override
@@ -132,7 +126,12 @@
             if (mFirstContentfulPaintMs == NONE) {
                 mFirstContentfulPaintMs = navigationStartMs + firstPaintMs;
             }
-            if (mPageLoadFinishedMs != NONE) logMetricsAndFinish();
+            if (allSet()) logMetricsAndFinish();
+        }
+
+        private boolean allSet() {
+            return mIntentSentMs != NONE && mPageLoadStartedMs != NONE
+                    && mFirstContentfulPaintMs != NONE && mPageLoadFinishedMs != NONE;
         }
 
         /** Outputs the available metrics, and die. Unavalaible metrics are set to -1. */
@@ -153,7 +152,7 @@
                 public void run() {
                     logMetricsAndFinish();
                 }
-            }, 3000);
+            }, delayMs);
         }
     }
 
diff --git a/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py b/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
index 219a1751..d4222b21 100755
--- a/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
+++ b/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
@@ -37,6 +37,8 @@
 _CHROME_PACKAGE = 'com.google.android.apps.chrome'
 _COMMAND_LINE_PATH = '/data/local/tmp/chrome-command-line'
 _TEST_APP_PACKAGE_NAME = 'org.chromium.customtabsclient.test'
+_INVALID_VALUE = -1
+
 
 # Command line arguments for Chrome.
 CHROME_ARGS = [
@@ -151,8 +153,9 @@
   assert len(tokens) == 8
   intent_sent_timestamp = int(tokens[4])
   return Result(bool(tokens[0]), tokens[1], int(tokens[2]), int(tokens[3]),
-                int(tokens[5]) - intent_sent_timestamp,
-                int(tokens[6]) - intent_sent_timestamp, int(tokens[7]))
+                max(_INVALID_VALUE, int(tokens[5]) - intent_sent_timestamp),
+                max(_INVALID_VALUE, int(tokens[6]) - intent_sent_timestamp),
+                max(_INVALID_VALUE, int(tokens[7]) - intent_sent_timestamp))
 
 
 def LoopOnDevice(device, configs, output_filename, wpr_archive_path=None,
@@ -221,9 +224,9 @@
   result['speculation_mode'] = data[:, 1]
   result['delay_to_may_launch_url'] = data[:, 2]
   result['delay_to_launch_url'] = data[:, 3]
-  result['commit'] = data[:, 5] - data[:, 4]
-  result['plt'] = data[:, 6] - data[:, 4]
-  result['first_contentful_paint'] = data[7]
+  result['commit'] = data[:, 4]
+  result['plt'] = data[:, 5]
+  result['first_contentful_paint'] = data[:, 6]
   return result
 
 
diff --git a/tools/gn/input_file_manager.cc b/tools/gn/input_file_manager.cc
index c695655..9fd3def 100644
--- a/tools/gn/input_file_manager.cc
+++ b/tools/gn/input_file_manager.cc
@@ -147,9 +147,7 @@
       }
     }
   }
-  g_scheduler->pool()->PostWorkerTaskWithShutdownBehavior(
-      FROM_HERE, schedule_this,
-      base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+  g_scheduler->ScheduleWork(schedule_this);
   return true;
 }
 
diff --git a/tools/gn/scheduler.h b/tools/gn/scheduler.h
index 67650d8..e20bced 100644
--- a/tools/gn/scheduler.h
+++ b/tools/gn/scheduler.h
@@ -33,7 +33,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
     return main_loop_.task_runner();
   }
-  base::SequencedWorkerPool* pool() { return pool_.get(); }
 
   InputFileManager* input_file_manager() { return input_file_manager_.get(); }
 
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index c5005129..626d01fc 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -352,6 +352,11 @@
   "devtools_resources.grd": {
     "includes": [28450],
   },
+
+  "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": {
+    "includes": [28600],
+	"messages": [28650],
+  },
   # END "everything else" section.
   # Everything but chrome/, components/, content/, and ios/
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 8f13b15..8c9ca90 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -125,7 +125,7 @@
 
       'CrWinClang(dbg)': 'clang_debug_bot_minimal_symbols_x86',
       'CrWinClang64': 'clang_official_release_bot_minimal_symbols',
-      'CrWinClang64(dll)': 'clang_minimal_symbols_shared_release_bot',
+      'CrWinClang64(dll)': 'clang_shared_release_bot',
       'CrWinClangGoma': 'clang_official_optimize_release_bot_minimal_symbols_x86',
       'CrWinGoma': 'release_bot_x86',
       'CrWinGoma(dll)': 'shared_release_bot_x86',
@@ -146,7 +146,7 @@
       'ClangToTWin(dll)': 'clang_tot_minimal_symbols_shared_release_x86',
       'ClangToTWin64': 'clang_tot_official_minimal_symbols_static_release',
       'ClangToTWin64(dbg)': 'clang_tot_minimal_symbols_shared_debug',
-      'ClangToTWin64(dll)': 'clang_tot_minimal_symbols_shared_release',
+      'ClangToTWin64(dll)': 'clang_tot_shared_release',
       'ClangToTiOS': 'ios',
       'Closure Compilation Linux': 'closure_compilation',
       'CrWinAsan': 'asan_clang_fuzzer_static_v8_heap_x86_full_symbols_release',
@@ -1034,8 +1034,8 @@
       'clang', 'official', 'release_trybot', 'x86',
     ],
 
-    'clang_minimal_symbols_shared_release_bot': [
-      'clang', 'minimal_symbols', 'shared_release_bot',
+    'clang_shared_release_bot': [
+      'clang', 'shared_release_bot',
     ],
 
     'clang_tot_asan_lsan_static_release': [
@@ -1084,6 +1084,10 @@
       'clang_tot', 'minimal_symbols', 'shared', 'release',
     ],
 
+    'clang_tot_shared_release': [
+      'clang_tot', 'shared', 'release',
+    ],
+
     'clang_tot_minimal_symbols_shared_release_x86': [
       'clang_tot', 'minimal_symbols', 'shared', 'release', 'x86',
     ],
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 199b3e4..486a2df 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -31341,6 +31341,14 @@
   </summary>
 </histogram>
 
+<histogram name="Net.HttpJob.PrefilterBytesRead" units="bytes">
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Total prefilter (e.g., before decompression) bytes read for an HttpJob
+    request.
+  </summary>
+</histogram>
+
 <histogram name="Net.HttpJob.TotalTime" units="ms">
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -92435,6 +92443,7 @@
   <int value="-741806604" label="DownloadsUi:disabled"/>
   <int value="-723224470" label="enable-password-force-saving:enabled"/>
   <int value="-716953514" label="disable-password-separated-signin-flow"/>
+  <int value="-714710496" label="VideoFullscreenOrientationLock:disabled"/>
   <int value="-711890895" label="enable-website-settings-manager"/>
   <int value="-699767107" label="enable-sync-app-list"/>
   <int value="-697751423" label="disable-quickoffice-component-app"/>
@@ -92641,6 +92650,7 @@
   <int value="379326303" label="enable-add-to-shelf"/>
   <int value="379428799" label="security-chip-animation"/>
   <int value="385969127" label="disable-win32k-lockdown"/>
+  <int value="387178525" label="VideoFullscreenOrientationLock:enabled"/>
   <int value="400322063" label="ash-disable-screen-orientation-lock"/>
   <int value="401983950" label="enable-spdy4"/>
   <int value="402143634" label="enable-search-button-in-omnibox-always"/>
@@ -109025,6 +109035,12 @@
   <affected-histogram name="HttpCache.PercentBeforeSend"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="HttpJobBytes" separator=".">
+  <suffix name="Cache" label="For requests served from the cache."/>
+  <suffix name="Net" label="For requests served from the network."/>
+  <affected-histogram name="Net.HttpJob.PrefilterBytesRead"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="HttpPipeliningCompatibility">
   <suffix name="disable_test" label="Do nothing"/>
   <suffix name="enable_test" label="Test connection for HTTP pipelining"/>
diff --git a/tools/resource_prefetch_predictor/prefetch_benchmark.py b/tools/resource_prefetch_predictor/prefetch_benchmark.py
index 95be021..518af04 100755
--- a/tools/resource_prefetch_predictor/prefetch_benchmark.py
+++ b/tools/resource_prefetch_predictor/prefetch_benchmark.py
@@ -48,6 +48,8 @@
   parser.add_argument('--url', help='URL to load.')
   parser.add_argument('--prefetch_delay_ms',
                       help='Prefetch delay in ms. -1 to disable prefetch.')
+  parser.add_argument('--output_filename',
+                      help='CSV file to append the result to.')
   return parser
 
 
@@ -97,9 +99,9 @@
   result = customtabs_benchmark.RunOnce(
       device, url, warmup=True, speculation_mode=prefetch_mode,
       delay_to_may_launch_url=2000,
-      delay_to_launch_url=max(0, prefetch_delay_ms), cold=False,
+      delay_to_launch_url=prefetch_delay_ms, cold=False,
       chrome_args=chrome_args, reset_chrome_state=False)
-  print customtabs_benchmark.ParseResult(result)
+  return customtabs_benchmark.ParseResult(result)
 
 
 def main():
@@ -115,7 +117,10 @@
     sys.exit(1)
 
   _Setup(device, args.database)
-  _Go(device, args.url, int(args.prefetch_delay_ms))
+  result = _Go(device, args.url, int(args.prefetch_delay_ms))
+  print result
+  with open(args.output_filename, 'a') as f:
+    f.write(','.join(str(x) for x in result) + '\n')
 
 
 if __name__ == '__main__':
diff --git a/ui/aura/mus/window_compositor_frame_sink.cc b/ui/aura/mus/window_compositor_frame_sink.cc
index 21f623a..7d252b89 100644
--- a/ui/aura/mus/window_compositor_frame_sink.cc
+++ b/ui/aura/mus/window_compositor_frame_sink.cc
@@ -113,7 +113,6 @@
 
 void WindowCompositorFrameSink::WillDrawSurface() {
   // TODO(fsamuel, staraz): Implement this.
-  NOTIMPLEMENTED();
 }
 
 void WindowCompositorFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) {
diff --git a/ui/gfx/vector_icons/BUILD.gn b/ui/gfx/vector_icons/BUILD.gn
index 77a55f86..0645973a 100644
--- a/ui/gfx/vector_icons/BUILD.gn
+++ b/ui/gfx/vector_icons/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chrome_build.gni")
+
 action("aggregate_vector_icons") {
   visibility = [ ":*" ]
 
@@ -26,7 +28,6 @@
     "check_circle.icon",
     "checkbox_active.icon",
     "checkbox_normal.icon",
-    "chrome_product.icon",
     "close_all.icon",
     "code.icon",
     "combobox_arrow_mac_disabled.icon",
@@ -173,6 +174,7 @@
     "window_control_right_snapped.icon",
     "zoom_minus.icon",
     "zoom_plus.icon",
+    "${branding_path_component}/product.icon",
   ]
 
   output_cc = "$target_gen_dir/vector_icons.cc"
diff --git a/ui/gfx/vector_icons/chrome_product.icon b/ui/gfx/vector_icons/chromium/product.icon
similarity index 100%
rename from ui/gfx/vector_icons/chrome_product.icon
rename to ui/gfx/vector_icons/chromium/product.icon
diff --git a/ui/gfx/vector_icons/google_chrome/product.icon b/ui/gfx/vector_icons/google_chrome/product.icon
new file mode 100644
index 0000000..ec96ab98
--- /dev/null
+++ b/ui/gfx/vector_icons/google_chrome/product.icon
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 24,
+// Red
+PATH_COLOR_ARGB, 0xFF, 0xDB, 0x44, 0x37,
+MOVE_TO, 12, 7.5f,
+R_H_LINE_TO, 8.9f,
+CUBIC_TO, 19.3f, 4.2f, 15.9f, 2, 12, 2,
+CUBIC_TO, 8.9f, 2, 6.1f, 3.4f, 4.3f, 5.6f,
+R_LINE_TO, 3.3f, 5.7f,
+R_CUBIC_TO, 0.3f, -2.1f, 2.2f, -3.8f, 4.4f, -3.8f,
+CLOSE,
+NEW_PATH,
+// Green
+PATH_COLOR_ARGB, 0xFF, 0x0F, 0x9D, 0x58,
+// R_MOVE_TO, 0, 9,
+MOVE_TO, 12, 16.5f,
+R_CUBIC_TO, -1.7f, 0, -3.1f, -0.9f, -3.9f, -2.3f,
+LINE_TO, 3.6f, 6.5f,
+CUBIC_TO, 2.6f, 8.1f, 2, 10, 2, 12,
+R_CUBIC_TO, 0, 5, 3.6f, 9.1f, 8.4f, 9.9f,
+R_LINE_TO, 3.3f, -5.7f,
+R_CUBIC_TO, -0.6f, 0.2f, -1.1f, 0.3f, -1.7f, 0.3f,
+CLOSE,
+NEW_PATH,
+// Yellow
+PATH_COLOR_ARGB, 0xFF, 0xFF, 0xCD, 0x40,
+MOVE_TO, 16.5f, 12,
+R_CUBIC_TO, 0, 0.8f, -0.2f, 1.6f, -0.6f, 2.2f,
+LINE_TO, 11.4f, 22,
+R_H_LINE_TO, 0.6f,
+R_CUBIC_TO, 5.5f, 0, 10, -4.5f, 10, -10,
+R_CUBIC_TO, 0, -1.2f, -0.2f, -2.4f, -0.6f, -3.5f,
+R_H_LINE_TO, -6.6f,
+R_CUBIC_TO, 1, 0.8f, 1.7f, 2.1f, 1.7f, 3.5f,
+CLOSE,
+NEW_PATH,
+// Blue
+PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
+CIRCLE, 12, 12, 3.5,
+END
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 5050c891..e7705f6 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -154,6 +154,8 @@
 
   if (use_egl) {
     sources += [
+      "angle_platform_impl.cc",
+      "angle_platform_impl.h",
       "egl_util.cc",
       "egl_util.h",
       "gl_bindings_autogen_egl.cc",
@@ -219,8 +221,6 @@
   }
   if (is_win) {
     sources += [
-      "angle_platform_impl.cc",
-      "angle_platform_impl.h",
       "gl_bindings_autogen_wgl.cc",
       "gl_bindings_autogen_wgl.h",
       "gl_context_wgl.cc",
diff --git a/ui/gl/egl_api_unittest.cc b/ui/gl/egl_api_unittest.cc
index b9dbd15a..772cf06 100644
--- a/ui/gl/egl_api_unittest.cc
+++ b/ui/gl/egl_api_unittest.cc
@@ -25,6 +25,7 @@
     g_driver_egl.fn.eglGetCurrentDisplayFn = &FakeGetCurrentDisplay;
     g_driver_egl.fn.eglGetDisplayFn = &FakeGetDisplay;
     g_driver_egl.fn.eglGetErrorFn = &FakeGetError;
+    g_driver_egl.fn.eglGetProcAddressFn = &FakeGetProcAddress;
   }
 
   void TearDown() override {
@@ -82,6 +83,11 @@
     return EGL_SUCCESS;
   }
 
+  static __eglMustCastToProperFunctionPointerType GL_BINDING_CALL
+  FakeGetProcAddress(const char* procname) {
+    return nullptr;
+  }
+
   std::pair<const char*, const char*> GetExtensions() {
     return std::make_pair(
         api_->eglQueryStringFn(EGL_NO_DISPLAY, EGL_EXTENSIONS),
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 54ffb7dd..56032ca9 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -21,6 +22,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/egl_util.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_context_egl.h"
@@ -134,6 +136,10 @@
 bool g_egl_surface_orientation_supported = false;
 bool g_use_direct_composition = false;
 
+base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
+    LAZY_INSTANCE_INITIALIZER;
+ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
+
 EGLDisplay GetPlatformANGLEDisplay(EGLNativeDisplayType native_display,
                                    EGLenum platform_type,
                                    bool warpDevice) {
@@ -533,7 +539,11 @@
 }
 
 // static
-void GLSurfaceEGL::ResetForTesting() {
+void GLSurfaceEGL::ShutdownOneOff() {
+  if (g_angle_platform_shutdown) {
+    g_angle_platform_shutdown();
+  }
+
   if (g_display != EGL_NO_DISPLAY)
     eglTerminate(g_display);
   g_display = EGL_NO_DISPLAY;
@@ -607,6 +617,17 @@
 
   g_native_display = native_display;
 
+  // Init ANGLE platform here, before we call GetPlatformDisplay().
+  ANGLEPlatformInitializeFunc angle_platform_init =
+      reinterpret_cast<ANGLEPlatformInitializeFunc>(
+          eglGetProcAddress("ANGLEPlatformInitialize"));
+  if (angle_platform_init) {
+    angle_platform_init(&g_angle_platform_impl.Get());
+
+    g_angle_platform_shutdown = reinterpret_cast<ANGLEPlatformShutdownFunc>(
+        eglGetProcAddress("ANGLEPlatformShutdown"));
+  }
+
   // If EGL_EXT_client_extensions not supported this call to eglQueryString
   // will return NULL.
   const char* client_extensions =
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index 1a0e7055..dc000eba 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -78,7 +78,7 @@
   GLSurface::Format GetFormat() override;
 
   static bool InitializeOneOff(EGLNativeDisplayType native_display);
-  static void ResetForTesting();
+  static void ShutdownOneOff();
   static EGLDisplay GetHardwareDisplay();
   static EGLDisplay InitializeDisplay(EGLNativeDisplayType native_display);
   static EGLNativeDisplayType GetNativeDisplay();
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc
index d528ad4..f338cc0 100644
--- a/ui/gl/init/gl_initializer_android.cc
+++ b/ui/gl/init/gl_initializer_android.cc
@@ -97,6 +97,7 @@
 }
 
 void ShutdownGLPlatform() {
+  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsOSMESA();
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc
index 624e43b..213ef8b 100644
--- a/ui/gl/init/gl_initializer_ozone.cc
+++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -92,6 +92,7 @@
 }
 
 void ShutdownGLPlatform() {
+  GLSurfaceEGL::ShutdownOneOff();
   if (HasGLOzone()) {
     GetGLOzone()->ShutdownGL();
     return;
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index 5b0fc28..89a3586 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/native_library.h"
 #include "base/path_service.h"
@@ -19,8 +18,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/windows_version.h"
-// TODO(jmadill): Apply to all platforms eventually
-#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_egl_api_implementation.h"
 #include "ui/gl/gl_features.h"
@@ -38,12 +35,6 @@
 
 const wchar_t kD3DCompiler[] = L"D3DCompiler_47.dll";
 
-// TODO(jmadill): Apply to all platforms eventually
-base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
-    LAZY_INSTANCE_INITIALIZER;
-
-ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
-
 bool LoadD3DXLibrary(const base::FilePath& module_path,
                      const base::FilePath::StringType& name) {
   base::NativeLibrary library =
@@ -152,22 +143,6 @@
   }
 #endif
 
-  if (!using_swift_shader) {
-    // Init ANGLE platform here, before we call GetPlatformDisplay().
-    // TODO(jmadill): Apply to all platforms eventually
-    ANGLEPlatformInitializeFunc angle_platform_init =
-        reinterpret_cast<ANGLEPlatformInitializeFunc>(
-            base::GetFunctionPointerFromNativeLibrary(
-                gles_library, "ANGLEPlatformInitialize"));
-    if (angle_platform_init) {
-      angle_platform_init(&g_angle_platform_impl.Get());
-
-      g_angle_platform_shutdown = reinterpret_cast<ANGLEPlatformShutdownFunc>(
-          base::GetFunctionPointerFromNativeLibrary(gles_library,
-                                                    "ANGLEPlatformShutdown"));
-    }
-  }
-
   GLGetProcAddressProc get_proc_address =
       reinterpret_cast<GLGetProcAddressProc>(
           base::GetFunctionPointerFromNativeLibrary(egl_library,
@@ -320,11 +295,7 @@
 }
 
 void ShutdownGLPlatform() {
-  // TODO(jmadill): Apply to all platforms eventually
-  if (g_angle_platform_shutdown) {
-    g_angle_platform_shutdown();
-  }
-
+  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsOSMESA();
diff --git a/ui/gl/init/gl_initializer_x11.cc b/ui/gl/init/gl_initializer_x11.cc
index a41e5ce..27fca039 100644
--- a/ui/gl/init/gl_initializer_x11.cc
+++ b/ui/gl/init/gl_initializer_x11.cc
@@ -186,6 +186,7 @@
 }
 
 void ShutdownGLPlatform() {
+  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsGLX();
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc
index d63b305..7abf320 100644
--- a/ui/ozone/common/gl_ozone_egl.cc
+++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -41,7 +41,7 @@
 }
 
 void GLOzoneEGL::ShutdownGL() {
-  gl::GLSurfaceEGL::ResetForTesting();
+  gl::GLSurfaceEGL::ShutdownOneOff();
   gl::ClearBindingsGL();
   gl::ClearBindingsEGL();
 }
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index 7d61c8c5..5eb80096 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -193,7 +193,9 @@
   // Returns a blocking pool task runner given a TaskRunnerType.
   virtual scoped_refptr<base::TaskRunner> GetBlockingPoolTaskRunner();
 
-  // Returns the insets that should be applied around a DialogClientView.
+  // Returns the insets that should be applied around a DialogClientView. Note
+  // that the top inset is used for the distance between the buttons and the
+  // DialogClientView's content view.
   virtual gfx::Insets GetDialogButtonInsets();
 
   // Returns the spacing between a pair of related horizontal buttons, used for
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 2950d2a0..cf50c1c6 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -170,7 +170,7 @@
 
   int buttons_height = GetButtonsAndExtraViewRowHeight();
   if (buttons_height != 0) {
-    size.Enlarge(0, buttons_height + kRelatedControlVerticalSpacing);
+    size.Enlarge(0, buttons_height + GetButtonsAndExtraViewRowTopPadding());
     // Inset the buttons and extra view.
     const gfx::Insets insets = GetButtonRowInsets();
     size.Enlarge(insets.width(), insets.height());
@@ -212,7 +212,7 @@
           GetDialogDelegate()->GetExtraViewPadding(&custom_padding)) {
         // The call to LayoutButton() will already have accounted for some of
         // the padding.
-        custom_padding -= kRelatedButtonHSpacing;
+        custom_padding -= GetButtonsAndExtraViewRowTopPadding();
         row_bounds.set_width(row_bounds.width() - custom_padding);
       }
       row_bounds.set_width(std::min(row_bounds.width(),
@@ -220,17 +220,8 @@
       extra_view_->SetBoundsRect(row_bounds);
     }
 
-    if (height > 0) {
-      // If the ViewsDelegate supplies a non-zero top inset, use that;
-      // otherwise, use kRelatedControlVerticalSpacing.
-      int spacing =
-          ViewsDelegate::GetInstance()
-              ? ViewsDelegate::GetInstance()->GetDialogButtonInsets().top()
-              : 0;
-      if (!spacing)
-        spacing = kRelatedControlVerticalSpacing;
-      bounds.Inset(0, 0, 0, height + spacing);
-    }
+    if (height > 0)
+      bounds.Inset(0, 0, 0, height + GetButtonsAndExtraViewRowTopPadding());
   }
 
   // Layout the contents view to the top and side edges of the contents bounds.
@@ -365,6 +356,18 @@
                                                 : button_row_insets_;
 }
 
+int DialogClientView::GetButtonsAndExtraViewRowTopPadding() const {
+  int spacing = button_row_insets_.top();
+  // Some subclasses of DialogClientView, in order to do their own layout, set
+  // button_row_insets_ to gfx::Insets(). To avoid breaking behavior of those
+  // dialogs, supplying 0 for the top inset of the row falls back to
+  // kRelatedControlVerticalSpacing.
+  // TODO(ellyjones): Figure out a more principled way to approach that issue.
+  if (!spacing)
+    spacing = kRelatedControlVerticalSpacing;
+  return spacing;
+}
+
 void DialogClientView::SetupFocusChain() {
   // Create a vector of child views in the order of intended focus.
   std::vector<View*> child_views;
diff --git a/ui/views/window/dialog_client_view.h b/ui/views/window/dialog_client_view.h
index 3fb88c08b..1d2b3de 100644
--- a/ui/views/window/dialog_client_view.h
+++ b/ui/views/window/dialog_client_view.h
@@ -99,6 +99,10 @@
   // Returns the insets for the buttons and extra view.
   gfx::Insets GetButtonRowInsets() const;
 
+  // Returns the vertical padding to place between the contents view and the
+  // buttons/extra view.
+  int GetButtonsAndExtraViewRowTopPadding() const;
+
   // How much to inset the button row.
   gfx::Insets button_row_insets_;