diff --git a/DEPS b/DEPS
index a0fb00a..c821913 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': 'f3b46e5193da843cac07d42fdc36c76c05f7fa77',
+  'skia_revision': 'cb2e235e6fb5d9230c41ccf58b865c90ff928f67',
   # 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': '25bd579f2ef421e2792c7fd6926f7ba80fd31a12',
+  'v8_revision': 'ec22a93f56c44d4e435e1e9fe60fda3487f9cf64',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'ccd5be05f61c85754daf5c8155f4932f6d35a55a',
+  'pdfium_revision': '4793f3474f2778dbbd225d797f011db0f45e0953',
   # 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': '8e06404d6939def91d0020d5082dca52546e3a31',
+  'catapult_revision': '1831170b3594c3f34f84a6e9825637f442a2df80',
   # 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/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index b065e96..03d2e7f 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -116,11 +116,11 @@
     cc::PaintFlags background_flags;
     background_flags.setAntiAlias(true);
     background_flags.setColor(color_);
-    gfx::Insets insets = GetMirroredBackgroundInsets(
-        tray_background_view_->shelf()->IsHorizontalAlignment());
-    gfx::Rect bounds = view->GetLocalBounds();
-    bounds.Inset(insets);
-    canvas->DrawRoundRect(bounds, kTrayRoundedBorderRadius, background_flags);
+
+    gfx::Rect bounds = tray_background_view_->GetBackgroundBounds();
+    const float dsf = canvas->UndoDeviceScaleFactor();
+    canvas->DrawRoundRect(gfx::ScaleToRoundedRect(bounds, dsf),
+                          kTrayRoundedBorderRadius * dsf, background_flags);
   }
 
   // Reference to the TrayBackgroundView for which this is a background.
@@ -169,9 +169,8 @@
   set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity);
 
   SetLayoutManager(new views::FillLayout);
+  SetBackground(std::unique_ptr<views::Background>(background_));
 
-  tray_container_->SetBackground(
-      std::unique_ptr<views::Background>(background_));
   AddChildView(tray_container_);
 
   tray_event_filter_.reset(new TrayEventFilter);
@@ -486,6 +485,13 @@
   GetBubbleView()->GetWidget()->SetBounds(target_bounds);
 }
 
+gfx::Rect TrayBackgroundView::GetBackgroundBounds() const {
+  gfx::Insets insets = GetBackgroundInsets();
+  gfx::Rect bounds = GetLocalBounds();
+  bounds.Inset(insets);
+  return bounds;
+}
+
 std::unique_ptr<views::InkDropMask> TrayBackgroundView::CreateInkDropMask()
     const {
   return base::MakeUnique<views::RoundRectInkDropMask>(
@@ -511,6 +517,10 @@
   ActionableView::HandlePerformActionResult(action_performed, event);
 }
 
+views::PaintInfo::ScaleType TrayBackgroundView::GetPaintScaleType() const {
+  return views::PaintInfo::ScaleType::kUniformScaling;
+}
+
 gfx::Insets TrayBackgroundView::GetBackgroundInsets() const {
   gfx::Insets insets =
       GetMirroredBackgroundInsets(shelf_->IsHorizontalAlignment());
@@ -525,11 +535,5 @@
   return insets;
 }
 
-gfx::Rect TrayBackgroundView::GetBackgroundBounds() const {
-  gfx::Insets insets = GetBackgroundInsets();
-  gfx::Rect bounds = GetLocalBounds();
-  bounds.Inset(insets);
-  return bounds;
-}
 
 }  // namespace ash
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index cbd40eb6f..357ca950 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -126,6 +126,10 @@
   // |close_bubble| is set.
   void AnimateToTargetBounds(const gfx::Rect& target_bounds, bool close_bubble);
 
+  // Helper function that calculates background bounds relative to local bounds
+  // based on background insets returned from GetBackgroundInsets().
+  gfx::Rect GetBackgroundBounds() const;
+
  protected:
   // ActionableView:
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
@@ -133,6 +137,7 @@
   bool PerformAction(const ui::Event& event) override;
   void HandlePerformActionResult(bool action_performed,
                                  const ui::Event& event) override;
+  views::PaintInfo::ScaleType GetPaintScaleType() const override;
 
   TrayDragController* drag_controller() { return drag_controller_.get(); }
   void set_drag_controller(
@@ -154,9 +159,6 @@
   // Helper function that calculates background insets relative to local bounds.
   gfx::Insets GetBackgroundInsets() const;
 
-  // Helper function that calculates background bounds relative to local bounds
-  // based on background insets returned from GetBackgroundInsets().
-  gfx::Rect GetBackgroundBounds() const;
 
   // The shelf containing the system tray for this view.
   Shelf* shelf_;
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 392673b..4294c79 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -478,7 +478,12 @@
           options.incremental_install_json_path)
       deps_info['enable_relocation_packing'] = options.enable_relocation_packing
 
-  if options.type in ('java_binary', 'java_library', 'android_apk', 'dist_jar'):
+  requires_javac_classpath = options.type in (
+      'java_binary', 'java_library', 'android_apk', 'dist_jar')
+  requires_full_classpath = (
+      options.type == 'java_prebuilt' or requires_javac_classpath)
+
+  if requires_javac_classpath:
     # Classpath values filled in below (after applying tested_apk_config).
     config['javac'] = {}
 
@@ -574,8 +579,9 @@
   if options.type in ['android_apk', 'deps_dex']:
     deps_dex_files = [c['dex_path'] for c in all_library_deps]
 
-  if options.type in ('java_binary', 'java_library', 'android_apk', 'dist_jar'):
+  if requires_javac_classpath:
     javac_classpath = [c['jar_path'] for c in direct_library_deps]
+  if requires_full_classpath:
     java_full_classpath = [c['jar_path'] for c in all_library_deps]
 
     if options.extra_classpath_jars:
@@ -653,7 +659,7 @@
     dex_config = config['final_dex']
     dex_config['dependency_dex_files'] = deps_dex_files
 
-  if options.type in ('java_binary', 'java_library', 'android_apk', 'dist_jar'):
+  if requires_javac_classpath:
     config['javac']['classpath'] = javac_classpath
     javac_interface_classpath = [
         _AsInterfaceJar(p) for p in javac_classpath
@@ -661,6 +667,7 @@
     javac_interface_classpath += deps_info.get('extra_classpath_jars', [])
     config['javac']['interface_classpath'] = javac_interface_classpath
 
+  if requires_full_classpath:
     deps_info['java'] = {
       'full_classpath': java_full_classpath,
     }
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 3707e11c..5b8e8458 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1172,7 +1172,7 @@
     _output_jar_path = invoker.output_jar_path
 
     _enable_assert =
-        defined(invoker.supports_android) && invoker.supports_android &&
+        defined(invoker.enable_build_hooks) && invoker.enable_build_hooks &&
         (is_java_debug || dcheck_always_on)
 
     _desugar = defined(invoker.supports_android) && invoker.supports_android
@@ -2030,6 +2030,13 @@
       _dex_target_name = "${_template_name}__dex"
     }
 
+    _enable_build_hooks =
+        _supports_android &&
+        (!defined(invoker.no_build_hooks) || !invoker.no_build_hooks)
+    if (_enable_build_hooks) {
+      _deps += [ "//build/android/buildhooks:build_hooks_java" ]
+    }
+
     write_build_config(_build_config_target_name) {
       type = "java_prebuilt"
       is_prebuilt_binary = defined(invoker.main_class)
@@ -2074,6 +2081,7 @@
       }
 
       supports_android = _supports_android
+      enable_build_hooks = _enable_build_hooks
       build_config = _build_config
       input_jar_path = invoker.jar_path
       output_jar_path = _jar_path
@@ -2355,6 +2363,8 @@
                                "alternative_android_sdk_ijar",
                                "alternative_android_sdk_ijar_dep",
                                "alternative_android_sdk_jar",
+                               "enable_build_hooks",
+                               "enable_build_hooks_android",
                                "jar_excluded_patterns",
                              ])
       supports_android = _supports_android
@@ -2466,7 +2476,12 @@
       _accumulated_deps += [ "//build/android/buildhooks:build_hooks_java" ]
     }
 
-    _enable_build_hooks_android = _enable_build_hooks && _requires_android
+    # Some testonly targets use their own resources and the code being
+    # tested will use custom resources so there's no need to enable this
+    # for testonly targets.
+    _enable_build_hooks_android =
+        _enable_build_hooks && _requires_android &&
+        (!defined(invoker.testonly) || !invoker.testonly)
     if (_enable_build_hooks_android) {
       _accumulated_deps +=
           [ "//build/android/buildhooks:build_hooks_android_java" ]
@@ -2630,6 +2645,7 @@
       supports_android = _supports_android
       requires_android = _requires_android
       emma_instrument = _emma_instrument
+      enable_build_hooks = _enable_build_hooks
       deps = _accumulated_deps
     }
     _accumulated_deps += [ ":$_compile_java_target" ]
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index a9717e7..fa7861d 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -3054,6 +3054,7 @@
                                [
                                  "create_srcjar",
                                  "deps",
+                                 "testonly",
                                ])
         if (!defined(deps)) {
           deps = []
@@ -3110,6 +3111,7 @@
                                  "jar_excluded_patterns",
                                  "proguard_configs",
                                  "requires_android",
+                                 "testonly",
                                ])
         if (!defined(deps)) {
           deps = []
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 3f334cd..850fe8c 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -109,7 +109,6 @@
   // reference to us.
   DCHECK(!layer_tree_host());
 
-  RemoveFromScrollTree();
   RemoveFromClipTree();
 
   // Remove the parent reference from all children and dependents.
@@ -721,32 +720,12 @@
   if (inputs_.scroll_parent == parent)
     return;
 
-  if (inputs_.scroll_parent)
-    inputs_.scroll_parent->RemoveScrollChild(this);
-
   inputs_.scroll_parent = parent;
 
-  if (inputs_.scroll_parent)
-    inputs_.scroll_parent->AddScrollChild(this);
-
   SetPropertyTreesNeedRebuild();
   SetNeedsCommit();
 }
 
-void Layer::AddScrollChild(Layer* child) {
-  if (!scroll_children_)
-    scroll_children_.reset(new std::set<Layer*>);
-  scroll_children_->insert(child);
-  SetNeedsCommit();
-}
-
-void Layer::RemoveScrollChild(Layer* child) {
-  scroll_children_->erase(child);
-  if (scroll_children_->empty())
-    scroll_children_ = nullptr;
-  SetNeedsCommit();
-}
-
 void Layer::SetClipParent(Layer* ancestor) {
   DCHECK(IsPropertyChangeAllowed());
   if (inputs_.clip_parent == ancestor)
@@ -1392,17 +1371,6 @@
   return nullptr;
 }
 
-void Layer::RemoveFromScrollTree() {
-  if (scroll_children_.get()) {
-    std::set<Layer*> copy = *scroll_children_;
-    for (std::set<Layer*>::iterator it = copy.begin(); it != copy.end(); ++it)
-      (*it)->SetScrollParent(nullptr);
-  }
-
-  DCHECK(!scroll_children_);
-  SetScrollParent(nullptr);
-}
-
 void Layer::RemoveFromClipTree() {
   if (clip_children_.get()) {
     std::set<Layer*> copy = *clip_children_;
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 7834546..2e61192 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -201,11 +201,6 @@
 
   Layer* scroll_parent() { return inputs_.scroll_parent; }
 
-  std::set<Layer*>* scroll_children() { return scroll_children_.get(); }
-  const std::set<Layer*>* scroll_children() const {
-    return scroll_children_.get();
-  }
-
   void SetClipParent(Layer* ancestor);
 
   Layer* clip_parent() { return inputs_.clip_parent; }
@@ -496,9 +491,6 @@
 
   bool ScrollOffsetAnimationWasInterrupted() const;
 
-  void AddScrollChild(Layer* child);
-  void RemoveScrollChild(Layer* child);
-
   void AddClipChild(Layer* child);
   void RemoveClipChild(Layer* child);
 
@@ -508,10 +500,6 @@
   // This should only be called from RemoveFromParent().
   void RemoveChildOrDependent(Layer* child);
 
-  // If this layer has a scroll parent, it removes |this| from its list of
-  // scroll children.
-  void RemoveFromScrollTree();
-
   // If this layer has a clip parent, it removes |this| from its list of clip
   // children.
   void RemoveFromClipTree();
@@ -668,7 +656,6 @@
   // This value is valid only when LayerTreeHost::has_copy_request() is true
   bool subtree_has_copy_request_ : 1;
   SkColor safe_opaque_background_color_;
-  std::unique_ptr<std::set<Layer*>> scroll_children_;
 
   std::unique_ptr<std::set<Layer*>> clip_children_;
 
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 4193566..2cc1480 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -94,11 +94,8 @@
 
 LayerImpl::~LayerImpl() {
   DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_);
-
   layer_tree_impl_->UnregisterLayer(this);
-
   layer_tree_impl_->RemoveFromElementMap(this);
-
   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
       TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::LayerImpl", this);
 }
diff --git a/cc/layers/layer_impl_test_properties.h b/cc/layers/layer_impl_test_properties.h
index e9bfa6d..5347236 100644
--- a/cc/layers/layer_impl_test_properties.h
+++ b/cc/layers/layer_impl_test_properties.h
@@ -54,7 +54,6 @@
   gfx::Point3F transform_origin;
   gfx::Transform transform;
   LayerImpl* scroll_parent;
-  std::unique_ptr<std::set<LayerImpl*>> scroll_children;
   LayerImpl* clip_parent;
   std::unique_ptr<std::set<LayerImpl*>> clip_children;
   std::vector<std::unique_ptr<viz::CopyOutputRequest>> copy_requests;
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index a02ab16..e727164 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -605,14 +605,13 @@
   EXPECT_EQ(child1, parent->children()[0]);
   EXPECT_EQ(child2, parent->children()[1]);
 
-  EXPECT_SET_NEEDS_COMMIT(2, child1->SetScrollParent(child2.get()));
+  EXPECT_SET_NEEDS_COMMIT(1, child1->SetScrollParent(child2.get()));
 
   EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, child2->RemoveFromParent());
 
   child1->ResetNeedsPushPropertiesForTesting();
 
-  EXPECT_SET_NEEDS_COMMIT(1, child2 = nullptr);
-
+  EXPECT_SET_NEEDS_COMMIT(1, child1->SetScrollParent(nullptr));
   EXPECT_TRUE(
       layer_tree_host_->LayerNeedsPushPropertiesForTesting(child1.get()));
 
@@ -635,17 +634,9 @@
   EXPECT_EQ(child1, parent->children()[0]);
   EXPECT_EQ(child2, parent->children()[1]);
 
-  EXPECT_SET_NEEDS_COMMIT(2, child1->SetScrollParent(child2.get()));
+  EXPECT_SET_NEEDS_COMMIT(1, child1->SetScrollParent(child2.get()));
 
   EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, child1->RemoveFromParent());
-
-  child2->ResetNeedsPushPropertiesForTesting();
-
-  EXPECT_SET_NEEDS_COMMIT(1, child1 = nullptr);
-
-  EXPECT_TRUE(
-      layer_tree_host_->LayerNeedsPushPropertiesForTesting(child2.get()));
-
   EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, layer_tree_host_->SetRootLayer(nullptr));
 }
 
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index c84f769f6..0ce5723c 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -23,6 +23,10 @@
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/skia_util.h"
 
+namespace {
+static constexpr int kMaxScrollbarDimension = 8192;
+};
+
 namespace cc {
 
 std::unique_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
@@ -246,13 +250,23 @@
 
 UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
     const gfx::Rect& layer_rect,
-    const gfx::Rect& content_rect,
+    const gfx::Rect& requested_content_rect,
     ScrollbarPart part) {
-  DCHECK(!content_rect.size().IsEmpty());
+  DCHECK(!requested_content_rect.size().IsEmpty());
   DCHECK(!layer_rect.size().IsEmpty());
 
+  gfx::Rect content_rect = requested_content_rect;
+
+  // Pages can end up requesting arbitrarily large scrollbars.  Prevent this
+  // from crashing due to OOM and try something smaller.
   SkBitmap skbitmap;
-  skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
+  if (!skbitmap.tryAllocN32Pixels(content_rect.width(),
+                                  content_rect.height())) {
+    content_rect.Intersect(
+        gfx::Rect(requested_content_rect.x(), requested_content_rect.y(),
+                  kMaxScrollbarDimension, kMaxScrollbarDimension));
+    skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
+  }
   SkiaPaintCanvas canvas(skbitmap);
 
   float scale_x =
diff --git a/cc/resources/layer_tree_resource_provider.cc b/cc/resources/layer_tree_resource_provider.cc
index c37f56a..45a8703 100644
--- a/cc/resources/layer_tree_resource_provider.cc
+++ b/cc/resources/layer_tree_resource_provider.cc
@@ -85,9 +85,6 @@
     unverified_sync_tokens.push_back(new_sync_token.GetData());
   }
 
-  if (compositor_context_provider_)
-    compositor_context_provider_->ContextSupport()->FlushPendingWork();
-
   if (!unverified_sync_tokens.empty()) {
     DCHECK(settings_.delegated_sync_points_required);
     DCHECK(gl);
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index aae639b..f75966b4 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -355,7 +355,7 @@
   return needs_redraw_;
 }
 
-bool SchedulerStateMachine::ShouldActivatePendingTree() const {
+bool SchedulerStateMachine::ShouldActivateSyncTree() const {
   // There is nothing to activate.
   if (!has_pending_tree_)
     return false;
@@ -559,7 +559,7 @@
 }
 
 SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
-  if (ShouldActivatePendingTree())
+  if (ShouldActivateSyncTree())
     return ACTION_ACTIVATE_SYNC_TREE;
   if (ShouldCommit())
     return ACTION_COMMIT;
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index f5036123..3370c63e 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -311,7 +311,7 @@
 
   bool ShouldBeginLayerTreeFrameSinkCreation() const;
   bool ShouldDraw() const;
-  bool ShouldActivatePendingTree() const;
+  bool ShouldActivateSyncTree() const;
   bool ShouldSendBeginMainFrame() const;
   bool ShouldCommit() const;
   bool ShouldPrepareTiles() const;
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 8cf047b..650c5cb 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -1401,6 +1401,8 @@
   resource_pool_->ReduceResourceUsage();
   image_controller_.ReduceMemoryUsage();
 
+  raster_buffer_provider_->Flush();
+
   // TODO(vmpstr): Temporary check to debug crbug.com/642927.
   CHECK(tile_task_manager_);
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 693ab2d1..797e669 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -225,6 +225,14 @@
 void LayerTreeHost::QueueSwapPromise(
     std::unique_ptr<SwapPromise> swap_promise) {
   swap_promise_manager_.QueueSwapPromise(std::move(swap_promise));
+
+  // Request a main frame if one is not already in progress. This might either
+  // A) request a commit ahead of time or B) request a commit which is not
+  // needed because there are not pending updates. If B) then the frame will
+  // be aborted early and the swap promises will be broken (see
+  // EarlyOut_NoUpdates).
+  if (!inside_main_frame_)
+    SetNeedsAnimate();
 }
 
 viz::SurfaceSequenceGenerator* LayerTreeHost::GetSurfaceSequenceGenerator() {
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 1ab182a..48ae628 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -5709,9 +5709,6 @@
   scroll_parent_clip->SetMasksToBounds(true);
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   root->SetBounds(gfx::Size(50, 50));
   scroll_parent_border->SetBounds(gfx::Size(40, 40));
@@ -5739,9 +5736,6 @@
   scroll_child->SetDrawsContent(true);
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   root->SetBounds(gfx::Size(50, 50));
   scroll_child_target->SetBounds(gfx::Size(50, 50));
@@ -5841,9 +5835,6 @@
   scroll_child->SetBounds(gfx::Size(50, 50));
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   ExecuteCalculateDrawProperties(root);
 
@@ -5884,15 +5875,8 @@
   scroll_grandparent_clip->SetMasksToBounds(true);
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   scroll_parent_border->test_properties()->scroll_parent = scroll_grandparent;
-  scroll_grandparent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_grandparent->test_properties()->scroll_children->insert(
-      scroll_parent_border);
 
   root->SetBounds(gfx::Size(50, 50));
   scroll_grandparent_border->SetBounds(gfx::Size(40, 40));
@@ -5957,15 +5941,8 @@
   scroll_grandparent_clip->SetMasksToBounds(true);
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   scroll_parent_border->test_properties()->scroll_parent = scroll_grandparent;
-  scroll_grandparent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_grandparent->test_properties()->scroll_children->insert(
-      scroll_parent_border);
 
   root->SetBounds(gfx::Size(50, 50));
   scroll_grandparent_border->SetBounds(gfx::Size(40, 40));
@@ -8593,9 +8570,6 @@
   scroll_parent->SetDrawsContent(true);
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   ExecuteCalculateDrawProperties(root);
   EXPECT_EQ(gfx::Rect(25, 25), scroll_child->visible_layer_rect());
@@ -9668,9 +9642,6 @@
   LayerImpl* scroll_parent = AddChild<LayerImpl>(scroll_clip);
 
   scroll_child->test_properties()->scroll_parent = scroll_parent;
-  scroll_parent->test_properties()->scroll_children =
-      std::make_unique<std::set<LayerImpl*>>();
-  scroll_parent->test_properties()->scroll_children->insert(scroll_child);
 
   scroll_parent->SetDrawsContent(true);
   scroll_child->SetDrawsContent(true);
@@ -10427,5 +10398,51 @@
   EXPECT_EQ(gfx::Rect(20, 20), cache_surface->visible_layer_rect());
 }
 
+TEST_F(LayerTreeHostCommonTest, BuildPropertyNodesForScrollChildrenInOrder) {
+  // This test is intended to test against a bug that scroll children were
+  // visited in unspecified order, which can cause data dependency to fail
+  // while resolving clip parent.
+
+  // Try multiple times because in the original bug the probability to fail
+  // was 50%, depends on the hash values.
+  int trial = 10;
+  while (trial--) {
+    scoped_refptr<Layer> root = Layer::Create();
+    scoped_refptr<Layer> scroller = Layer::Create();
+    scoped_refptr<Layer> scroll_sibling_1 = Layer::Create();
+    scoped_refptr<Layer> scroll_sibling_2 = Layer::Create();
+    scoped_refptr<Layer> clip_escaper = Layer::Create();
+
+    root->SetBounds(gfx::Size(100, 100));
+    scroll_sibling_1->SetBounds(gfx::Size(10, 20));
+    scroll_sibling_1->SetMasksToBounds(true);
+    scroll_sibling_2->SetBounds(gfx::Size(20, 10));
+    scroll_sibling_2->SetMasksToBounds(true);
+    clip_escaper->SetBounds(gfx::Size(30, 30));
+    clip_escaper->SetIsDrawable(true);
+
+    host()->SetRootLayer(root);
+    root->AddChild(scroller.get());
+    root->AddChild(scroll_sibling_1.get());
+    root->AddChild(scroll_sibling_2.get());
+    scroll_sibling_2->AddChild(clip_escaper.get());
+
+    // Also randomize scroll children insertion order.
+    if (trial & 1) {
+      scroll_sibling_1->SetScrollParent(scroller.get());
+      scroll_sibling_2->SetScrollParent(scroller.get());
+    } else {
+      scroll_sibling_2->SetScrollParent(scroller.get());
+      scroll_sibling_1->SetScrollParent(scroller.get());
+    }
+    clip_escaper->SetClipParent(scroll_sibling_1.get());
+
+    ExecuteCalculateDrawProperties(root.get());
+
+    EXPECT_EQ(scroll_sibling_1->clip_tree_index(),
+              clip_escaper->clip_tree_index());
+  }
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index c384d0c..e3dc44b 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -2388,10 +2388,6 @@
   viewport_scroll->test_properties()->AddChild(std::move(scroll_child_clip));
 
   child_clip->test_properties()->scroll_parent = parent;
-  std::unique_ptr<std::set<LayerImpl*>> scroll_children(
-      new std::set<LayerImpl*>);
-  scroll_children->insert(child_clip);
-  parent->test_properties()->scroll_children = std::move(scroll_children);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   DrawFrame();
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 4b9e1b2..ad27600 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -5358,6 +5358,63 @@
   int* set_needs_commit_count_;
 };
 
+class LayerTreeHostTestSwapPromiseDuringCommit : public LayerTreeHostTest {
+ protected:
+  LayerTreeHostTestSwapPromiseDuringCommit() {}
+
+  void WillBeginMainFrame() override {
+    if (TestEnded())
+      return;
+
+    std::unique_ptr<SwapPromise> swap_promise(
+        new TestSwapPromise(&swap_promise_result_[0]));
+    int set_needs_commit_count = 0;
+    int set_needs_redraw_count = 0;
+
+    {
+      std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
+          new SimpleSwapPromiseMonitor(layer_tree_host(), NULL,
+                                       &set_needs_commit_count,
+                                       &set_needs_redraw_count));
+      layer_tree_host()->QueueSwapPromise(std::move(swap_promise));
+      // Queueing a swap promise from WillBeginMainFrame should not cause
+      // another commit to be scheduled.
+      EXPECT_EQ(0, set_needs_commit_count);
+    }
+  }
+
+  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+  void DidBeginMainFrame() override {
+    if (TestEnded())
+      return;
+
+    std::unique_ptr<SwapPromise> swap_promise(
+        new TestSwapPromise(&swap_promise_result_[1]));
+    int set_needs_commit_count = 0;
+    int set_needs_redraw_count = 0;
+
+    {
+      std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
+          new SimpleSwapPromiseMonitor(layer_tree_host(), NULL,
+                                       &set_needs_commit_count,
+                                       &set_needs_redraw_count));
+      layer_tree_host()->QueueSwapPromise(std::move(swap_promise));
+      // Queueing a swap promise from DidBeginMainFrame should cause a
+      // subsequent main frame to be scheduled.
+      EXPECT_EQ(1, set_needs_commit_count);
+    }
+
+    EndTest();
+  }
+
+  void AfterTest() override {}
+
+  TestSwapPromiseResult swap_promise_result_[2];
+};
+
+MULTI_THREAD_TEST_F(LayerTreeHostTestSwapPromiseDuringCommit);
+
 class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest {
  public:
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index aba6d6b..d0d582e 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -658,52 +658,52 @@
   if (layer_list_.empty())
     return;
 
+  // Note we lazily delete element ids from the |element_id_to_xxx|
+  // maps below if we find they have no node present in their
+  // respective tree. This can be the case if the layer associated
+  // with that element id has been removed.
+
   auto element_id_to_opacity = element_id_to_opacity_animations_.begin();
   while (element_id_to_opacity != element_id_to_opacity_animations_.end()) {
     const ElementId id = element_id_to_opacity->first;
-    if (EffectNode* node =
-            property_trees_.effect_tree.FindNodeFromElementId(id)) {
-      if (!node->is_currently_animating_opacity ||
-          node->opacity == element_id_to_opacity->second) {
-        element_id_to_opacity_animations_.erase(element_id_to_opacity++);
-        continue;
-      }
-      node->opacity = element_id_to_opacity->second;
-      property_trees_.effect_tree.set_needs_update(true);
+    EffectNode* node = property_trees_.effect_tree.FindNodeFromElementId(id);
+    if (!node || !node->is_currently_animating_opacity ||
+        node->opacity == element_id_to_opacity->second) {
+      element_id_to_opacity_animations_.erase(element_id_to_opacity++);
+      continue;
     }
+    node->opacity = element_id_to_opacity->second;
+    property_trees_.effect_tree.set_needs_update(true);
     ++element_id_to_opacity;
   }
 
   auto element_id_to_filter = element_id_to_filter_animations_.begin();
   while (element_id_to_filter != element_id_to_filter_animations_.end()) {
     const ElementId id = element_id_to_filter->first;
-    if (EffectNode* node =
-            property_trees_.effect_tree.FindNodeFromElementId(id)) {
-      if (!node->is_currently_animating_filter ||
-          node->filters == element_id_to_filter->second) {
-        element_id_to_filter_animations_.erase(element_id_to_filter++);
-        continue;
-      }
-      node->filters = element_id_to_filter->second;
-      property_trees_.effect_tree.set_needs_update(true);
+    EffectNode* node = property_trees_.effect_tree.FindNodeFromElementId(id);
+    if (!node || !node->is_currently_animating_filter ||
+        node->filters == element_id_to_filter->second) {
+      element_id_to_filter_animations_.erase(element_id_to_filter++);
+      continue;
     }
+    node->filters = element_id_to_filter->second;
+    property_trees_.effect_tree.set_needs_update(true);
     ++element_id_to_filter;
   }
 
   auto element_id_to_transform = element_id_to_transform_animations_.begin();
   while (element_id_to_transform != element_id_to_transform_animations_.end()) {
     const ElementId id = element_id_to_transform->first;
-    if (TransformNode* node =
-            property_trees_.transform_tree.FindNodeFromElementId(id)) {
-      if (!node->is_currently_animating ||
-          node->local == element_id_to_transform->second) {
-        element_id_to_transform_animations_.erase(element_id_to_transform++);
-        continue;
-      }
-      node->local = element_id_to_transform->second;
-      node->needs_local_transform_update = true;
-      property_trees_.transform_tree.set_needs_update(true);
+    TransformNode* node =
+        property_trees_.transform_tree.FindNodeFromElementId(id);
+    if (!node || !node->is_currently_animating ||
+        node->local == element_id_to_transform->second) {
+      element_id_to_transform_animations_.erase(element_id_to_transform++);
+      continue;
     }
+    node->local = element_id_to_transform->second;
+    node->needs_local_transform_update = true;
+    property_trees_.transform_tree.set_needs_update(true);
     ++element_id_to_transform;
   }
 
@@ -1229,9 +1229,6 @@
 void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) {
   DCHECK(LayerById(layer->id()));
   layers_that_should_push_properties_.erase(layer);
-  element_id_to_transform_animations_.erase(layer->element_id());
-  element_id_to_opacity_animations_.erase(layer->element_id());
-  element_id_to_filter_animations_.erase(layer->element_id());
   layer_id_map_.erase(layer->id());
 }
 
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 656ba60..b68556a 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -1065,9 +1065,6 @@
     // This should cause scroll child and its descendants to be affected by
     // |child|'s clip.
     scroll_child->test_properties()->scroll_parent = child.get();
-    child->test_properties()->scroll_children =
-        std::make_unique<std::set<LayerImpl*>>();
-    child->test_properties()->scroll_children->insert(scroll_child.get());
 
     grand_child->SetBounds(gfx::Size(200, 200));
     grand_child->SetDrawsContent(true);
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 9a57979..089eaa4 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -53,29 +53,34 @@
 template <typename LayerType>
 class PropertyTreeBuilderContext {
  public:
-  PropertyTreeBuilderContext(const LayerType* page_scale_layer,
+  PropertyTreeBuilderContext(LayerType* root_layer,
+                             const LayerType* page_scale_layer,
                              const LayerType* inner_viewport_scroll_layer,
                              const LayerType* outer_viewport_scroll_layer,
                              const LayerType* overscroll_elasticity_layer,
                              const gfx::Vector2dF& elastic_overscroll,
                              float page_scale_factor,
                              const gfx::Transform& device_transform,
-                             PropertyTrees& property_trees)
-      : page_scale_layer_(page_scale_layer),
+                             PropertyTrees* property_trees)
+      : root_layer_(root_layer),
+        page_scale_layer_(page_scale_layer),
         inner_viewport_scroll_layer_(inner_viewport_scroll_layer),
         outer_viewport_scroll_layer_(outer_viewport_scroll_layer),
         overscroll_elasticity_layer_(overscroll_elasticity_layer),
         elastic_overscroll_(elastic_overscroll),
         page_scale_factor_(page_scale_factor),
         device_transform_(device_transform),
-        property_trees_(property_trees),
-        transform_tree_(property_trees.transform_tree),
-        clip_tree_(property_trees.clip_tree),
-        effect_tree_(property_trees.effect_tree),
-        scroll_tree_(property_trees.scroll_tree) {}
+        property_trees_(*property_trees),
+        transform_tree_(property_trees->transform_tree),
+        clip_tree_(property_trees->clip_tree),
+        effect_tree_(property_trees->effect_tree),
+        scroll_tree_(property_trees->scroll_tree) {
+    InitializeScrollChildrenMap();
+  }
 
-  void BuildPropertyTrees(LayerType* root_layer,
-                          float device_scale_factor,
+  void InitializeScrollChildrenMap();
+
+  void BuildPropertyTrees(float device_scale_factor,
                           const gfx::Rect& viewport,
                           SkColor root_background_color) const;
 
@@ -106,6 +111,7 @@
       LayerType* layer,
       DataForRecursion<LayerType>* data_for_children) const;
 
+  LayerType* root_layer_;
   const LayerType* page_scale_layer_;
   const LayerType* inner_viewport_scroll_layer_;
   const LayerType* outer_viewport_scroll_layer_;
@@ -118,6 +124,7 @@
   ClipTree& clip_tree_;
   EffectTree& effect_tree_;
   ScrollTree& scroll_tree_;
+  std::multimap<const LayerType*, LayerType*> scroll_children_map_;
 };
 
 static LayerPositionConstraint PositionConstraint(Layer* layer) {
@@ -161,14 +168,6 @@
   return layer->test_properties()->scroll_parent;
 }
 
-static std::set<Layer*>* ScrollChildren(Layer* layer) {
-  return layer->scroll_children();
-}
-
-static std::set<LayerImpl*>* ScrollChildren(LayerImpl* layer) {
-  return layer->test_properties()->scroll_children.get();
-}
-
 static Layer* ClipParent(Layer* layer) {
   return layer->clip_parent();
 }
@@ -1213,21 +1212,18 @@
     SetLayerPropertyChangedForChild(layer, current_child);
     if (!ScrollParent(current_child)) {
       BuildPropertyTreesInternal(current_child, data_for_children);
-    } else {
-      // The child should be included in its scroll parent's list of scroll
-      // children.
-      DCHECK(ScrollChildren(ScrollParent(current_child))->count(current_child));
     }
   }
 
-  if (ScrollChildren(layer)) {
-    for (LayerType* scroll_child : *ScrollChildren(layer)) {
-      DCHECK_EQ(ScrollParent(scroll_child), layer);
-      DCHECK(Parent(scroll_child));
-      data_for_children.effect_tree_parent =
-          Parent(scroll_child)->effect_tree_index();
-      BuildPropertyTreesInternal(scroll_child, data_for_children);
-    }
+  auto scroll_children_range = scroll_children_map_.equal_range(layer);
+  for (auto it = scroll_children_range.first;
+       it != scroll_children_range.second; ++it) {
+    LayerType* scroll_child = it->second;
+    DCHECK_EQ(ScrollParent(scroll_child), layer);
+    DCHECK(Parent(scroll_child));
+    data_for_children.effect_tree_parent =
+        Parent(scroll_child)->effect_tree_index();
+    BuildPropertyTreesInternal(scroll_child, data_for_children);
   }
 
   if (MaskLayer(layer)) {
@@ -1242,6 +1238,13 @@
   }
 }
 
+const LayerTreeHost& AllLayerRange(const Layer* root_layer) {
+  return *root_layer->layer_tree_host();
+}
+const LayerTreeImpl& AllLayerRange(const LayerImpl* root_layer) {
+  return *root_layer->layer_tree_impl();
+}
+
 }  // namespace
 
 Layer* PropertyTreeBuilder::FindFirstScrollableLayer(Layer* layer) {
@@ -1262,7 +1265,6 @@
 
 template <typename LayerType>
 void PropertyTreeBuilderContext<LayerType>::BuildPropertyTrees(
-    LayerType* root_layer,
     float device_scale_factor,
     const gfx::Rect& viewport,
     SkColor root_background_color) const {
@@ -1274,10 +1276,10 @@
         &property_trees_, overscroll_elasticity_layer_, elastic_overscroll_);
     clip_tree_.SetViewportClip(gfx::RectF(viewport));
     float page_scale_factor_for_root =
-        page_scale_layer_ == root_layer ? page_scale_factor_ : 1.f;
+        page_scale_layer_ == root_layer_ ? page_scale_factor_ : 1.f;
     transform_tree_.SetRootTransformsAndScales(
         device_scale_factor, page_scale_factor_for_root, device_transform_,
-        root_layer->position());
+        root_layer_->position());
     return;
   }
 
@@ -1313,7 +1315,7 @@
   data_for_recursion.clip_tree_parent =
       clip_tree_.Insert(root_clip, ClipTree::kRootNodeId);
 
-  BuildPropertyTreesInternal(root_layer, data_for_recursion);
+  BuildPropertyTreesInternal(root_layer_, data_for_recursion);
   property_trees_.needs_rebuild = false;
 
   // The transform tree is kept up to date as it is built, but the
@@ -1326,17 +1328,20 @@
 }
 
 #if DCHECK_IS_ON()
-static void CheckScrollAndClipPointersForLayer(Layer* layer) {
+template <typename LayerType>
+static void CheckDanglingScrollParent(LayerType* root_layer) {
+  std::unordered_set<const LayerType*> layers;
+  for (const auto* layer : AllLayerRange(root_layer))
+    layers.insert(layer);
+  for (auto* layer : AllLayerRange(root_layer))
+    DCHECK(!ScrollParent(layer) ||
+           layers.find(ScrollParent(layer)) != layers.end());
+}
+
+static void CheckClipPointersForLayer(Layer* layer) {
   if (!layer)
     return;
 
-  if (layer->scroll_children()) {
-    for (std::set<Layer*>::iterator it = layer->scroll_children()->begin();
-         it != layer->scroll_children()->end(); ++it) {
-      DCHECK_EQ((*it)->scroll_parent(), layer);
-    }
-  }
-
   if (layer->clip_children()) {
     for (std::set<Layer*>::iterator it = layer->clip_children()->begin();
          it != layer->clip_children()->end(); ++it) {
@@ -1346,6 +1351,17 @@
 }
 #endif
 
+template <typename LayerType>
+void PropertyTreeBuilderContext<LayerType>::InitializeScrollChildrenMap() {
+#if DCHECK_IS_ON()
+  CheckDanglingScrollParent(root_layer_);
+#endif
+  for (auto* layer : AllLayerRange(root_layer_)) {
+    if (ScrollParent(layer))
+      scroll_children_map_.emplace(ScrollParent(layer), layer);
+  }
+}
+
 void PropertyTreeBuilder::BuildPropertyTrees(
     Layer* root_layer,
     const Layer* page_scale_layer,
@@ -1366,13 +1382,13 @@
   if (root_layer->layer_tree_host()->has_copy_request())
     UpdateSubtreeHasCopyRequestRecursive(root_layer);
   PropertyTreeBuilderContext<Layer>(
-      page_scale_layer, inner_viewport_scroll_layer,
+      root_layer, page_scale_layer, inner_viewport_scroll_layer,
       outer_viewport_scroll_layer, overscroll_elasticity_layer,
-      elastic_overscroll, page_scale_factor, device_transform, *property_trees)
-      .BuildPropertyTrees(root_layer, device_scale_factor, viewport, color);
+      elastic_overscroll, page_scale_factor, device_transform, property_trees)
+      .BuildPropertyTrees(device_scale_factor, viewport, color);
 #if DCHECK_IS_ON()
-  for (auto* layer : *root_layer->layer_tree_host())
-    CheckScrollAndClipPointersForLayer(layer);
+  for (auto* layer : AllLayerRange(root_layer))
+    CheckClipPointersForLayer(layer);
 #endif
   property_trees->ResetCachedData();
   // During building property trees, all copy requests are moved from layers to
@@ -1405,10 +1421,10 @@
   UpdateSubtreeHasCopyRequestRecursive(root_layer);
 
   PropertyTreeBuilderContext<LayerImpl>(
-      page_scale_layer, inner_viewport_scroll_layer,
+      root_layer, page_scale_layer, inner_viewport_scroll_layer,
       outer_viewport_scroll_layer, overscroll_elasticity_layer,
-      elastic_overscroll, page_scale_factor, device_transform, *property_trees)
-      .BuildPropertyTrees(root_layer, device_scale_factor, viewport, color);
+      elastic_overscroll, page_scale_factor, device_transform, property_trees)
+      .BuildPropertyTrees(device_scale_factor, viewport, color);
   property_trees->effect_tree.CreateOrReuseRenderSurfaces(
       &render_surfaces, root_layer->layer_tree_impl());
   property_trees->ResetCachedData();
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index 1afd24a..f7e7e9f9 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -117,13 +117,6 @@
           effect_tree.Node(layer_impl->effect_tree_index())->mask_layer_id);
     }
 
-    const Layer* layer_scroll_parent = layer->scroll_parent();
-
-    if (layer_scroll_parent) {
-      ASSERT_TRUE(layer_scroll_parent->scroll_children()->find(layer) !=
-                  layer_scroll_parent->scroll_children()->end());
-    }
-
     const Layer* layer_clip_parent = layer->clip_parent();
 
     if (layer_clip_parent) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
index 7a19f00..b643dfc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
@@ -89,8 +89,6 @@
     /** Rectangles that defines the area where each stack need to be laid out. */
     private final RectF[] mStackRects;
 
-    private final RectF mCachedRect = new RectF();
-
     private int mStackAnimationCount;
 
     private float mFlingSpeed; // pixel/ms
@@ -876,6 +874,7 @@
         }
 
         float getTopHeightOffset() {
+            if (FeatureUtilities.isChromeHomeModernEnabled()) return MODERN_TOP_MARGIN_DP;
             if (FeatureUtilities.isChromeHomeEnabled()) return 0;
             return (StackLayout.this.getHeight() - getHeightMinusBrowserControls())
                     * mStackOffsetYPercent;
@@ -1334,16 +1333,12 @@
                 resourceManager, fullscreenManager);
         // If the browser controls are at the bottom make sure to use theme colors for this layout
         // specifically.
-        mCachedRect.set(viewport);
         if (fullscreenManager.areBrowserControlsAtBottom() && mLayoutTabs != null) {
             for (LayoutTab t : mLayoutTabs) t.setForceDefaultThemeColor(false);
-            if (FeatureUtilities.isChromeHomeModernEnabled()) {
-                mCachedRect.offset(0, MODERN_TOP_MARGIN_DP * mDpToPx);
-            }
         }
         assert mSceneLayer != null;
 
-        mSceneLayer.pushLayers(getContext(), mCachedRect, contentViewport, this, layerTitleCache,
+        mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, layerTitleCache,
                 tabContentManager, resourceManager, fullscreenManager);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
index ab2d197e..7e51bd5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
@@ -143,10 +143,6 @@
                 .apply();
     }
 
-    public static void logRendererCrash() {
-        nativeLogRendererCrash();
-    }
-
     /**
      * Updates the metrics services based on a change of consent. This can happen during first-run
      * flow, and when the user changes their preferences.
@@ -205,7 +201,6 @@
     private static native void nativeUpdateMetricsServiceState(boolean mayUpload);
     private native void nativeUmaResumeSession(long nativeUmaSessionStats);
     private native void nativeUmaEndSession(long nativeUmaSessionStats);
-    private static native void nativeLogRendererCrash();
     private static native void nativeRegisterExternalExperiment(
             String studyName, int[] experimentIds);
     private static native void nativeRegisterSyntheticFieldTrial(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index b2b2d68..34c9e9fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -17,7 +17,6 @@
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.media.MediaCaptureNotificationService;
-import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.metrics.UmaUtils;
 import org.chromium.chrome.browser.policy.PolicyAuditor;
 import org.chromium.chrome.browser.policy.PolicyAuditor.AuditEvent;
@@ -125,7 +124,7 @@
             rendererCrashStatus = TAB_RENDERER_CRASH_STATUS_SHOWN_IN_FOREGROUND_APP;
             mTab.showSadTab();
             // This is necessary to correlate histogram data with stability counts.
-            UmaSessionStats.logRendererCrash();
+            RecordHistogram.recordBooleanHistogram("Stability.Android.RendererCrash", true);
         }
         RecordHistogram.recordEnumeratedHistogram(
                 "Tab.RendererCrashStatus", rendererCrashStatus, TAB_RENDERER_CRASH_STATUS_MAX);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4b9d900..847759f 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1693,6 +1693,7 @@
     "//crypto:platform",
     "//device/base",
     "//device/bluetooth:mojo",
+    "//device/hid",
     "//device/usb/mojo",
     "//device/usb/public/interfaces",
     "//device/vr/features",
diff --git a/chrome/browser/android/metrics/uma_session_stats.cc b/chrome/browser/android/metrics/uma_session_stats.cc
index f2a557f1..d4b229ce 100644
--- a/chrome/browser/android/metrics/uma_session_stats.cc
+++ b/chrome/browser/android/metrics/uma_session_stats.cc
@@ -143,18 +143,6 @@
       may_upload);
 }
 
-// Renderer process crashed in the foreground.
-static void LogRendererCrash(JNIEnv*, const JavaParamRef<jclass>&) {
-  DCHECK(g_browser_process);
-  // Increment the renderer crash count in stability metrics.
-  PrefService* pref = g_browser_process->local_state();
-  DCHECK(pref);
-  int value = pref->GetInteger(metrics::prefs::kStabilityRendererCrashCount);
-  pref->SetInteger(metrics::prefs::kStabilityRendererCrashCount, value + 1);
-  // Migrate proto to histogram to repurpose proto count.
-  UMA_HISTOGRAM_BOOLEAN("Stability.Android.RendererCrash", true);
-}
-
 static void RegisterExternalExperiment(
     JNIEnv* env,
     const JavaParamRef<jclass>& clazz,
diff --git a/chrome/browser/android/vr_shell/gl_browser_interface.h b/chrome/browser/android/vr_shell/gl_browser_interface.h
index dd0abb8..0942b937 100644
--- a/chrome/browser/android/vr_shell/gl_browser_interface.h
+++ b/chrome/browser/android/vr_shell/gl_browser_interface.h
@@ -43,6 +43,7 @@
   virtual void ToggleCardboardGamepad(bool enabled) = 0;
   virtual void OnGlInitialized(unsigned int content_texture_id) = 0;
   virtual void OnWebVrFrameAvailable() = 0;
+  virtual void OnWebVrTimedOut() = 0;
   virtual void OnProjMatrixChanged(const gfx::Transform& proj_matrix) = 0;
 };
 
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.cc b/chrome/browser/android/vr_shell/vr_gl_thread.cc
index 49073a6..5d77c9a5 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.cc
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.cc
@@ -255,6 +255,11 @@
   scene_manager_->OnWebVrFrameAvailable();
 }
 
+void VrGLThread::OnWebVrTimedOut() {
+  DCHECK(task_runner()->BelongsToCurrentThread());
+  scene_manager_->OnWebVrTimedOut();
+}
+
 void VrGLThread::OnProjMatrixChanged(const gfx::Transform& proj_matrix) {
   DCHECK(task_runner()->BelongsToCurrentThread());
   scene_manager_->OnProjMatrixChanged(proj_matrix);
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.h b/chrome/browser/android/vr_shell/vr_gl_thread.h
index 27767352..6d7c4e7 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.h
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.h
@@ -60,6 +60,7 @@
   void ToggleCardboardGamepad(bool enabled) override;
   void OnGlInitialized(unsigned int content_texture_id) override;
   void OnWebVrFrameAvailable() override;
+  void OnWebVrTimedOut() override;
   void OnProjMatrixChanged(const gfx::Transform& proj_matrix) override;
 
   // vr::UiBrowserInterface implementation (UI calling to VrShell).
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index a44a6e0..dd51bf9 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -87,6 +87,8 @@
 static constexpr base::TimeDelta kWebVRFenceCheckTimeout =
     base::TimeDelta::FromMicroseconds(2000);
 
+static constexpr int kWebVrInitialFrameTimeoutSeconds = 5;
+
 // Provides the direction the head is looking towards as a 3x1 unit vector.
 gfx::Vector3dF GetForwardVector(const gfx::Transform& head_pose) {
   // Same as multiplying the inverse of the rotation component of the matrix by
@@ -368,6 +370,7 @@
   closePresentationBindings();
   submit_client_.Bind(std::move(submit_client_info));
   binding_.Bind(std::move(request));
+  ScheduleWebVrFrameTimeout();
 }
 
 void VrShellGl::OnContentFrameAvailable() {
@@ -393,6 +396,31 @@
   browser_->OnWebVrFrameAvailable();
 
   DrawFrame(frame_index);
+  if (web_vr_mode_) {
+    ++webvr_frames_received_;
+    ScheduleWebVrFrameTimeout();
+  } else {
+    webvr_frame_timeout_.Cancel();
+  }
+}
+
+void VrShellGl::ScheduleWebVrFrameTimeout() {
+  // TODO(mthiesse): We should also timeout after the initial frame to prevent
+  // bad experiences, but we have to be careful to handle things like splash
+  // screens correctly. For now just ensure we receive a first frame.
+  if (webvr_frames_received_ > 0) {
+    webvr_frame_timeout_.Cancel();
+    return;
+  }
+  webvr_frame_timeout_.Reset(
+      base::Bind(&VrShellGl::OnWebVrFrameTimedOut, base::Unretained(this)));
+  task_runner_->PostDelayedTask(
+      FROM_HERE, webvr_frame_timeout_.callback(),
+      base::TimeDelta::FromSeconds(kWebVrInitialFrameTimeoutSeconds));
+}
+
+void VrShellGl::OnWebVrFrameTimedOut() {
+  browser_->OnWebVrTimedOut();
 }
 
 void VrShellGl::GvrInit(gvr_context* gvr_api) {
@@ -1049,26 +1077,36 @@
   vsync_helper_.CancelVSyncRequest();
   controller_->OnPause();
   gvr_api_->PauseTracking();
+  webvr_frame_timeout_.Cancel();
 }
 
 void VrShellGl::OnResume() {
   gvr_api_->RefreshViewerProfile();
   gvr_api_->ResumeTracking();
   controller_->OnResume();
-  if (ready_to_draw_) {
-    vsync_helper_.CancelVSyncRequest();
-    OnVSync(base::TimeTicks::Now());
-  }
+  if (!ready_to_draw_)
+    return;
+  vsync_helper_.CancelVSyncRequest();
+  OnVSync(base::TimeTicks::Now());
+  if (web_vr_mode_ && submit_client_)
+    ScheduleWebVrFrameTimeout();
 }
 
 void VrShellGl::SetWebVrMode(bool enabled) {
   web_vr_mode_ = enabled;
 
+  if (web_vr_mode_ && submit_client_) {
+    ScheduleWebVrFrameTimeout();
+  } else {
+    webvr_frame_timeout_.Cancel();
+    webvr_frames_received_ = 0;
+  }
+
   if (cardboard_) {
     browser_->ToggleCardboardGamepad(enabled);
   }
 
-  if (!enabled) {
+  if (!web_vr_mode_) {
     closePresentationBindings();
   }
 }
@@ -1242,6 +1280,7 @@
 }
 
 void VrShellGl::closePresentationBindings() {
+  webvr_frame_timeout_.Cancel();
   submit_client_.reset();
   if (!callback_.is_null()) {
     // When this Presentation provider is going away we have to respond to
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 0d6fd66..a55c259 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -158,8 +158,12 @@
       const gfx::Vector3dF& controller_direction);
   void SendGestureToContent(std::unique_ptr<blink::WebInputEvent> event);
   void CreateUiSurface();
+
   void OnContentFrameAvailable();
   void OnWebVRFrameAvailable();
+  void ScheduleWebVrFrameTimeout();
+  void OnWebVrFrameTimedOut();
+
   int64_t GetPredictedFrameTimeNanos();
 
   void OnVSync(base::TimeTicks frame_time);
@@ -256,6 +260,8 @@
   // Larger than frame_index_ so it can be initialized out-of-band.
   uint16_t last_frame_index_ = -1;
 
+  uint64_t webvr_frames_received_ = 0;
+
   // Attributes for gesture detection while holding app button.
   gfx::Vector3dF controller_start_direction_;
 
@@ -275,6 +281,8 @@
 
   AndroidVSyncHelper vsync_helper_;
 
+  base::CancelableCallback<void()> webvr_frame_timeout_;
+
   base::WeakPtrFactory<VrShellGl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(VrShellGl);
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index d8f5c8a..ee63812 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -698,6 +698,11 @@
        content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB)) {
     base::RecordAction(UserMetricsAction("ClearBrowsingData_Cookies"));
 
+    HostContentSettingsMapFactory::GetForProfile(profile_)
+        ->ClearSettingsForOneTypeWithPredicate(
+            CONTENT_SETTINGS_TYPE_CLIENT_HINTS, base::Time(),
+            base::Bind(&WebsiteSettingsFilterAdapter, filter));
+
     // Clear the safebrowsing cookies only if time period is for "all time".  It
     // doesn't make sense to apply the time period of deleting in the last X
     // hours/days to the safebrowsing cookies since they aren't the result of
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index c74e620..b167729 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1756,6 +1756,106 @@
   EXPECT_EQ(CONTENT_SETTING_ALLOW, host_settings[0].GetContentSetting());
 }
 
+TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveSelectedClientHints) {
+  // Add our settings.
+  HostContentSettingsMap* host_content_settings_map =
+      HostContentSettingsMapFactory::GetForProfile(GetProfile());
+
+  std::unique_ptr<base::ListValue> expiration_times_list =
+      base::MakeUnique<base::ListValue>();
+  expiration_times_list->AppendInteger(0);
+  expiration_times_list->AppendInteger(2);
+
+  double expiration_time =
+      (base::Time::Now() + base::TimeDelta::FromHours(24)).ToDoubleT();
+
+  auto expiration_times_dictionary = std::make_unique<base::DictionaryValue>();
+  expiration_times_dictionary->SetList("client_hints",
+                                       std::move(expiration_times_list));
+  expiration_times_dictionary->SetDouble("expiration_time", expiration_time);
+
+  host_content_settings_map->SetWebsiteSettingDefaultScope(
+      kOrigin1, GURL(), CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(),
+      expiration_times_dictionary->CreateDeepCopy());
+  host_content_settings_map->SetWebsiteSettingDefaultScope(
+      kOrigin2, GURL(), CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(),
+      expiration_times_dictionary->CreateDeepCopy());
+
+  host_content_settings_map->SetWebsiteSettingDefaultScope(
+      kOrigin3, GURL(), CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(),
+      expiration_times_dictionary->CreateDeepCopy());
+
+  // Clear all except for origin1 and origin3.
+  std::unique_ptr<BrowsingDataFilterBuilder> filter(
+      BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::BLACKLIST));
+  filter->AddRegisterableDomain(kTestRegisterableDomain1);
+  filter->AddRegisterableDomain(kTestRegisterableDomain3);
+  BlockUntilOriginDataRemoved(AnHourAgo(), base::Time::Max(),
+                              content::BrowsingDataRemover::DATA_TYPE_COOKIES,
+                              std::move(filter));
+
+  ContentSettingsForOneType host_settings;
+  host_content_settings_map->GetSettingsForOneType(
+      CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(), &host_settings);
+
+  ASSERT_EQ(2u, host_settings.size());
+
+  EXPECT_EQ(ContentSettingsPattern::FromURLNoWildcard(kOrigin1),
+            host_settings[0].primary_pattern)
+      << host_settings[0].primary_pattern.ToString();
+
+  EXPECT_EQ(ContentSettingsPattern::FromURLNoWildcard(kOrigin3),
+            host_settings[1].primary_pattern)
+      << host_settings[1].primary_pattern.ToString();
+
+  for (size_t i = 0; i < host_settings.size(); ++i) {
+    EXPECT_EQ(ContentSettingsPattern::Wildcard(),
+              host_settings.at(i).secondary_pattern);
+    EXPECT_EQ(*expiration_times_dictionary, *host_settings.at(i).setting_value);
+  }
+}
+
+TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveAllClientHints) {
+  // Add our settings.
+  HostContentSettingsMap* host_content_settings_map =
+      HostContentSettingsMapFactory::GetForProfile(GetProfile());
+
+  std::unique_ptr<base::ListValue> expiration_times_list =
+      base::MakeUnique<base::ListValue>();
+  expiration_times_list->AppendInteger(0);
+  expiration_times_list->AppendInteger(2);
+
+  double expiration_time =
+      (base::Time::Now() + base::TimeDelta::FromHours(24)).ToDoubleT();
+
+  auto expiration_times_dictionary = std::make_unique<base::DictionaryValue>();
+  expiration_times_dictionary->SetList("client_hints",
+                                       std::move(expiration_times_list));
+  expiration_times_dictionary->SetDouble("expiration_time", expiration_time);
+
+  host_content_settings_map->SetWebsiteSettingDefaultScope(
+      kOrigin1, GURL(), CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(),
+      expiration_times_dictionary->CreateDeepCopy());
+  host_content_settings_map->SetWebsiteSettingDefaultScope(
+      kOrigin2, GURL(), CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(),
+      expiration_times_dictionary->CreateDeepCopy());
+
+  host_content_settings_map->SetWebsiteSettingDefaultScope(
+      kOrigin3, GURL(), CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(),
+      expiration_times_dictionary->CreateDeepCopy());
+
+  // Clear all.
+  BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(),
+                                content::BrowsingDataRemover::DATA_TYPE_COOKIES,
+                                false);
+
+  ContentSettingsForOneType host_settings;
+  host_content_settings_map->GetSettingsForOneType(
+      CONTENT_SETTINGS_TYPE_CLIENT_HINTS, std::string(), &host_settings);
+
+  ASSERT_EQ(0u, host_settings.size());
+}
+
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveDurablePermission) {
   // Add our settings.
   HostContentSettingsMap* host_content_settings_map =
diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc
index 4bf799d3..c5fac0c 100644
--- a/chrome/browser/chrome_browser_main_android.cc
+++ b/chrome/browser/chrome_browser_main_android.cc
@@ -21,6 +21,7 @@
 #include "components/crash/content/app/breakpad_linux.h"
 #include "components/crash/content/browser/child_process_crash_observer_android.h"
 #include "components/crash/content/browser/crash_dump_observer_android.h"
+#include "components/metrics/stability_metrics_helper.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/public/browser/browser_thread.h"
@@ -67,7 +68,10 @@
     PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_dir);
     breakpad::CrashDumpObserver::GetInstance()->RegisterClient(
         base::MakeUnique<breakpad::ChildProcessCrashObserver>(
-            crash_dump_dir, kAndroidMinidumpDescriptor));
+            crash_dump_dir, kAndroidMinidumpDescriptor,
+            base::Bind(
+                &metrics::StabilityMetricsHelper::IncreaseRendererCrashCount,
+                g_browser_process->local_state())));
   }
 
   // Auto-detect based on en-US whether secondary locale .pak files exist.
diff --git a/chrome/browser/chromeos/tether/tether_service.cc b/chrome/browser/chromeos/tether/tether_service.cc
index 0c3df9c7..4064a35 100644
--- a/chrome/browser/chromeos/tether/tether_service.cc
+++ b/chrome/browser/chromeos/tether/tether_service.cc
@@ -18,12 +18,13 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/chromeos_switches.h"
-#include "chromeos/components/tether/initializer.h"
+#include "chromeos/components/tether/initializer_impl.h"
 #include "chromeos/network/network_connect.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "components/cryptauth/cryptauth_service.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/proximity_auth/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "ui/message_center/message_center.h"
 
@@ -62,7 +63,7 @@
   registry->RegisterBooleanPref(prefs::kInstantTetheringBleAdvertisingSupported,
                                 true);
 
-  chromeos::tether::Initializer::RegisterProfilePrefs(registry);
+  chromeos::tether::InitializerImpl::RegisterProfilePrefs(registry);
 }
 
 // static
@@ -70,28 +71,6 @@
   return base::FeatureList::IsEnabled(features::kInstantTethering);
 }
 
-void TetherService::InitializerDelegate::InitializeTether(
-    cryptauth::CryptAuthService* cryptauth_service,
-    chromeos::tether::NotificationPresenter* notification_presenter,
-    PrefService* pref_service,
-    ProfileOAuth2TokenService* token_service,
-    chromeos::NetworkStateHandler* network_state_handler,
-    chromeos::ManagedNetworkConfigurationHandler*
-        managed_network_configuration_handler,
-    chromeos::NetworkConnect* network_connect,
-    chromeos::NetworkConnectionHandler* network_connection_handler,
-    scoped_refptr<device::BluetoothAdapter> adapter) {
-  chromeos::tether::Initializer::Init(
-      cryptauth_service, std::move(notification_presenter), pref_service,
-      token_service, network_state_handler,
-      managed_network_configuration_handler, network_connect,
-      network_connection_handler, adapter);
-}
-
-void TetherService::InitializerDelegate::ShutdownTether() {
-  chromeos::tether::Initializer::Shutdown();
-}
-
 TetherService::TetherService(
     Profile* profile,
     chromeos::PowerManagerClient* power_manager_client,
@@ -103,7 +82,6 @@
       session_manager_client_(session_manager_client),
       cryptauth_service_(cryptauth_service),
       network_state_handler_(network_state_handler),
-      initializer_delegate_(base::MakeUnique<InitializerDelegate>()),
       notification_presenter_(
           base::MakeUnique<chromeos::tether::TetherNotificationPresenter>(
               profile_,
@@ -132,7 +110,10 @@
                             weak_ptr_factory_.GetWeakPtr())));
 }
 
-TetherService::~TetherService() {}
+TetherService::~TetherService() {
+  if (initializer_)
+    initializer_->RemoveObserver(this);
+}
 
 void TetherService::StartTetherIfPossible() {
   if (GetTetherTechnologyState() !=
@@ -140,7 +121,12 @@
     return;
   }
 
-  initializer_delegate_->InitializeTether(
+  // Do not initialize the Tether component if it already exists.
+  if (initializer_)
+    return;
+
+  PA_LOG(INFO) << "Starting up Tether component.";
+  initializer_ = chromeos::tether::InitializerImpl::Factory::NewInstance(
       cryptauth_service_, notification_presenter_.get(), profile_->GetPrefs(),
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_),
       network_state_handler_,
@@ -150,7 +136,13 @@
 }
 
 void TetherService::StopTetherIfNecessary() {
-  initializer_delegate_->ShutdownTether();
+  if (!initializer_)
+    return;
+
+  PA_LOG(INFO) << "Shutting down Tether component.";
+
+  initializer_->AddObserver(this);
+  initializer_->RequestShutdown();
 }
 
 void TetherService::Shutdown() {
@@ -269,6 +261,20 @@
   UpdateTetherTechnologyState();
 }
 
+void TetherService::OnShutdownComplete() {
+  DCHECK(initializer_->status() ==
+         chromeos::tether::Initializer::Status::SHUT_DOWN);
+  initializer_->RemoveObserver(this);
+  initializer_.reset();
+  PA_LOG(INFO) << "Tether component was shut down.";
+
+  // It is possible that the Tether TechnologyState was set to ENABLED while the
+  // previous Initializer instance was shutting down. If that was the case,
+  // restart the Tether component.
+  if (!shut_down_)
+    StartTetherIfPossible();
+}
+
 void TetherService::OnPrefsChanged() {
   UpdateTetherTechnologyState();
 }
@@ -494,11 +500,6 @@
                             TetherFeatureState::TETHER_FEATURE_STATE_MAX);
 }
 
-void TetherService::SetInitializerDelegateForTest(
-    std::unique_ptr<InitializerDelegate> initializer_delegate) {
-  initializer_delegate_ = std::move(initializer_delegate);
-}
-
 void TetherService::SetNotificationPresenterForTest(
     std::unique_ptr<chromeos::tether::NotificationPresenter>
         notification_presenter) {
diff --git a/chrome/browser/chromeos/tether/tether_service.h b/chrome/browser/chromeos/tether/tether_service.h
index 0e1d536..1fb0716 100644
--- a/chrome/browser/chromeos/tether/tether_service.h
+++ b/chrome/browser/chromeos/tether/tether_service.h
@@ -10,6 +10,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chromeos/components/tether/initializer.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "chromeos/network/network_state_handler.h"
@@ -21,9 +22,6 @@
 
 namespace chromeos {
 class NetworkStateHandler;
-class ManagedNetworkConfigurationHandler;
-class NetworkConnect;
-class NetworkConnectionHandler;
 namespace tether {
 class NotificationPresenter;
 }  // namespace tether
@@ -35,14 +33,14 @@
 
 class PrefRegistrySimple;
 class Profile;
-class ProfileOAuth2TokenService;
 
 class TetherService : public KeyedService,
                       public chromeos::PowerManagerClient::Observer,
                       public chromeos::SessionManagerClient::Observer,
                       public cryptauth::CryptAuthDeviceManager::Observer,
                       public device::BluetoothAdapter::Observer,
-                      public chromeos::NetworkStateHandlerObserver {
+                      public chromeos::NetworkStateHandlerObserver,
+                      public chromeos::tether::Initializer::Observer {
  public:
   TetherService(Profile* profile,
                 chromeos::PowerManagerClient* power_manager_client,
@@ -65,24 +63,6 @@
   // Should only be called once a user is logged in.
   virtual void StartTetherIfPossible();
 
-  // Delegate used to call the static functions of Initializer. Injected to
-  // aid in testing.
-  class InitializerDelegate {
-   public:
-    virtual void InitializeTether(
-        cryptauth::CryptAuthService* cryptauth_service,
-        chromeos::tether::NotificationPresenter* notification_presenter,
-        PrefService* pref_service,
-        ProfileOAuth2TokenService* token_service,
-        chromeos::NetworkStateHandler* network_state_handler,
-        chromeos::ManagedNetworkConfigurationHandler*
-            managed_network_configuration_handler,
-        chromeos::NetworkConnect* network_connect,
-        chromeos::NetworkConnectionHandler* network_connection_handler,
-        scoped_refptr<device::BluetoothAdapter> adapter);
-    virtual void ShutdownTether();
-  };
-
  protected:
   // KeyedService:
   void Shutdown() override;
@@ -109,6 +89,9 @@
       const chromeos::NetworkState* network) override;
   void DeviceListChanged() override;
 
+  // chromeos::tether::Initializer::Observer:
+  void OnShutdownComplete() override;
+
   // Callback when the controlling pref changes.
   void OnPrefsChanged();
 
@@ -203,8 +186,6 @@
   // Record to UMA Tether's current feature state.
   void RecordTetherFeatureState();
 
-  void SetInitializerDelegateForTest(
-      std::unique_ptr<InitializerDelegate> initializer_delegate);
   void SetNotificationPresenterForTest(
       std::unique_ptr<chromeos::tether::NotificationPresenter>
           notification_presenter);
@@ -223,9 +204,9 @@
   chromeos::SessionManagerClient* session_manager_client_;
   cryptauth::CryptAuthService* cryptauth_service_;
   chromeos::NetworkStateHandler* network_state_handler_;
-  std::unique_ptr<InitializerDelegate> initializer_delegate_;
   std::unique_ptr<chromeos::tether::NotificationPresenter>
       notification_presenter_;
+  std::unique_ptr<chromeos::tether::Initializer> initializer_;
 
   PrefChangeRegistrar registrar_;
   scoped_refptr<device::BluetoothAdapter> adapter_;
diff --git a/chrome/browser/chromeos/tether/tether_service_unittest.cc b/chrome/browser/chromeos/tether/tether_service_unittest.cc
index 2171bb3..d65f4c3b 100644
--- a/chrome/browser/chromeos/tether/tether_service_unittest.cc
+++ b/chrome/browser/chromeos/tether/tether_service_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
@@ -18,7 +19,9 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/components/tether/fake_initializer.h"
 #include "chromeos/components/tether/fake_notification_presenter.h"
+#include "chromeos/components/tether/initializer_impl.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_power_manager_client.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
@@ -122,12 +125,35 @@
   int updated_technology_state_count_ = 0;
 };
 
-class TestInitializerDelegate : public TetherService::InitializerDelegate {
+class FakeInitializerWithDestructorCallback
+    : public chromeos::tether::FakeInitializer {
  public:
-  bool is_tether_running() { return is_tether_running_; }
+  FakeInitializerWithDestructorCallback(
+      const base::Closure& destructor_callback)
+      : FakeInitializer(false /* has_asynchronous_shutdown */),
+        destructor_callback_(destructor_callback) {}
 
-  // TetherService::InitializerDelegate:
-  void InitializeTether(
+  ~FakeInitializerWithDestructorCallback() override {
+    destructor_callback_.Run();
+  }
+
+ private:
+  base::Closure destructor_callback_;
+};
+
+class TestInitializerFactory
+    : public chromeos::tether::InitializerImpl::Factory {
+ public:
+  TestInitializerFactory() {}
+
+  // Returns nullptr if no Initializer has been created or if the last one that
+  // was created has already been deleted.
+  FakeInitializerWithDestructorCallback* active_initializer() {
+    return active_initializer_;
+  }
+
+  // chromeos::tether::InitializerImpl::Factory:
+  std::unique_ptr<chromeos::tether::Initializer> BuildInstance(
       cryptauth::CryptAuthService* cryptauth_service,
       chromeos::tether::NotificationPresenter* notification_presenter,
       PrefService* pref_service,
@@ -138,13 +164,16 @@
       chromeos::NetworkConnect* network_connect,
       chromeos::NetworkConnectionHandler* network_connection_handler,
       scoped_refptr<device::BluetoothAdapter> adapter) override {
-    is_tether_running_ = true;
+    active_initializer_ = new FakeInitializerWithDestructorCallback(
+        base::Bind(&TestInitializerFactory::OnActiveInitializerDeleted,
+                   base::Unretained(this)));
+    return base::WrapUnique(active_initializer_);
   }
 
-  void ShutdownTether() override { is_tether_running_ = false; }
-
  private:
-  bool is_tether_running_ = false;
+  void OnActiveInitializerDeleted() { active_initializer_ = nullptr; }
+
+  FakeInitializerWithDestructorCallback* active_initializer_ = nullptr;
 };
 
 }  // namespace
@@ -191,6 +220,10 @@
     ON_CALL(*mock_adapter_, IsPowered())
         .WillByDefault(Invoke(this, &TetherServiceTest::IsBluetoothPowered));
     device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
+
+    test_initializer_factory_ = base::WrapUnique(new TestInitializerFactory());
+    chromeos::tether::InitializerImpl::Factory::SetInstanceForTesting(
+        test_initializer_factory_.get());
   }
 
   void TearDown() override {
@@ -211,10 +244,6 @@
         fake_session_manager_client_.get(), fake_cryptauth_service_.get(),
         network_state_handler()));
 
-    test_initializer_delegate_ = new TestInitializerDelegate();
-    tether_service_->SetInitializerDelegateForTest(
-        base::WrapUnique(test_initializer_delegate_));
-
     fake_notification_presenter_ =
         new chromeos::tether::FakeNotificationPresenter();
     tether_service_->SetNotificationPresenterForTest(
@@ -226,7 +255,7 @@
         chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
         network_state_handler()->GetTechnologyState(
             chromeos::NetworkTypePattern::Tether()));
-    EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+    VerifyTetherActiveStatus(false /* expected_active */);
 
     base::RunLoop().RunUntilIdle();
   }
@@ -287,6 +316,11 @@
                                          1);
   }
 
+  void VerifyTetherActiveStatus(bool expected_active) {
+    EXPECT_EQ(expected_active,
+              test_initializer_factory_->active_initializer() != nullptr);
+  }
+
   const content::TestBrowserThreadBundle thread_bundle_;
 
   std::unique_ptr<TestingProfile> profile_;
@@ -296,7 +330,7 @@
   std::unique_ptr<TestingPrefServiceSimple> test_pref_service_;
   std::unique_ptr<NiceMock<MockCryptAuthDeviceManager>>
       mock_cryptauth_device_manager_;
-  TestInitializerDelegate* test_initializer_delegate_;
+  std::unique_ptr<TestInitializerFactory> test_initializer_factory_;
   chromeos::tether::FakeNotificationPresenter* fake_notification_presenter_;
   std::unique_ptr<cryptauth::FakeCryptAuthService> fake_cryptauth_service_;
 
@@ -314,7 +348,7 @@
 
 TEST_F(TetherServiceTest, TestShutdown) {
   CreateTetherService();
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   ShutdownTetherService();
 
@@ -324,12 +358,46 @@
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
+}
+
+TEST_F(TetherServiceTest, TestAsyncTetherShutdown) {
+  CreateTetherService();
+
+  // Tether should be ENABLED, and there should be no AsyncShutdownTask.
+  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
+            network_state_handler()->GetTechnologyState(
+                chromeos::NetworkTypePattern::Tether()));
+  VerifyTetherActiveStatus(true /* expected_active */);
+
+  // Use an asynchronous shutdown.
+  test_initializer_factory_->active_initializer()
+      ->set_has_asynchronous_shutdown(true);
+
+  // Disable the Tether preference. This should trigger the asynchrnous
+  // shutdown.
+  SetTetherTechnologyStateEnabled(false);
+
+  // Tether should be active, but shutting down.
+  VerifyTetherActiveStatus(true /* expected_active */);
+  EXPECT_EQ(chromeos::tether::Initializer::Status::SHUTTING_DOWN,
+            test_initializer_factory_->active_initializer()->status());
+
+  // Tether should be AVAILABLE.
+  EXPECT_EQ(
+      chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
+      network_state_handler()->GetTechnologyState(
+          chromeos::NetworkTypePattern::Tether()));
+
+  // Complete the shutdown process; TetherService should delete its
+  // Initializer instance.
+  test_initializer_factory_->active_initializer()->FinishAsynchronousShutdown();
+  VerifyTetherActiveStatus(false /* expected_active */);
 }
 
 TEST_F(TetherServiceTest, TestSuspend) {
   CreateTetherService();
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   fake_power_manager_client_->SendSuspendImminent();
 
@@ -337,14 +405,14 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   fake_power_manager_client_->SendSuspendDone();
 
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   fake_power_manager_client_->SendSuspendImminent();
 
@@ -361,7 +429,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::BLE_ADVERTISING_NOT_SUPPORTED);
@@ -381,7 +449,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
   EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(
       prefs::kInstantTetheringBleAdvertisingSupported));
 
@@ -391,7 +459,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
   EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(
       prefs::kInstantTetheringBleAdvertisingSupported));
 
@@ -416,7 +484,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetIsBluetoothPowered(true);
 
@@ -424,7 +492,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::BLE_ADVERTISING_NOT_SUPPORTED);
@@ -443,7 +511,7 @@
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
   EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(
       prefs::kInstantTetheringBleAdvertisingSupported));
 
@@ -453,7 +521,7 @@
 
 TEST_F(TetherServiceTest, TestScreenLock) {
   CreateTetherService();
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   SetIsScreenLocked(true);
 
@@ -461,14 +529,14 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetIsScreenLocked(false);
 
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   SetIsScreenLocked(true);
 
@@ -501,7 +569,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::NO_AVAILABLE_HOSTS);
@@ -516,7 +584,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_PROHIBITED,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::PROHIBITED);
@@ -545,14 +613,14 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetIsBluetoothPowered(true);
 
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   SetIsBluetoothPowered(false);
 
@@ -560,7 +628,7 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::BLUETOOTH_DISABLED);
@@ -580,13 +648,13 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetTetherTechnologyStateEnabled(true);
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::ENABLED);
@@ -606,41 +674,41 @@
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Cellular()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetTetherTechnologyStateEnabled(false);
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetTetherTechnologyStateEnabled(true);
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   // Cellular enabled
   SetCellularTechnologyStateEnabled(true);
   ASSERT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Cellular()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   SetTetherTechnologyStateEnabled(false);
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
       network_state_handler()->GetTechnologyState(
           chromeos::NetworkTypePattern::Tether()));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetTetherTechnologyStateEnabled(true);
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   SetCellularTechnologyStateEnabled(false);
 
@@ -659,7 +727,7 @@
           chromeos::NetworkTypePattern::Tether()));
   EXPECT_FALSE(
       profile_->GetPrefs()->GetBoolean(prefs::kInstantTetheringEnabled));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::USER_PREFERENCE_DISABLED);
@@ -671,7 +739,7 @@
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   SetTetherTechnologyStateEnabled(false);
   EXPECT_EQ(
@@ -680,7 +748,7 @@
           chromeos::NetworkTypePattern::Tether()));
   EXPECT_FALSE(
       profile_->GetPrefs()->GetBoolean(prefs::kInstantTetheringEnabled));
-  EXPECT_FALSE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(false /* expected_active */);
 
   SetTetherTechnologyStateEnabled(true);
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
@@ -688,7 +756,7 @@
                 chromeos::NetworkTypePattern::Tether()));
   EXPECT_TRUE(
       profile_->GetPrefs()->GetBoolean(prefs::kInstantTetheringEnabled));
-  EXPECT_TRUE(test_initializer_delegate_->is_tether_running());
+  VerifyTetherActiveStatus(true /* expected_active */);
 
   ShutdownAndVerifyFinalTetherFeatureState(
       TetherService::TetherFeatureState::ENABLED);
@@ -700,6 +768,7 @@
 // state than the user preference.
 TEST_F(TetherServiceTest, TestEnabledMultipleChanges) {
   CreateTetherService();
+
   // CreateTetherService calls RunUntilIdle() so UpdateTetherTechnologyState()
   // may be called multiple times in the initialization process.
   int updated_technology_state_count =
diff --git a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
index 8654a2c2..c3345f1 100644
--- a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
+++ b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "device/base/mock_device_client.h"
 #include "device/hid/hid_device_info.h"
 #include "device/hid/mock_hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/usb/mock_usb_device.h"
 #include "device/usb/mock_usb_service.h"
 #include "extensions/browser/api/device_permissions_manager.h"
@@ -70,21 +71,21 @@
     device2_ =
         new MockUsbDevice(0, 0, "Test Manufacturer", "Test Product", "12345");
     device3_ = new MockUsbDevice(0, 0, "Test Manufacturer", "Test Product", "");
-    device4_ =
-        new HidDeviceInfo(kTestDeviceIds[0], 0, 0, "Test HID Device", "abcde",
-                          device::kHIDBusTypeUSB, std::vector<uint8_t>());
+    device4_ = new HidDeviceInfo(
+        kTestDeviceIds[0], 0, 0, "Test HID Device", "abcde",
+        device::mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>());
     device_client_.hid_service()->AddDevice(device4_);
-    device5_ =
-        new HidDeviceInfo(kTestDeviceIds[1], 0, 0, "Test HID Device", "",
-                          device::kHIDBusTypeUSB, std::vector<uint8_t>());
+    device5_ = new HidDeviceInfo(kTestDeviceIds[1], 0, 0, "Test HID Device", "",
+                                 device::mojom::HidBusType::kHIDBusTypeUSB,
+                                 std::vector<uint8_t>());
     device_client_.hid_service()->AddDevice(device5_);
-    device6_ =
-        new HidDeviceInfo(kTestDeviceIds[2], 0, 0, "Test HID Device", "67890",
-                          device::kHIDBusTypeUSB, std::vector<uint8_t>());
+    device6_ = new HidDeviceInfo(
+        kTestDeviceIds[2], 0, 0, "Test HID Device", "67890",
+        device::mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>());
     device_client_.hid_service()->AddDevice(device6_);
-    device7_ =
-        new HidDeviceInfo(kTestDeviceIds[3], 0, 0, "Test HID Device", "",
-                          device::kHIDBusTypeUSB, std::vector<uint8_t>());
+    device7_ = new HidDeviceInfo(kTestDeviceIds[3], 0, 0, "Test HID Device", "",
+                                 device::mojom::HidBusType::kHIDBusTypeUSB,
+                                 std::vector<uint8_t>());
     device_client_.hid_service()->AddDevice(device7_);
     device_client_.hid_service()->FirstEnumerationComplete();
   }
@@ -109,8 +110,8 @@
       DevicePermissionsManager::Get(env_->profile());
   manager->AllowUsbDevice(extension_->id(), device0_);
   manager->AllowUsbDevice(extension_->id(), device1_);
-  manager->AllowHidDevice(extension_->id(), device4_);
-  manager->AllowHidDevice(extension_->id(), device5_);
+  manager->AllowHidDevice(extension_->id(), *device4_->device());
+  manager->AllowHidDevice(extension_->id(), *device5_->device());
 
   DevicePermissions* device_permissions =
       manager->GetForExtension(extension_->id());
@@ -123,13 +124,15 @@
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device2_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device3_).get());
   scoped_refptr<DevicePermissionEntry> device4_entry =
-      device_permissions->FindHidDeviceEntry(device4_);
+      device_permissions->FindHidDeviceEntry(*device4_->device());
   ASSERT_TRUE(device4_entry.get());
   scoped_refptr<DevicePermissionEntry> device5_entry =
-      device_permissions->FindHidDeviceEntry(device5_);
+      device_permissions->FindHidDeviceEntry(*device5_->device());
   ASSERT_TRUE(device5_entry.get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device6_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device7_).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device6_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device7_->device()).get());
   EXPECT_EQ(4U, device_permissions->entries().size());
 
   EXPECT_EQ(base::ASCIIToUTF16(
@@ -150,26 +153,34 @@
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device1_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device2_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device3_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device5_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device6_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device7_).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device6_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device7_->device()).get());
   EXPECT_EQ(0U, device_permissions->entries().size());
 
   // After clearing device it should be possible to grant permission again.
   manager->AllowUsbDevice(extension_->id(), device0_);
   manager->AllowUsbDevice(extension_->id(), device1_);
-  manager->AllowHidDevice(extension_->id(), device4_);
-  manager->AllowHidDevice(extension_->id(), device5_);
+  manager->AllowHidDevice(extension_->id(), *device4_->device());
+  manager->AllowHidDevice(extension_->id(), *device5_->device());
 
   EXPECT_TRUE(device_permissions->FindUsbDeviceEntry(device0_).get());
   EXPECT_TRUE(device_permissions->FindUsbDeviceEntry(device1_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device2_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device3_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device5_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device6_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device7_).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device6_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device7_->device()).get());
 }
 
 TEST_F(DevicePermissionsManagerTest, DisconnectDevice) {
@@ -177,8 +188,8 @@
       DevicePermissionsManager::Get(env_->profile());
   manager->AllowUsbDevice(extension_->id(), device0_);
   manager->AllowUsbDevice(extension_->id(), device1_);
-  manager->AllowHidDevice(extension_->id(), device4_);
-  manager->AllowHidDevice(extension_->id(), device5_);
+  manager->AllowHidDevice(extension_->id(), *device4_->device());
+  manager->AllowHidDevice(extension_->id(), *device5_->device());
 
   DevicePermissions* device_permissions =
       manager->GetForExtension(extension_->id());
@@ -186,10 +197,14 @@
   EXPECT_TRUE(device_permissions->FindUsbDeviceEntry(device1_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device2_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device3_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device5_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device6_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device7_).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device6_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device7_->device()).get());
 
   device_client_.usb_service()->RemoveDevice(device0_);
   device_client_.usb_service()->RemoveDevice(device1_);
@@ -210,11 +225,15 @@
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device2_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device3_).get());
   // Device 4 is like device 0, but HID.
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device4_).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
   // Device 5 is like device 1, but HID.
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device5_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device6_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device7_).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device6_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device7_->device()).get());
 }
 
 TEST_F(DevicePermissionsManagerTest, RevokeAndRegrantAccess) {
@@ -222,8 +241,8 @@
       DevicePermissionsManager::Get(env_->profile());
   manager->AllowUsbDevice(extension_->id(), device0_);
   manager->AllowUsbDevice(extension_->id(), device1_);
-  manager->AllowHidDevice(extension_->id(), device4_);
-  manager->AllowHidDevice(extension_->id(), device5_);
+  manager->AllowHidDevice(extension_->id(), *device4_->device());
+  manager->AllowHidDevice(extension_->id(), *device5_->device());
 
   DevicePermissions* device_permissions =
       manager->GetForExtension(extension_->id());
@@ -234,10 +253,10 @@
       device_permissions->FindUsbDeviceEntry(device1_);
   ASSERT_TRUE(device1_entry.get());
   scoped_refptr<DevicePermissionEntry> device4_entry =
-      device_permissions->FindHidDeviceEntry(device4_);
+      device_permissions->FindHidDeviceEntry(*device4_->device());
   ASSERT_TRUE(device4_entry.get());
   scoped_refptr<DevicePermissionEntry> device5_entry =
-      device_permissions->FindHidDeviceEntry(device5_);
+      device_permissions->FindHidDeviceEntry(*device5_->device());
   ASSERT_TRUE(device5_entry.get());
 
   manager->RemoveEntry(extension_->id(), device0_entry);
@@ -257,27 +276,35 @@
   EXPECT_TRUE(device_permissions->FindUsbDeviceEntry(device1_).get());
 
   manager->RemoveEntry(extension_->id(), device4_entry);
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device5_).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
 
-  manager->AllowHidDevice(extension_->id(), device4_);
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device5_).get());
+  manager->AllowHidDevice(extension_->id(), *device4_->device());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
 
   manager->RemoveEntry(extension_->id(), device5_entry);
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device5_).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
 
-  manager->AllowHidDevice(extension_->id(), device5_);
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device4_).get());
-  EXPECT_TRUE(device_permissions->FindHidDeviceEntry(device5_).get());
+  manager->AllowHidDevice(extension_->id(), *device5_->device());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device4_->device()).get());
+  EXPECT_TRUE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
 }
 
 TEST_F(DevicePermissionsManagerTest, UpdateLastUsed) {
   DevicePermissionsManager* manager =
       DevicePermissionsManager::Get(env_->profile());
   manager->AllowUsbDevice(extension_->id(), device0_);
-  manager->AllowHidDevice(extension_->id(), device4_);
+  manager->AllowHidDevice(extension_->id(), *device4_->device());
 
   DevicePermissions* device_permissions =
       manager->GetForExtension(extension_->id());
@@ -285,7 +312,7 @@
       device_permissions->FindUsbDeviceEntry(device0_);
   EXPECT_TRUE(device0_entry->last_used().is_null());
   scoped_refptr<DevicePermissionEntry> device4_entry =
-      device_permissions->FindHidDeviceEntry(device4_);
+      device_permissions->FindHidDeviceEntry(*device4_->device());
   EXPECT_TRUE(device4_entry->last_used().is_null());
 
   manager->UpdateLastUsed(extension_->id(), device0_entry);
@@ -327,11 +354,14 @@
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device2_).get());
   EXPECT_FALSE(device_permissions->FindUsbDeviceEntry(device3_).get());
   scoped_refptr<DevicePermissionEntry> device4_entry =
-      device_permissions->FindHidDeviceEntry(device4_);
+      device_permissions->FindHidDeviceEntry(*device4_->device());
   ASSERT_TRUE(device4_entry.get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device5_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device6_).get());
-  EXPECT_FALSE(device_permissions->FindHidDeviceEntry(device7_).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device5_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device6_->device()).get());
+  EXPECT_FALSE(
+      device_permissions->FindHidDeviceEntry(*device7_->device()).get());
 
   EXPECT_EQ(base::ASCIIToUTF16(
                 "Test Product from Test Manufacturer (serial number ABCDE)"),
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
index 528e2f9..f098331 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
 
 #include "base/macros.h"
+#include "base/test/histogram_tester.h"
+#include "build/build_config.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -69,6 +71,7 @@
 }
 
 TEST_F(ChromeStabilityMetricsProviderTest, NotificationObserver) {
+  base::HistogramTester histogram_tester;
   ChromeStabilityMetricsProvider provider(prefs());
   std::unique_ptr<TestingProfileManager> profile_manager(
       new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
@@ -128,7 +131,13 @@
   // be executed immediately.
   provider.ProvideStabilityMetrics(&system_profile);
 
+#if defined(OS_ANDROID)
+  EXPECT_EQ(
+      2u,
+      histogram_tester.GetAllSamples("Stability.Android.RendererCrash").size());
+#else
   EXPECT_EQ(2, system_profile.stability().renderer_crash_count());
+#endif
   EXPECT_EQ(1, system_profile.stability().renderer_failed_launch_count());
   EXPECT_EQ(0, system_profile.stability().extension_renderer_crash_count());
 
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index 1e0bd755..b350fb5a 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -26,9 +26,11 @@
 #include "content/public/common/network_service.mojom.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/resource_response_info.h"
+#include "content/public/common/simple_url_loader.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_loader.mojom.h"
 #include "content/public/common/url_loader_factory.mojom.h"
+#include "content/public/test/simple_url_loader_test_helper.h"
 #include "content/public/test/test_url_loader_client.h"
 #include "mojo/common/data_pipe_utils.h"
 #include "net/base/filename_util.h"
@@ -132,56 +134,42 @@
 };
 
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, BasicRequest) {
-  content::mojom::URLLoaderPtr loader;
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
+
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = embedded_test_server()->GetURL("/echo");
-  request.method = "GET";
-  request.request_initiator = url::Origin();
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
-  ASSERT_TRUE(client.response_head().headers);
-  EXPECT_EQ(200, client.response_head().headers->response_code());
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
 
-  client.RunUntilResponseBodyArrived();
-  // TODO(mmenke):  Is blocking the UI Thread while reading the response really
-  // the best way to test requests in a browser test?
-  std::string response_body;
-  EXPECT_TRUE(mojo::common::BlockingCopyToString(client.response_body_release(),
-                                                 &response_body));
-  EXPECT_EQ("Echo", response_body);
-
-  client.RunUntilComplete();
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
+  ASSERT_TRUE(simple_loader->ResponseInfo());
+  ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
+  EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ("Echo", *simple_loader_helper.response_body());
 }
 
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, DataURL) {
-  content::mojom::URLLoaderPtr loader;
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
+
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = GURL("data:text/plain,foo");
-  request.method = "GET";
-  request.request_initiator = url::Origin();
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+
+  ASSERT_TRUE(simple_loader->ResponseInfo());
   // Data URLs don't have headers.
-  EXPECT_FALSE(client.response_head().headers);
-  EXPECT_EQ("text/plain", client.response_head().mime_type);
-
-  client.RunUntilResponseBodyArrived();
-  std::string response_body;
-  EXPECT_TRUE(mojo::common::BlockingCopyToString(client.response_body_release(),
-                                                 &response_body));
-  EXPECT_EQ("foo", response_body);
-
-  client.RunUntilComplete();
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
+  EXPECT_FALSE(simple_loader->ResponseInfo()->headers);
+  EXPECT_EQ("text/plain", simple_loader->ResponseInfo()->mime_type);
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ("foo", *simple_loader_helper.response_body());
 }
 
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, FileURL) {
@@ -194,84 +182,64 @@
   ASSERT_EQ(static_cast<int>(strlen(kFileContents)),
             base::WriteFile(file_path, kFileContents, strlen(kFileContents)));
 
-  content::mojom::URLLoaderPtr loader;
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
+
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = net::FilePathToFileURL(file_path);
-  request.method = "GET";
-  request.request_initiator = url::Origin();
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+
+  ASSERT_TRUE(simple_loader->ResponseInfo());
   // File URLs don't have headers.
-  EXPECT_FALSE(client.response_head().headers);
-
-  client.RunUntilResponseBodyArrived();
-  std::string response_body;
-  EXPECT_TRUE(mojo::common::BlockingCopyToString(client.response_body_release(),
-                                                 &response_body));
-  EXPECT_EQ(kFileContents, response_body);
-
-  client.RunUntilComplete();
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
+  EXPECT_FALSE(simple_loader->ResponseInfo()->headers);
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ(kFileContents, *simple_loader_helper.response_body());
 }
 
 // Make sure a cache is used when expected.
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, Cache) {
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
+
   // Make a request whose response should be cached.
-  content::mojom::URLLoaderPtr loader;
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = embedded_test_server()->GetURL("/cachetime");
-  request.method = "GET";
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
-  ASSERT_TRUE(client.response_head().headers);
-  EXPECT_EQ(200, client.response_head().headers->response_code());
-  client.RunUntilResponseBodyArrived();
-  std::string response_body;
-  EXPECT_TRUE(mojo::common::BlockingCopyToString(client.response_body_release(),
-                                                 &response_body));
-  EXPECT_GE(response_body.size(), 0u);
-  client.RunUntilComplete();
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
-  EXPECT_FALSE(client.completion_status().exists_in_cache);
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_GT(simple_loader_helper.response_body()->size(), 0u);
 
   // Stop the server.
   ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
 
   // Make the request again, and make sure it's cached or not, according to
   // expectations. Reuse the content::ResourceRequest, but nothing else.
-  content::mojom::URLLoaderPtr loader2;
-  content::TestURLLoaderClient client2;
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader2), 3, 2, content::mojom::kURLLoadOptionNone,
-      request, client2.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+  content::SimpleURLLoaderTestHelper simple_loader_helper2;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader2 =
+      content::SimpleURLLoader::Create();
+  simple_loader2->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper2.GetCallback());
+  simple_loader_helper2.WaitForCallback();
   if (GetHttpCacheType() == StorageType::kNone) {
-    client2.RunUntilComplete();
-    // If there's no cache, and not server running, the request should fail.
-    EXPECT_EQ(net::ERR_CONNECTION_REFUSED,
-              client2.completion_status().error_code);
+    // If there's no cache, and not server running, the request should have
+    // failed.
+    EXPECT_FALSE(simple_loader_helper2.response_body());
+    EXPECT_EQ(net::ERR_CONNECTION_REFUSED, simple_loader2->NetError());
   } else {
-    // Otherwise, the request should succeed, and return the same result as
-    // before.
-    client2.RunUntilResponseReceived();
-    ASSERT_TRUE(client2.response_head().headers);
-    EXPECT_EQ(200, client2.response_head().headers->response_code());
-    client2.RunUntilResponseBodyArrived();
-    std::string response_body2;
-    EXPECT_TRUE(mojo::common::BlockingCopyToString(
-        client2.response_body_release(), &response_body2));
-    EXPECT_EQ(response_body, response_body2);
-    client2.RunUntilComplete();
-    EXPECT_EQ(net::OK, client2.completion_status().error_code);
-    EXPECT_TRUE(client2.completion_status().exists_in_cache);
+    // Otherwise, the request should have succeeded, and returned the same
+    // result as before.
+    ASSERT_TRUE(simple_loader_helper2.response_body());
+    EXPECT_EQ(*simple_loader_helper.response_body(),
+              *simple_loader_helper2.response_body());
   }
 }
 
@@ -291,28 +259,22 @@
             base::WriteFile(save_url_file_path, test_url.spec().c_str(),
                             test_url.spec().length()));
 
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
+
   // Make a request whose response should be cached.
-  content::mojom::URLLoaderPtr loader;
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = test_url;
-  request.method = "GET";
   request.headers = "foo: foopity foo\r\n\r\n";
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
-  ASSERT_TRUE(client.response_head().headers);
-  EXPECT_EQ(200, client.response_head().headers->response_code());
-  client.RunUntilResponseBodyArrived();
-  std::string response_body;
-  EXPECT_TRUE(mojo::common::BlockingCopyToString(client.response_body_release(),
-                                                 &response_body));
-  EXPECT_EQ("foopity foo", response_body);
-  client.RunUntilComplete();
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
-  EXPECT_FALSE(client.completion_status().exists_in_cache);
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+
+  EXPECT_EQ(net::OK, simple_loader->NetError());
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ(*simple_loader_helper.response_body(), "foopity foo");
 }
 
 // Check if the URL loaded in PRE_DiskCache is still in the cache, across a
@@ -373,77 +335,52 @@
 // respected.
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationFixedPortBrowserTest,
                        TestingFixedPort) {
-  content::mojom::URLLoaderPtr loader;
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
+
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   // This URL does not use the port the embedded test server is using. The
   // command line switch should make it result in the request being directed to
   // the test server anyways.
   request.url = GURL("http://127.0.0.1/echo");
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 0, 0, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
-  ASSERT_TRUE(client.response_head().headers);
-  EXPECT_EQ(200, client.response_head().headers->response_code());
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
 
-  client.RunUntilResponseBodyArrived();
-  std::string response_body;
-  EXPECT_TRUE(mojo::common::BlockingCopyToString(client.response_body_release(),
-                                                 &response_body));
-  EXPECT_EQ("Echo", response_body);
-
-  client.RunUntilComplete();
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
+  EXPECT_EQ(net::OK, simple_loader->NetError());
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
 }
 
-INSTANTIATE_TEST_CASE_P(
-    SystemNetworkContext,
-    NetworkContextConfigurationBrowserTest,
-    ::testing::Values(TestCase({NetworkServiceState::kDisabled,
-                                NetworkContextType::kSystem}),
-                      TestCase({NetworkServiceState::kEnabled,
-                                NetworkContextType::kSystem})));
+// Instiates tests with a prefix indicating which NetworkContext is being
+// tested, and a suffix of "/0" if the network service is disabled and "/1" if
+// it's enabled.
+#define INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(TestFixture)               \
+  INSTANTIATE_TEST_CASE_P(                                                 \
+      SystemNetworkContext, TestFixture,                                   \
+      ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
+                                  NetworkContextType::kSystem}),           \
+                        TestCase({NetworkServiceState::kEnabled,           \
+                                  NetworkContextType::kSystem})));         \
+                                                                           \
+  INSTANTIATE_TEST_CASE_P(                                                 \
+      ProfileMainNetworkContext, TestFixture,                              \
+      ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
+                                  NetworkContextType::kProfile}),          \
+                        TestCase({NetworkServiceState::kEnabled,           \
+                                  NetworkContextType::kProfile})));        \
+                                                                           \
+  INSTANTIATE_TEST_CASE_P(                                                 \
+      IncognitoProfileMainNetworkContext, TestFixture,                     \
+      ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
+                                  NetworkContextType::kIncognitoProfile}), \
+                        TestCase({NetworkServiceState::kEnabled,           \
+                                  NetworkContextType::kIncognitoProfile})))
 
-INSTANTIATE_TEST_CASE_P(
-    ProfileMainNetworkContext,
-    NetworkContextConfigurationBrowserTest,
-    ::testing::Values(TestCase({NetworkServiceState::kDisabled,
-                                NetworkContextType::kProfile}),
-                      TestCase({NetworkServiceState::kEnabled,
-                                NetworkContextType::kProfile})));
-
-INSTANTIATE_TEST_CASE_P(
-    IncognitoProfileMainNetworkContext,
-    NetworkContextConfigurationBrowserTest,
-    ::testing::Values(TestCase({NetworkServiceState::kDisabled,
-                                NetworkContextType::kIncognitoProfile}),
-                      TestCase({NetworkServiceState::kEnabled,
-                                NetworkContextType::kIncognitoProfile})));
-
-INSTANTIATE_TEST_CASE_P(
-    SystemNetworkContext,
-    NetworkContextConfigurationFixedPortBrowserTest,
-    ::testing::Values(TestCase({NetworkServiceState::kDisabled,
-                                NetworkContextType::kSystem}),
-                      TestCase({NetworkServiceState::kEnabled,
-                                NetworkContextType::kSystem})));
-
-INSTANTIATE_TEST_CASE_P(
-    ProfileMainNetworkContext,
-    NetworkContextConfigurationFixedPortBrowserTest,
-    ::testing::Values(TestCase({NetworkServiceState::kDisabled,
-                                NetworkContextType::kProfile}),
-                      TestCase({NetworkServiceState::kEnabled,
-                                NetworkContextType::kProfile})));
-
-INSTANTIATE_TEST_CASE_P(
-    IncognitoProfileMainNetworkContext,
-    NetworkContextConfigurationFixedPortBrowserTest,
-    ::testing::Values(TestCase({NetworkServiceState::kDisabled,
-                                NetworkContextType::kIncognitoProfile}),
-                      TestCase({NetworkServiceState::kEnabled,
-                                NetworkContextType::kIncognitoProfile})));
+INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(NetworkContextConfigurationBrowserTest);
+INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
+    NetworkContextConfigurationFixedPortBrowserTest);
 
 }  // namespace
diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc
index 81c7794..ef77df0 100644
--- a/chrome/browser/net/predictor.cc
+++ b/chrome/browser/net/predictor.cc
@@ -961,18 +961,16 @@
 
 void Predictor::LookupFinished(const GURL& url, bool found) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  auto info_it = results_.find(url);
-  UrlInfo* info = &info_it->second;
+  UrlInfo* info = &results_[url];
   DCHECK(info->HasUrl(url));
-  bool is_marked_to_delete = info->is_marked_to_delete();
-
-  if (found)
-    info->SetFoundState();
-  else
-    info->SetNoSuchNameState();
-
-  if (is_marked_to_delete)
-    results_.erase(info_it);
+  if (info->is_marked_to_delete()) {
+    results_.erase(url);
+  } else {
+    if (found)
+      info->SetFoundState();
+    else
+      info->SetNoSuchNameState();
+  }
 }
 
 bool Predictor::WouldLikelyProxyURL(const GURL& url) {
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index f9bdff4e..a749986 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -23,11 +23,8 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/network_service.mojom.h"
-#include "content/public/common/resource_response.h"
-#include "content/public/common/resource_response_info.h"
-#include "content/public/common/url_loader.mojom.h"
 #include "content/public/common/url_loader_factory.mojom.h"
-#include "content/public/test/test_url_loader_client.h"
+#include "content/public/test/simple_url_loader_test_helper.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -73,21 +70,18 @@
 
 IN_PROC_BROWSER_TEST_P(ProfileNetworkContextServiceBrowsertest,
                        DiskCacheLocation) {
-  // Start a request, to give the network service time to create a cache
-  // directory.
-  content::mojom::URLLoaderPtr loader;
+  // Run a request that caches the response, to give the network service time to
+  // create a cache directory.
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = embedded_test_server()->GetURL("/cachetime");
-  request.method = "GET";
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
-  ASSERT_TRUE(client.response_head().headers);
-  EXPECT_EQ(200, client.response_head().headers->response_code());
-  client.RunUntilResponseBodyArrived();
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+  ASSERT_TRUE(simple_loader_helper.response_body());
 
   base::FilePath expected_cache_path;
   chrome::GetUserCacheDirectory(browser()->profile()->GetPath(),
@@ -126,21 +120,18 @@
   ASSERT_EQ(TempPath(), browser()->profile()->GetPrefs()->GetFilePath(
                             prefs::kDiskCacheDir));
 
-  // Start a request, to give the network service time to create a cache
-  // directory.
-  content::mojom::URLLoaderPtr loader;
+  // Run a request that caches the response, to give the network service time to
+  // create a cache directory.
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create();
   content::ResourceRequest request;
-  content::TestURLLoaderClient client;
   request.url = embedded_test_server()->GetURL("/cachetime");
-  request.method = "GET";
-  loader_factory()->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 2, 1, content::mojom::kURLLoadOptionNone,
-      request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-  client.RunUntilResponseReceived();
-  ASSERT_TRUE(client.response_head().headers);
-  EXPECT_EQ(200, client.response_head().headers->response_code());
-  client.RunUntilResponseBodyArrived();
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      request, loader_factory(), TRAFFIC_ANNOTATION_FOR_TESTS,
+      simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+  ASSERT_TRUE(simple_loader_helper.response_body());
 
   // Cache directory should now exist.
   base::FilePath expected_cache_path =
diff --git a/chrome/browser/net/url_info.cc b/chrome/browser/net/url_info.cc
index 83a8ae3..e46eaf555 100644
--- a/chrome/browser/net/url_info.cc
+++ b/chrome/browser/net/url_info.cc
@@ -159,7 +159,7 @@
 }
 
 void UrlInfo::SetFoundState() {
-  DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_);
+  DCHECK(ASSIGNED == state_);
   state_ = FOUND;
   resolve_duration_ = GetDuration();
   const TimeDelta max_duration = MaxNonNetworkDnsLookupDuration();
@@ -172,7 +172,7 @@
 }
 
 void UrlInfo::SetNoSuchNameState() {
-  DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_);
+  DCHECK(ASSIGNED == state_);
   state_ = NO_SUCH_NAME;
   resolve_duration_ = GetDuration();
 #ifndef NDEBUG
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 6dd8d3a..01b975c 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -4028,7 +4028,7 @@
 }
 
 // Tests that if the timeout expires before the network time fetch
-// returns, then a normal SSL intersitial is shown.
+// returns, then a normal SSL interstitial is shown.
 IN_PROC_BROWSER_TEST_F(SSLNetworkTimeBrowserTest,
                        TimeoutExpiresBeforeFetchCompletes) {
   ASSERT_TRUE(https_server_expired_.Start());
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index 89e929ff..b584ae7 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -184,8 +184,7 @@
     const gfx::Size& page_size,
     const scoped_refptr<base::RefCountedBytes>& print_data,
     const PrinterHandler::PrintCallback& callback) {
-  std::unique_ptr<extensions::PrinterProviderPrintJob> print_job(
-      new extensions::PrinterProviderPrintJob());
+  auto print_job = base::MakeUnique<extensions::PrinterProviderPrintJob>();
   print_job->printer_id = destination_id;
   print_job->job_title = job_title;
   print_job->ticket_json = ticket_json;
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index bbe4197..b45c0b7 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -10,6 +10,7 @@
 #include <memory>
 #include <queue>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
@@ -457,18 +458,19 @@
 
 class ExtensionPrinterHandlerTest : public testing::Test {
  public:
-  ExtensionPrinterHandlerTest() : pwg_raster_converter_(NULL) {}
+  ExtensionPrinterHandlerTest() = default;
   ~ExtensionPrinterHandlerTest() override = default;
 
   void SetUp() override {
     extensions::PrinterProviderAPIFactory::GetInstance()->SetTestingFactory(
         env_.profile(), &BuildTestingPrinterProviderAPI);
-    extension_printer_handler_.reset(
-        new ExtensionPrinterHandler(env_.profile()));
+    extension_printer_handler_ =
+        base::MakeUnique<ExtensionPrinterHandler>(env_.profile());
 
-    pwg_raster_converter_ = new FakePWGRasterConverter();
+    auto pwg_raster_converter = base::MakeUnique<FakePWGRasterConverter>();
+    pwg_raster_converter_ = pwg_raster_converter.get();
     extension_printer_handler_->SetPWGRasterConverterForTesting(
-        std::unique_ptr<PWGRasterConverter>(pwg_raster_converter_));
+        std::move(pwg_raster_converter));
   }
 
  protected:
@@ -486,7 +488,8 @@
   TestExtensionEnvironment env_;
   std::unique_ptr<ExtensionPrinterHandler> extension_printer_handler_;
 
-  FakePWGRasterConverter* pwg_raster_converter_;
+  // Owned by |extension_printer_handler_|.
+  FakePWGRasterConverter* pwg_raster_converter_ = nullptr;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ExtensionPrinterHandlerTest);
@@ -545,11 +548,11 @@
 }
 
 TEST_F(ExtensionPrinterHandlerTest, GetUsbPrinters) {
-  scoped_refptr<MockUsbDevice> device0 =
-      new MockUsbDevice(0, 0, "Google", "USB Printer", "");
+  auto device0 =
+      base::MakeRefCounted<MockUsbDevice>(0, 0, "Google", "USB Printer", "");
   usb_service().AddDevice(device0);
-  scoped_refptr<MockUsbDevice> device1 =
-      new MockUsbDevice(0, 1, "Google", "USB Printer", "");
+  auto device1 =
+      base::MakeRefCounted<MockUsbDevice>(0, 1, "Google", "USB Printer", "");
   usb_service().AddDevice(device1);
 
   const Extension* extension_1 = env_.MakeExtension(
@@ -666,8 +669,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -704,8 +707,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -730,8 +733,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -769,8 +772,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -822,8 +825,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -875,8 +878,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -904,8 +907,8 @@
   bool success = false;
   std::string status;
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -926,8 +929,8 @@
 
   pwg_raster_converter_->FailConversion();
 
-  scoped_refptr<base::RefCountedBytes> print_data(
-      new base::RefCountedBytes(kPrintData, kPrintDataLength));
+  auto print_data =
+      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
@@ -942,8 +945,8 @@
 }
 
 TEST_F(ExtensionPrinterHandlerTest, GrantUsbPrinterAccess) {
-  scoped_refptr<MockUsbDevice> device =
-      new MockUsbDevice(0, 0, "Google", "USB Printer", "");
+  auto device =
+      base::MakeRefCounted<MockUsbDevice>(0, 0, "Google", "USB Printer", "");
   usb_service().AddDevice(device);
 
   size_t call_count = 0;
@@ -974,8 +977,8 @@
 }
 
 TEST_F(ExtensionPrinterHandlerTest, GrantUsbPrinterAccess_Reset) {
-  scoped_refptr<MockUsbDevice> device =
-      new MockUsbDevice(0, 0, "Google", "USB Printer", "");
+  auto device =
+      base::MakeRefCounted<MockUsbDevice>(0, 0, "Google", "USB Printer", "");
   usb_service().AddDevice(device);
 
   size_t call_count = 0;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 6805fd3..5b7b1b6 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -158,7 +158,7 @@
     return true;
   }
   // Invalid request.
-  scoped_refptr<base::RefCountedBytes> empty_bytes(new base::RefCountedBytes);
+  auto empty_bytes = base::MakeRefCounted<base::RefCountedBytes>();
   callback.Run(empty_bytes.get());
   return true;
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc
index 12910fa..b374eda8 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc
@@ -28,11 +28,11 @@
 
 namespace {
 
-base::RefCountedBytes* CreateTestData() {
+scoped_refptr<base::RefCountedBytes> CreateTestData() {
   const unsigned char blob1[] =
       "12346102356120394751634516591348710478123649165419234519234512349134";
   std::vector<unsigned char> preview_data(blob1, blob1 + sizeof(blob1));
-  return new base::RefCountedBytes(preview_data);
+  return base::MakeRefCounted<base::RefCountedBytes>(preview_data);
 }
 
 bool IsShowingWebContentsModalDialog(WebContents* tab) {
@@ -105,7 +105,7 @@
   EXPECT_EQ(dummy_data.get(), data.get());
 
   // This should not cause any memory leaks.
-  dummy_data = new base::RefCountedBytes();
+  dummy_data = base::MakeRefCounted<base::RefCountedBytes>();
   preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX,
                                           dummy_data.get());
 
diff --git a/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc b/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc
index f06e3f2..06de50c 100644
--- a/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc
@@ -23,7 +23,7 @@
 
  protected:
   void SetUp() override {
-    test_backend_ = new TestPrintBackend();
+    test_backend_ = base::MakeRefCounted<TestPrintBackend>();
     PrintBackend::SetPrintBackendForTesting(test_backend_.get());
   }
 
diff --git a/chrome/browser/ui/webui/print_preview/sticky_settings.cc b/chrome/browser/ui/webui/print_preview/sticky_settings.cc
index b2507188..222469e 100644
--- a/chrome/browser/ui/webui/print_preview/sticky_settings.cc
+++ b/chrome/browser/ui/webui/print_preview/sticky_settings.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/print_preview/sticky_settings.h"
 
+#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
@@ -22,14 +23,18 @@
 
 StickySettings::~StickySettings() {}
 
-void StickySettings::StoreAppState(const std::string& data) {
-  printer_app_state_.reset(new std::string(data));
+const std::string* StickySettings::printer_app_state() const {
+  return printer_app_state_ ? &printer_app_state_.value() : nullptr;
 }
 
-void StickySettings::SaveInPrefs(PrefService* prefs) {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+void StickySettings::StoreAppState(const std::string& data) {
+  printer_app_state_ = base::make_optional(data);
+}
+
+void StickySettings::SaveInPrefs(PrefService* prefs) const {
+  auto value = base::MakeUnique<base::DictionaryValue>();
   if (printer_app_state_)
-    value->SetString(kSettingAppState, *printer_app_state_);
+    value->SetString(kSettingAppState, printer_app_state_.value());
   prefs->Set(prefs::kPrintPreviewStickySettings, *value);
 }
 
@@ -41,13 +46,10 @@
     StoreAppState(buffer);
 }
 
+// static
 void StickySettings::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterDictionaryPref(prefs::kPrintPreviewStickySettings);
 }
 
-std::string* StickySettings::printer_app_state() {
-  return printer_app_state_.get();
-}
-
 }  // namespace printing
diff --git a/chrome/browser/ui/webui/print_preview/sticky_settings.h b/chrome/browser/ui/webui/print_preview/sticky_settings.h
index ddc142b..e2a10d5c 100644
--- a/chrome/browser/ui/webui/print_preview/sticky_settings.h
+++ b/chrome/browser/ui/webui/print_preview/sticky_settings.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_STICKY_SETTINGS_H_
 #define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_STICKY_SETTINGS_H_
 
-#include <memory>
 #include <string>
 
+#include "base/optional.h"
 #include "printing/print_job_constants.h"
 
 class PrefService;
@@ -26,17 +26,18 @@
   StickySettings();
   ~StickySettings();
 
-  std::string* printer_app_state();
+  const std::string* printer_app_state() const;
 
   // Stores app state for the last used printer.
   void StoreAppState(const std::string& app_state);
 
-  void SaveInPrefs(PrefService* profile);
+  void SaveInPrefs(PrefService* profile) const;
   void RestoreFromPrefs(PrefService* profile);
+
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
  private:
-  std::unique_ptr<std::string> printer_app_state_;
+  base::Optional<std::string> printer_app_state_;
 };
 
 }  // namespace printing
diff --git a/chrome/browser/vr/ui_scene_manager.cc b/chrome/browser/vr/ui_scene_manager.cc
index c7b9c26..406854c 100644
--- a/chrome/browser/vr/ui_scene_manager.cc
+++ b/chrome/browser/vr/ui_scene_manager.cc
@@ -682,6 +682,10 @@
   ConfigureScene();
 }
 
+void UiSceneManager::OnWebVrTimedOut() {
+  browser_->ExitPresent();
+}
+
 void UiSceneManager::OnProjMatrixChanged(const gfx::Transform& proj_matrix) {
   // Determine if the projected size of the content quad changed more than a
   // given threshold. If so, propagate this info so that the content's
diff --git a/chrome/browser/vr/ui_scene_manager.h b/chrome/browser/vr/ui_scene_manager.h
index 766ff5a..429a2b3 100644
--- a/chrome/browser/vr/ui_scene_manager.h
+++ b/chrome/browser/vr/ui_scene_manager.h
@@ -64,6 +64,7 @@
   void OnAppButtonClicked();
   void OnAppButtonGesturePerformed(UiInterface::Direction direction);
   void OnWebVrFrameAvailable();
+  void OnWebVrTimedOut();
   void OnProjMatrixChanged(const gfx::Transform& proj_matrix);
 
   void SetExitVrPromptEnabled(bool enabled, UiUnsupportedMode reason);
diff --git a/chrome/common/stack_sampling_configuration.cc b/chrome/common/stack_sampling_configuration.cc
index e28d59d9..b27023c 100644
--- a/chrome/common/stack_sampling_configuration.cc
+++ b/chrome/common/stack_sampling_configuration.cc
@@ -215,9 +215,9 @@
     case version_info::Channel::CANARY:
       return ChooseConfiguration({{PROFILE_BROWSER_PROCESS, 0},
                                   {PROFILE_GPU_PROCESS, 0},
-                                  {PROFILE_BROWSER_AND_GPU_PROCESS, 10},
-                                  {PROFILE_CONTROL, 10},
-                                  {PROFILE_DISABLED, 80}});
+                                  {PROFILE_BROWSER_AND_GPU_PROCESS, 50},
+                                  {PROFILE_CONTROL, 50},
+                                  {PROFILE_DISABLED, 0}});
 #endif
 
     default:
diff --git a/chrome/installer/linux/debian/dist-package-versions.json b/chrome/installer/linux/debian/dist-package-versions.json
new file mode 100644
index 0000000..8537de3
--- /dev/null
+++ b/chrome/installer/linux/debian/dist-package-versions.json
@@ -0,0 +1,240 @@
+{
+    "Debian 10 (Buster)": {
+        "gconf-service": "3.2.6-4+b1",
+        "libasound2": "1.1.3-5",
+        "libatk1.0-0": "2.24.0-1",
+        "libc6": "2.24-14",
+        "libcairo2": "1.14.10-1",
+        "libcups2": "2.2.4-3",
+        "libdbus-1-3": "1.11.16+really1.10.22-1",
+        "libexpat1": "2.2.3-1",
+        "libfontconfig1": "2.12.3-0.2",
+        "libgcc1": "1:7.2.0-1",
+        "libgconf-2-4": "3.2.6-4+b1",
+        "libgdk-pixbuf2.0-0": "2.36.5-2",
+        "libglib2.0-0": "2.53.4-3",
+        "libgtk-3-0": "3.22.18-1",
+        "libnspr4": "2:4.16-1",
+        "libnss3": "2:3.31-1",
+        "libpango-1.0-0": "1.40.6-1",
+        "libpangocairo-1.0-0": "1.40.6-1",
+        "libstdc++6": "7.2.0-1",
+        "libx11-6": "2:1.6.4-3",
+        "libx11-xcb1": "2:1.6.4-3",
+        "libxcb1": "1.12-1",
+        "libxcomposite1": "1:0.4.4-2",
+        "libxcursor1": "1:1.1.14-1+b4",
+        "libxdamage1": "1:1.1.4-3",
+        "libxext6": "2:1.3.3-1+b2",
+        "libxfixes3": "1:5.0.3-1",
+        "libxi6": "2:1.7.9-1",
+        "libxrandr2": "2:1.5.1-1",
+        "libxrender1": "1:0.9.10-1",
+        "libxss1": "1:1.2.2-1+b2",
+        "libxtst6": "2:1.2.3-1"
+    },
+    "Debian 8 (Jessie)": {
+        "gconf-service": "3.2.6-3",
+        "libasound2": "1.0.28-1",
+        "libatk1.0-0": "2.14.0-1",
+        "libc6": "2.19-18+deb8u10",
+        "libcairo2": "1.14.0-2.1+deb8u2",
+        "libcups2": "1.7.5-11+deb8u1",
+        "libdbus-1-3": "1.8.22-0+deb8u1",
+        "libexpat1": "2.1.0-6+deb8u4",
+        "libfontconfig1": "2.11.0-6.3+deb8u1",
+        "libgcc1": "1:4.9.2-10",
+        "libgconf-2-4": "3.2.6-3",
+        "libgdk-pixbuf2.0-0": "2.31.1-2+deb8u5",
+        "libglib2.0-0": "2.42.1-1+b1",
+        "libgtk-3-0": "3.14.5-1+deb8u1",
+        "libnspr4": "2:4.12-1+debu8u1",
+        "libnss3": "2:3.26-1+debu8u2",
+        "libpango-1.0-0": "1.36.8-3",
+        "libpangocairo-1.0-0": "1.36.8-3",
+        "libstdc++6": "4.9.2-10",
+        "libx11-6": "2:1.6.2-3",
+        "libx11-xcb1": "2:1.6.2-3",
+        "libxcb1": "1.10-3+b1",
+        "libxcomposite1": "1:0.4.4-1",
+        "libxcursor1": "1:1.1.14-1+b1",
+        "libxdamage1": "1:1.1.4-2+b1",
+        "libxext6": "2:1.3.3-1",
+        "libxfixes3": "1:5.0.1-2+b2",
+        "libxi6": "2:1.7.4-1+b2",
+        "libxrandr2": "2:1.4.2-1+b1",
+        "libxrender1": "1:0.9.8-1+b1",
+        "libxss1": "1:1.2.2-1",
+        "libxtst6": "2:1.2.2-1+b1"
+    },
+    "Debian 9 (Stretch)": {
+        "gconf-service": "3.2.6-4+b1",
+        "libasound2": "1.1.3-5",
+        "libatk1.0-0": "2.22.0-1",
+        "libc6": "2.24-11+deb9u1",
+        "libcairo2": "1.14.8-1",
+        "libcups2": "2.2.1-8",
+        "libdbus-1-3": "1.10.18-1",
+        "libexpat1": "2.2.0-2+deb9u1",
+        "libfontconfig1": "2.11.0-6.7+b1",
+        "libgcc1": "1:6.3.0-18",
+        "libgconf-2-4": "3.2.6-4+b1",
+        "libgdk-pixbuf2.0-0": "2.36.5-2",
+        "libglib2.0-0": "2.50.3-2",
+        "libgtk-3-0": "3.22.11-1",
+        "libnspr4": "2:4.12-6",
+        "libnss3": "2:3.26.2-1.1",
+        "libpango-1.0-0": "1.40.5-1",
+        "libpangocairo-1.0-0": "1.40.5-1",
+        "libstdc++6": "6.3.0-18",
+        "libx11-6": "2:1.6.4-3",
+        "libx11-xcb1": "2:1.6.4-3",
+        "libxcb1": "1.12-1",
+        "libxcomposite1": "1:0.4.4-2",
+        "libxcursor1": "1:1.1.14-1+b4",
+        "libxdamage1": "1:1.1.4-2+b3",
+        "libxext6": "2:1.3.3-1+b2",
+        "libxfixes3": "1:5.0.3-1",
+        "libxi6": "2:1.7.9-1",
+        "libxrandr2": "2:1.5.1-1",
+        "libxrender1": "1:0.9.10-1",
+        "libxss1": "1:1.2.2-1",
+        "libxtst6": "2:1.2.3-1"
+    },
+    "Ubuntu 14.04 (Trusty)": {
+        "gconf-service": "3.2.6-0ubuntu2",
+        "libasound2": "1.0.27.2-3ubuntu7",
+        "libatk1.0-0": "2.10.0-2ubuntu2",
+        "libc6": "2.19-0ubuntu6.13",
+        "libcairo2": "1.13.0~20140204-0ubuntu1.1",
+        "libcups2": "1.7.2-0ubuntu1.7",
+        "libdbus-1-3": "1.6.18-0ubuntu4.4",
+        "libexpat1": "2.1.0-4ubuntu1.4",
+        "libfontconfig1": "2.11.0-0ubuntu4.2",
+        "libgcc1": "1:4.9.3-0ubuntu4",
+        "libgconf-2-4": "3.2.6-0ubuntu2",
+        "libgdk-pixbuf2.0-0": "2.30.7-0ubuntu1.6",
+        "libglib2.0-0": "2.40.2-0ubuntu1",
+        "libgtk-3-0": "3.10.8-0ubuntu1.4",
+        "libnspr4": "2:4.13.1-0ubuntu0.14.04.1",
+        "libnss3": "2:3.28.4-0ubuntu0.14.04.2",
+        "libpango-1.0-0": "1.36.3-1ubuntu1.1",
+        "libpangocairo-1.0-0": "1.36.3-1ubuntu1.1",
+        "libstdc++6": "4.8.4-2ubuntu1~14.04.3",
+        "libx11-6": "2:1.6.2-1ubuntu2",
+        "libx11-xcb1": "2:1.6.2-1ubuntu2",
+        "libxcb1": "1.10-2ubuntu1",
+        "libxcomposite1": "1:0.4.4-1",
+        "libxcursor1": "1:1.1.14-1",
+        "libxdamage1": "1:1.1.4-1ubuntu1",
+        "libxext6": "2:1.3.2-1ubuntu0.0.14.04.1",
+        "libxfixes3": "1:5.0.1-1ubuntu1.1",
+        "libxi6": "2:1.7.1.901-1ubuntu1.1",
+        "libxrandr2": "2:1.5.0-1~trusty1",
+        "libxrender1": "1:0.9.8-1build0.14.04.1",
+        "libxss1": "1:1.2.2-1",
+        "libxtst6": "2:1.2.2-1"
+    },
+    "Ubuntu 16.04 (Xenial)": {
+        "gconf-service": "3.2.6-3ubuntu6",
+        "libasound2": "1.1.0-0ubuntu1",
+        "libatk1.0-0": "2.18.0-1",
+        "libc6": "2.23-0ubuntu9",
+        "libcairo2": "1.14.6-1",
+        "libcups2": "2.1.3-4",
+        "libdbus-1-3": "1.10.6-1ubuntu3.1",
+        "libexpat1": "2.1.0-7ubuntu0.16.04.3",
+        "libfontconfig1": "2.11.94-0ubuntu1.1",
+        "libgcc1": "1:6.0.1-0ubuntu1",
+        "libgconf-2-4": "3.2.6-3ubuntu6",
+        "libgdk-pixbuf2.0-0": "2.32.2-1ubuntu1.2",
+        "libglib2.0-0": "2.48.2-0ubuntu1",
+        "libgtk-3-0": "3.18.9-1ubuntu3.3",
+        "libnspr4": "2:4.13.1-0ubuntu0.16.04.1",
+        "libnss3": "2:3.28.4-0ubuntu0.16.04.2",
+        "libpango-1.0-0": "1.38.1-1",
+        "libpangocairo-1.0-0": "1.38.1-1",
+        "libstdc++6": "5.4.0-6ubuntu1~16.04.4",
+        "libx11-6": "2:1.6.3-1ubuntu2",
+        "libx11-xcb1": "2:1.6.3-1ubuntu2",
+        "libxcb1": "1.11.1-1ubuntu1",
+        "libxcomposite1": "1:0.4.4-1",
+        "libxcursor1": "1:1.1.14-1",
+        "libxdamage1": "1:1.1.4-2",
+        "libxext6": "2:1.3.3-1",
+        "libxfixes3": "1:5.0.1-2",
+        "libxi6": "2:1.7.6-1",
+        "libxrandr2": "2:1.5.0-1",
+        "libxrender1": "1:0.9.9-0ubuntu1",
+        "libxss1": "1:1.2.2-1",
+        "libxtst6": "2:1.2.2-1"
+    },
+    "Ubuntu 17.04 (Zesty)": {
+        "gconf-service": "3.2.6-3ubuntu7",
+        "libasound2": "1.1.3-5",
+        "libatk1.0-0": "2.22.0-1",
+        "libc6": "2.24-9ubuntu2.2",
+        "libcairo2": "1.14.8-1",
+        "libcups2": "2.2.2-1ubuntu1",
+        "libdbus-1-3": "1.10.10-1ubuntu2",
+        "libexpat1": "2.2.0-2ubuntu0.1",
+        "libfontconfig1": "2.11.94-0ubuntu2",
+        "libgcc1": "1:6.3.0-12ubuntu2",
+        "libgconf-2-4": "3.2.6-3ubuntu7",
+        "libgdk-pixbuf2.0-0": "2.36.5-3",
+        "libglib2.0-0": "2.52.0-1",
+        "libgtk-3-0": "3.22.11-0ubuntu3",
+        "libnspr4": "2:4.13.1-0ubuntu0.17.04.1",
+        "libnss3": "2:3.28.4-0ubuntu0.17.04.2",
+        "libpango-1.0-0": "1.40.4-1",
+        "libpangocairo-1.0-0": "1.40.4-1",
+        "libstdc++6": "6.3.0-12ubuntu2",
+        "libx11-6": "2:1.6.4-3",
+        "libx11-xcb1": "2:1.6.4-3",
+        "libxcb1": "1.11.1-1ubuntu1",
+        "libxcomposite1": "1:0.4.4-2",
+        "libxcursor1": "1:1.1.14-1",
+        "libxdamage1": "1:1.1.4-2",
+        "libxext6": "2:1.3.3-1",
+        "libxfixes3": "1:5.0.3-1",
+        "libxi6": "2:1.7.9-1",
+        "libxrandr2": "2:1.5.1-1",
+        "libxrender1": "1:0.9.10-1",
+        "libxss1": "1:1.2.2-1",
+        "libxtst6": "2:1.2.3-1"
+    },
+    "Ubuntu 17.10 (Artful)": {
+        "gconf-service": "3.2.6-4ubuntu1",
+        "libasound2": "1.1.3-5",
+        "libatk1.0-0": "2.25.90-0ubuntu1",
+        "libc6": "2.24-12ubuntu1",
+        "libcairo2": "1.14.10-1",
+        "libcups2": "2.2.4-5",
+        "libdbus-1-3": "1.10.22-1ubuntu1",
+        "libexpat1": "2.2.3-1",
+        "libfontconfig1": "2.11.94-0ubuntu2",
+        "libgcc1": "1:7.2.0-1ubuntu2",
+        "libgconf-2-4": "3.2.6-4ubuntu1",
+        "libgdk-pixbuf2.0-0": "2.36.5-3",
+        "libglib2.0-0": "2.53.6-1ubuntu1",
+        "libgtk-3-0": "3.22.19-0ubuntu1",
+        "libnspr4": "2:4.16-1ubuntu1",
+        "libnss3": "2:3.32-1ubuntu2",
+        "libpango-1.0-0": "1.40.11-1",
+        "libpangocairo-1.0-0": "1.40.11-1",
+        "libstdc++6": "7.2.0-1ubuntu2",
+        "libx11-6": "2:1.6.4-3",
+        "libx11-xcb1": "2:1.6.4-3",
+        "libxcb1": "1.11.1-1ubuntu1",
+        "libxcomposite1": "1:0.4.4-2",
+        "libxcursor1": "1:1.1.14-3",
+        "libxdamage1": "1:1.1.4-3",
+        "libxext6": "2:1.3.3-1",
+        "libxfixes3": "1:5.0.3-1",
+        "libxi6": "2:1.7.9-1",
+        "libxrandr2": "2:1.5.1-1",
+        "libxrender1": "1:0.9.10-1",
+        "libxss1": "1:1.2.2-1",
+        "libxtst6": "2:1.2.3-1"
+    }
+}
diff --git a/chrome/installer/linux/debian/download-repo-signing-keys.sh b/chrome/installer/linux/debian/download-repo-signing-keys.sh
new file mode 100755
index 0000000..d8c2596d
--- /dev/null
+++ b/chrome/installer/linux/debian/download-repo-signing-keys.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -o nounset
+set -o errexit
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+KEY_FINGERS=(
+  # Debian Archive Automatic Signing Key (7.0/wheezy)
+  "A1BD8E9D78F7FE5C3E65D8AF8B48AD6246925553"
+  # Debian Archive Automatic Signing Key (8/jessie)
+  "126C0D24BD8A2942CC7DF8AC7638D0442B90D010"
+  # Debian Security Archive Automatic Signing Key (8/jessie)
+  "D21169141CECD440F2EB8DDA9D6D8F6BC857C906"
+  # Jessie Stable Release Key
+  "75DDC3C4A499F1A18CB5F3C8CBF8D6FD518E17E1"
+  # Debian Stable Release Key (9/stretch)
+  "067E3C456BAE240ACEE88F6FEF0F382A1A7B6500"
+  # Ubuntu Archive Automatic Signing Key
+  "630239CC130E1A7FD81A27B140976EAF437D05B5"
+  # Ubuntu Archive Automatic Signing Key (2012)
+  "790BC7277767219C42C86F933B4FE6ACC0B21F32"
+)
+
+gpg --keyserver pgp.mit.edu --recv-keys ${KEY_FINGERS[@]}
+gpg --output "${SCRIPT_DIR}/repo_signing_keys.gpg" --export ${KEY_FINGERS[@]}
diff --git a/chrome/installer/linux/debian/repo_signing_keys.gpg b/chrome/installer/linux/debian/repo_signing_keys.gpg
new file mode 100644
index 0000000..9cf5e93
--- /dev/null
+++ b/chrome/installer/linux/debian/repo_signing_keys.gpg
Binary files differ
diff --git a/chrome/installer/linux/debian/update-dist-package-versions.py b/chrome/installer/linux/debian/update-dist-package-versions.py
new file mode 100755
index 0000000..7d49b6f
--- /dev/null
+++ b/chrome/installer/linux/debian/update-dist-package-versions.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Downloads package lists and records package versions into
+dist-package-versions.json.
+"""
+
+import binascii
+import cStringIO
+import gzip
+import hashlib
+import json
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import urllib2
+
+SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+SUPPORTED_DEBIAN_RELEASES = {
+    'Debian 8 (Jessie)': 'jessie',
+    'Debian 9 (Stretch)': 'stretch',
+    'Debian 10 (Buster)': 'buster',
+}
+
+SUPPORTED_UBUNTU_RELEASES = {
+    'Ubuntu 14.04 (Trusty)': 'trusty',
+    'Ubuntu 16.04 (Xenial)': 'xenial',
+    'Ubuntu 17.04 (Zesty)': 'zesty',
+    'Ubuntu 17.10 (Artful)': 'artful',
+}
+
+PACKAGE_FILTER = set([
+    "gconf-service",
+    "libasound2",
+    "libatk1.0-0",
+    "libc6",
+    "libcairo2",
+    "libcups2",
+    "libdbus-1-3",
+    "libexpat1",
+    "libfontconfig1",
+    "libgcc1",
+    "libgconf-2-4",
+    "libgdk-pixbuf2.0-0",
+    "libglib2.0-0",
+    "libgtk-3-0",
+    "libnspr4",
+    "libnss3",
+    "libpango-1.0-0",
+    "libpangocairo-1.0-0",
+    "libstdc++6",
+    "libx11-6",
+    "libx11-xcb1",
+    "libxcb1",
+    "libxcomposite1",
+    "libxcursor1",
+    "libxdamage1",
+    "libxext6",
+    "libxfixes3",
+    "libxi6",
+    "libxrandr2",
+    "libxrender1",
+    "libxss1",
+    "libxtst6",
+])
+
+def create_temp_file_from_data(data):
+  file = tempfile.NamedTemporaryFile()
+  file.write(data)
+  file.flush()
+  return file
+
+if sys.platform != 'linux2':
+  print >> sys.stderr, "Only supported on Linux."
+  sys.exit(1)
+
+deb_sources = {}
+for release in SUPPORTED_DEBIAN_RELEASES:
+  codename = SUPPORTED_DEBIAN_RELEASES[release]
+  deb_sources[release] = [
+      {
+          "base_url": url,
+          "packages": [ "main/binary-amd64/Packages.gz" ]
+      } for url in [
+          "http://ftp.us.debian.org/debian/dists/%s" % codename,
+          "http://ftp.us.debian.org/debian/dists/%s-updates" % codename,
+          "http://security.debian.org/dists/%s/updates" % codename,
+      ]
+  ]
+for release in SUPPORTED_UBUNTU_RELEASES:
+  codename = SUPPORTED_UBUNTU_RELEASES[release]
+  repos = ['main', 'universe']
+  deb_sources[release] = [
+      {
+          "base_url": url,
+          "packages": [
+              "%s/binary-amd64/Packages.gz" % repo for repo in repos
+          ],
+      } for url in [
+          "http://us.archive.ubuntu.com/ubuntu/dists/%s" % codename,
+          "http://us.archive.ubuntu.com/ubuntu/dists/%s-updates" % codename,
+          "http://security.ubuntu.com/ubuntu/dists/%s-security" % codename,
+      ]
+  ]
+
+distro_package_versions = {}
+package_regex = re.compile('^Package: (.*)$')
+version_regex = re.compile('^Version: (.*)$')
+for distro in deb_sources:
+  package_versions = {}
+  for source in deb_sources[distro]:
+    base_url = source["base_url"]
+    release = urllib2.urlopen("%s/Release" % base_url).read()
+    release_gpg = urllib2.urlopen("%s/Release.gpg" % base_url).read()
+    keyring = os.path.join(SCRIPT_DIR, 'repo_signing_keys.gpg')
+    release_file = create_temp_file_from_data(release)
+    release_gpg_file = create_temp_file_from_data(release_gpg)
+    subprocess.check_output(['gpgv', '--quiet', '--keyring', keyring,
+                             release_gpg_file.name, release_file.name])
+    for packages_gz in source["packages"]:
+      gz_data = urllib2.urlopen("%s/%s" % (base_url, packages_gz)).read()
+
+      sha = hashlib.sha256()
+      sha.update(gz_data)
+      digest = binascii.hexlify(sha.digest())
+      matches = [line for line in release.split('\n')
+                 if digest in line and packages_gz in line]
+      assert len(matches) == 1
+
+      zipped_file = cStringIO.StringIO()
+      zipped_file.write(gz_data)
+      zipped_file.seek(0)
+      contents = gzip.GzipFile(fileobj=zipped_file, mode='rb').read()
+      package = ''
+      for line in contents.split('\n'):
+        if line.startswith('Package: '):
+          match = re.search(package_regex, line)
+          package = match.group(1)
+        elif line.startswith('Version: '):
+          match = re.search(version_regex, line)
+          version = match.group(1)
+          if package in PACKAGE_FILTER:
+            package_versions[package] = version
+  distro_package_versions[distro] = package_versions
+
+with open(os.path.join(SCRIPT_DIR, 'dist-package-versions.json'), 'w') as f:
+  f.write(json.dumps(distro_package_versions, sort_keys=True, indent=4,
+                     separators=(',', ': ')))
+  f.write('\n')
diff --git a/chrome/installer/linux/rpm/dist-package-provides.json b/chrome/installer/linux/rpm/dist-package-provides.json
new file mode 100644
index 0000000..79ad0ed
--- /dev/null
+++ b/chrome/installer/linux/rpm/dist-package-provides.json
@@ -0,0 +1,1139 @@
+{
+    "Fedora 25": [
+        "ld-linux-x86-64.so.2()(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.4)(64bit)",
+        "libX11-xcb.so.1()(64bit)",
+        "libX11.so.6()(64bit)",
+        "libXcomposite.so.1()(64bit)",
+        "libXcursor.so.1()(64bit)",
+        "libXdamage.so.1()(64bit)",
+        "libXext.so.6()(64bit)",
+        "libXfixes.so.3()(64bit)",
+        "libXi.so.6()(64bit)",
+        "libXrandr.so.2()(64bit)",
+        "libXrender.so.1()(64bit)",
+        "libXss.so.1()(64bit)",
+        "libXtst.so.6()(64bit)",
+        "libasound.so.2()(64bit)",
+        "libasound.so.2(ALSA_0.9)(64bit)",
+        "libasound.so.2(ALSA_0.9.0)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc4)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc8)(64bit)",
+        "libasound.so.2(ALSA_0.9.3)(64bit)",
+        "libasound.so.2(ALSA_0.9.5)(64bit)",
+        "libasound.so.2(ALSA_0.9.7)(64bit)",
+        "libatk-1.0.so.0()(64bit)",
+        "libc.so.6()(64bit)",
+        "libc.so.6(GLIBC_2.10)(64bit)",
+        "libc.so.6(GLIBC_2.11)(64bit)",
+        "libc.so.6(GLIBC_2.12)(64bit)",
+        "libc.so.6(GLIBC_2.13)(64bit)",
+        "libc.so.6(GLIBC_2.14)(64bit)",
+        "libc.so.6(GLIBC_2.15)(64bit)",
+        "libc.so.6(GLIBC_2.16)(64bit)",
+        "libc.so.6(GLIBC_2.17)(64bit)",
+        "libc.so.6(GLIBC_2.18)(64bit)",
+        "libc.so.6(GLIBC_2.2.5)(64bit)",
+        "libc.so.6(GLIBC_2.2.6)(64bit)",
+        "libc.so.6(GLIBC_2.22)(64bit)",
+        "libc.so.6(GLIBC_2.23)(64bit)",
+        "libc.so.6(GLIBC_2.24)(64bit)",
+        "libc.so.6(GLIBC_2.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.2)(64bit)",
+        "libc.so.6(GLIBC_2.3.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.4)(64bit)",
+        "libc.so.6(GLIBC_2.4)(64bit)",
+        "libc.so.6(GLIBC_2.5)(64bit)",
+        "libc.so.6(GLIBC_2.6)(64bit)",
+        "libc.so.6(GLIBC_2.7)(64bit)",
+        "libc.so.6(GLIBC_2.8)(64bit)",
+        "libc.so.6(GLIBC_2.9)(64bit)",
+        "libcairo.so()(64bit)",
+        "libcairo.so.2()(64bit)",
+        "libcups.so.2()(64bit)",
+        "libdbus-1.so.3()(64bit)",
+        "libdbus-1.so.3(LIBDBUS_1_3)(64bit)",
+        "libdbus-1.so.3(LIBDBUS_PRIVATE_1.11.16)(64bit)",
+        "libdl.so.2()(64bit)",
+        "libdl.so.2(GLIBC_2.2.5)(64bit)",
+        "libdl.so.2(GLIBC_2.3.3)(64bit)",
+        "libdl.so.2(GLIBC_2.3.4)(64bit)",
+        "libexpat.so.0()(64bit)",
+        "libexpat.so.1()(64bit)",
+        "libfontconfig.so.1()(64bit)",
+        "libgcc_s.so.1()(64bit)",
+        "libgcc_s.so.1(GCC_3.0)(64bit)",
+        "libgcc_s.so.1(GCC_3.3)(64bit)",
+        "libgcc_s.so.1(GCC_3.3.1)(64bit)",
+        "libgcc_s.so.1(GCC_3.4)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.2)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.4)(64bit)",
+        "libgcc_s.so.1(GCC_4.0.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.2.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.3.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.7.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.8.0)(64bit)",
+        "libgconf-2.so.4()(64bit)",
+        "libgdk-3.so.0()(64bit)",
+        "libgdk_pixbuf-2.0.so.0()(64bit)",
+        "libgio-2.0.so.0()(64bit)",
+        "libglib-2.0.so.0()(64bit)",
+        "libgmodule-2.0.so.0()(64bit)",
+        "libgobject-2.0.so.0()(64bit)",
+        "libgtk-3.so.0()(64bit)",
+        "libm.so.6()(64bit)",
+        "libm.so.6(GLIBC_2.15)(64bit)",
+        "libm.so.6(GLIBC_2.18)(64bit)",
+        "libm.so.6(GLIBC_2.2.5)(64bit)",
+        "libm.so.6(GLIBC_2.23)(64bit)",
+        "libm.so.6(GLIBC_2.24)(64bit)",
+        "libm.so.6(GLIBC_2.4)(64bit)",
+        "libnspr4.so()(64bit)",
+        "libnss3.so()(64bit)",
+        "libnss3.so(NSS_3.10)(64bit)",
+        "libnss3.so(NSS_3.10.2)(64bit)",
+        "libnss3.so(NSS_3.11)(64bit)",
+        "libnss3.so(NSS_3.11.1)(64bit)",
+        "libnss3.so(NSS_3.11.2)(64bit)",
+        "libnss3.so(NSS_3.11.7)(64bit)",
+        "libnss3.so(NSS_3.11.9)(64bit)",
+        "libnss3.so(NSS_3.12)(64bit)",
+        "libnss3.so(NSS_3.12.1)(64bit)",
+        "libnss3.so(NSS_3.12.10)(64bit)",
+        "libnss3.so(NSS_3.12.3)(64bit)",
+        "libnss3.so(NSS_3.12.4)(64bit)",
+        "libnss3.so(NSS_3.12.5)(64bit)",
+        "libnss3.so(NSS_3.12.6)(64bit)",
+        "libnss3.so(NSS_3.12.7)(64bit)",
+        "libnss3.so(NSS_3.12.9)(64bit)",
+        "libnss3.so(NSS_3.13)(64bit)",
+        "libnss3.so(NSS_3.13.2)(64bit)",
+        "libnss3.so(NSS_3.14)(64bit)",
+        "libnss3.so(NSS_3.14.1)(64bit)",
+        "libnss3.so(NSS_3.14.3)(64bit)",
+        "libnss3.so(NSS_3.15)(64bit)",
+        "libnss3.so(NSS_3.15.4)(64bit)",
+        "libnss3.so(NSS_3.16.1)(64bit)",
+        "libnss3.so(NSS_3.16.2)(64bit)",
+        "libnss3.so(NSS_3.18)(64bit)",
+        "libnss3.so(NSS_3.19)(64bit)",
+        "libnss3.so(NSS_3.19.1)(64bit)",
+        "libnss3.so(NSS_3.2)(64bit)",
+        "libnss3.so(NSS_3.2.1)(64bit)",
+        "libnss3.so(NSS_3.21)(64bit)",
+        "libnss3.so(NSS_3.22)(64bit)",
+        "libnss3.so(NSS_3.3)(64bit)",
+        "libnss3.so(NSS_3.3.1)(64bit)",
+        "libnss3.so(NSS_3.30)(64bit)",
+        "libnss3.so(NSS_3.31)(64bit)",
+        "libnss3.so(NSS_3.4)(64bit)",
+        "libnss3.so(NSS_3.5)(64bit)",
+        "libnss3.so(NSS_3.6)(64bit)",
+        "libnss3.so(NSS_3.7)(64bit)",
+        "libnss3.so(NSS_3.7.1)(64bit)",
+        "libnss3.so(NSS_3.8)(64bit)",
+        "libnss3.so(NSS_3.9)(64bit)",
+        "libnss3.so(NSS_3.9.2)(64bit)",
+        "libnss3.so(NSS_3.9.3)(64bit)",
+        "libnssutil3.so()(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.3)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.5)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.7)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.13)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.14)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.15)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.17.1)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.21)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.24)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.25)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.31)(64bit)",
+        "libpango-1.0.so.0()(64bit)",
+        "libpangocairo-1.0.so.0()(64bit)",
+        "libpthread.so.0()(64bit)",
+        "libpthread.so.0(GLIBC_2.11)(64bit)",
+        "libpthread.so.0(GLIBC_2.12)(64bit)",
+        "libpthread.so.0(GLIBC_2.18)(64bit)",
+        "libpthread.so.0(GLIBC_2.2.5)(64bit)",
+        "libpthread.so.0(GLIBC_2.2.6)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.2)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.3)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.4)(64bit)",
+        "libpthread.so.0(GLIBC_2.4)(64bit)",
+        "librt.so.1()(64bit)",
+        "librt.so.1(GLIBC_2.2.5)(64bit)",
+        "librt.so.1(GLIBC_2.3.3)(64bit)",
+        "librt.so.1(GLIBC_2.3.4)(64bit)",
+        "librt.so.1(GLIBC_2.4)(64bit)",
+        "librt.so.1(GLIBC_2.7)(64bit)",
+        "libsmime3.so()(64bit)",
+        "libsmime3.so(NSS_3.10)(64bit)",
+        "libsmime3.so(NSS_3.12.10)(64bit)",
+        "libsmime3.so(NSS_3.12.2)(64bit)",
+        "libsmime3.so(NSS_3.13)(64bit)",
+        "libsmime3.so(NSS_3.15)(64bit)",
+        "libsmime3.so(NSS_3.16)(64bit)",
+        "libsmime3.so(NSS_3.18)(64bit)",
+        "libsmime3.so(NSS_3.2)(64bit)",
+        "libsmime3.so(NSS_3.2.1)(64bit)",
+        "libsmime3.so(NSS_3.3)(64bit)",
+        "libsmime3.so(NSS_3.4)(64bit)",
+        "libsmime3.so(NSS_3.4.1)(64bit)",
+        "libsmime3.so(NSS_3.6)(64bit)",
+        "libsmime3.so(NSS_3.7)(64bit)",
+        "libsmime3.so(NSS_3.7.2)(64bit)",
+        "libsmime3.so(NSS_3.8)(64bit)",
+        "libsmime3.so(NSS_3.9)(64bit)",
+        "libsmime3.so(NSS_3.9.3)(64bit)",
+        "libxcb.so.1()(64bit)"
+    ],
+    "Fedora 26": [
+        "ld-linux-x86-64.so.2()(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.4)(64bit)",
+        "libX11-xcb.so.1()(64bit)",
+        "libX11.so.6()(64bit)",
+        "libXcomposite.so.1()(64bit)",
+        "libXcursor.so.1()(64bit)",
+        "libXdamage.so.1()(64bit)",
+        "libXext.so.6()(64bit)",
+        "libXfixes.so.3()(64bit)",
+        "libXi.so.6()(64bit)",
+        "libXrandr.so.2()(64bit)",
+        "libXrender.so.1()(64bit)",
+        "libXss.so.1()(64bit)",
+        "libXtst.so.6()(64bit)",
+        "libasound.so.2()(64bit)",
+        "libasound.so.2(ALSA_0.9)(64bit)",
+        "libasound.so.2(ALSA_0.9.0)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc4)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc8)(64bit)",
+        "libasound.so.2(ALSA_0.9.3)(64bit)",
+        "libasound.so.2(ALSA_0.9.5)(64bit)",
+        "libasound.so.2(ALSA_0.9.7)(64bit)",
+        "libatk-1.0.so.0()(64bit)",
+        "libc.so.6()(64bit)",
+        "libc.so.6(GLIBC_2.10)(64bit)",
+        "libc.so.6(GLIBC_2.11)(64bit)",
+        "libc.so.6(GLIBC_2.12)(64bit)",
+        "libc.so.6(GLIBC_2.13)(64bit)",
+        "libc.so.6(GLIBC_2.14)(64bit)",
+        "libc.so.6(GLIBC_2.15)(64bit)",
+        "libc.so.6(GLIBC_2.16)(64bit)",
+        "libc.so.6(GLIBC_2.17)(64bit)",
+        "libc.so.6(GLIBC_2.18)(64bit)",
+        "libc.so.6(GLIBC_2.2.5)(64bit)",
+        "libc.so.6(GLIBC_2.2.6)(64bit)",
+        "libc.so.6(GLIBC_2.22)(64bit)",
+        "libc.so.6(GLIBC_2.23)(64bit)",
+        "libc.so.6(GLIBC_2.24)(64bit)",
+        "libc.so.6(GLIBC_2.25)(64bit)",
+        "libc.so.6(GLIBC_2.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.2)(64bit)",
+        "libc.so.6(GLIBC_2.3.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.4)(64bit)",
+        "libc.so.6(GLIBC_2.4)(64bit)",
+        "libc.so.6(GLIBC_2.5)(64bit)",
+        "libc.so.6(GLIBC_2.6)(64bit)",
+        "libc.so.6(GLIBC_2.7)(64bit)",
+        "libc.so.6(GLIBC_2.8)(64bit)",
+        "libc.so.6(GLIBC_2.9)(64bit)",
+        "libcairo.so()(64bit)",
+        "libcairo.so.2()(64bit)",
+        "libcups.so.2()(64bit)",
+        "libdbus-1.so.3()(64bit)",
+        "libdbus-1.so.3(LIBDBUS_1_3)(64bit)",
+        "libdbus-1.so.3(LIBDBUS_PRIVATE_1.11.16)(64bit)",
+        "libdl.so.2()(64bit)",
+        "libdl.so.2(GLIBC_2.2.5)(64bit)",
+        "libdl.so.2(GLIBC_2.3.3)(64bit)",
+        "libdl.so.2(GLIBC_2.3.4)(64bit)",
+        "libexpat.so.0()(64bit)",
+        "libexpat.so.1()(64bit)",
+        "libfontconfig.so.1()(64bit)",
+        "libgcc_s.so.1()(64bit)",
+        "libgcc_s.so.1(GCC_3.0)(64bit)",
+        "libgcc_s.so.1(GCC_3.3)(64bit)",
+        "libgcc_s.so.1(GCC_3.3.1)(64bit)",
+        "libgcc_s.so.1(GCC_3.4)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.2)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.4)(64bit)",
+        "libgcc_s.so.1(GCC_4.0.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.2.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.3.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.7.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.8.0)(64bit)",
+        "libgcc_s.so.1(GCC_7.0.0)(64bit)",
+        "libgconf-2.so.4()(64bit)",
+        "libgdk-3.so.0()(64bit)",
+        "libgdk_pixbuf-2.0.so.0()(64bit)",
+        "libgio-2.0.so.0()(64bit)",
+        "libglib-2.0.so.0()(64bit)",
+        "libgmodule-2.0.so.0()(64bit)",
+        "libgobject-2.0.so.0()(64bit)",
+        "libgtk-3.so.0()(64bit)",
+        "libm.so.6()(64bit)",
+        "libm.so.6(GLIBC_2.15)(64bit)",
+        "libm.so.6(GLIBC_2.18)(64bit)",
+        "libm.so.6(GLIBC_2.2.5)(64bit)",
+        "libm.so.6(GLIBC_2.23)(64bit)",
+        "libm.so.6(GLIBC_2.24)(64bit)",
+        "libm.so.6(GLIBC_2.25)(64bit)",
+        "libm.so.6(GLIBC_2.4)(64bit)",
+        "libnspr4.so()(64bit)",
+        "libnss3.so()(64bit)",
+        "libnss3.so(NSS_3.10)(64bit)",
+        "libnss3.so(NSS_3.10.2)(64bit)",
+        "libnss3.so(NSS_3.11)(64bit)",
+        "libnss3.so(NSS_3.11.1)(64bit)",
+        "libnss3.so(NSS_3.11.2)(64bit)",
+        "libnss3.so(NSS_3.11.7)(64bit)",
+        "libnss3.so(NSS_3.11.9)(64bit)",
+        "libnss3.so(NSS_3.12)(64bit)",
+        "libnss3.so(NSS_3.12.1)(64bit)",
+        "libnss3.so(NSS_3.12.10)(64bit)",
+        "libnss3.so(NSS_3.12.3)(64bit)",
+        "libnss3.so(NSS_3.12.4)(64bit)",
+        "libnss3.so(NSS_3.12.5)(64bit)",
+        "libnss3.so(NSS_3.12.6)(64bit)",
+        "libnss3.so(NSS_3.12.7)(64bit)",
+        "libnss3.so(NSS_3.12.9)(64bit)",
+        "libnss3.so(NSS_3.13)(64bit)",
+        "libnss3.so(NSS_3.13.2)(64bit)",
+        "libnss3.so(NSS_3.14)(64bit)",
+        "libnss3.so(NSS_3.14.1)(64bit)",
+        "libnss3.so(NSS_3.14.3)(64bit)",
+        "libnss3.so(NSS_3.15)(64bit)",
+        "libnss3.so(NSS_3.15.4)(64bit)",
+        "libnss3.so(NSS_3.16.1)(64bit)",
+        "libnss3.so(NSS_3.16.2)(64bit)",
+        "libnss3.so(NSS_3.18)(64bit)",
+        "libnss3.so(NSS_3.19)(64bit)",
+        "libnss3.so(NSS_3.19.1)(64bit)",
+        "libnss3.so(NSS_3.2)(64bit)",
+        "libnss3.so(NSS_3.2.1)(64bit)",
+        "libnss3.so(NSS_3.21)(64bit)",
+        "libnss3.so(NSS_3.22)(64bit)",
+        "libnss3.so(NSS_3.3)(64bit)",
+        "libnss3.so(NSS_3.3.1)(64bit)",
+        "libnss3.so(NSS_3.30)(64bit)",
+        "libnss3.so(NSS_3.31)(64bit)",
+        "libnss3.so(NSS_3.4)(64bit)",
+        "libnss3.so(NSS_3.5)(64bit)",
+        "libnss3.so(NSS_3.6)(64bit)",
+        "libnss3.so(NSS_3.7)(64bit)",
+        "libnss3.so(NSS_3.7.1)(64bit)",
+        "libnss3.so(NSS_3.8)(64bit)",
+        "libnss3.so(NSS_3.9)(64bit)",
+        "libnss3.so(NSS_3.9.2)(64bit)",
+        "libnss3.so(NSS_3.9.3)(64bit)",
+        "libnssutil3.so()(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.3)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.5)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.7)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.13)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.14)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.15)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.17.1)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.21)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.24)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.25)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.31)(64bit)",
+        "libpango-1.0.so.0()(64bit)",
+        "libpangocairo-1.0.so.0()(64bit)",
+        "libpthread.so.0()(64bit)",
+        "libpthread.so.0(GLIBC_2.11)(64bit)",
+        "libpthread.so.0(GLIBC_2.12)(64bit)",
+        "libpthread.so.0(GLIBC_2.18)(64bit)",
+        "libpthread.so.0(GLIBC_2.2.5)(64bit)",
+        "libpthread.so.0(GLIBC_2.2.6)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.2)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.3)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.4)(64bit)",
+        "libpthread.so.0(GLIBC_2.4)(64bit)",
+        "librt.so.1()(64bit)",
+        "librt.so.1(GLIBC_2.2.5)(64bit)",
+        "librt.so.1(GLIBC_2.3.3)(64bit)",
+        "librt.so.1(GLIBC_2.3.4)(64bit)",
+        "librt.so.1(GLIBC_2.4)(64bit)",
+        "librt.so.1(GLIBC_2.7)(64bit)",
+        "libsmime3.so()(64bit)",
+        "libsmime3.so(NSS_3.10)(64bit)",
+        "libsmime3.so(NSS_3.12.10)(64bit)",
+        "libsmime3.so(NSS_3.12.2)(64bit)",
+        "libsmime3.so(NSS_3.13)(64bit)",
+        "libsmime3.so(NSS_3.15)(64bit)",
+        "libsmime3.so(NSS_3.16)(64bit)",
+        "libsmime3.so(NSS_3.18)(64bit)",
+        "libsmime3.so(NSS_3.2)(64bit)",
+        "libsmime3.so(NSS_3.2.1)(64bit)",
+        "libsmime3.so(NSS_3.3)(64bit)",
+        "libsmime3.so(NSS_3.4)(64bit)",
+        "libsmime3.so(NSS_3.4.1)(64bit)",
+        "libsmime3.so(NSS_3.6)(64bit)",
+        "libsmime3.so(NSS_3.7)(64bit)",
+        "libsmime3.so(NSS_3.7.2)(64bit)",
+        "libsmime3.so(NSS_3.8)(64bit)",
+        "libsmime3.so(NSS_3.9)(64bit)",
+        "libsmime3.so(NSS_3.9.3)(64bit)",
+        "libxcb.so.1()(64bit)"
+    ],
+    "openSUSE Leap 42.2": [
+        "ld-linux-x86-64.so.2()(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.4)(64bit)",
+        "libX11-xcb.so.1",
+        "libX11-xcb.so.1()(64bit)",
+        "libX11.so.6",
+        "libX11.so.6()(64bit)",
+        "libXcomposite.so.1",
+        "libXcomposite.so.1()(64bit)",
+        "libXcursor.so.1",
+        "libXcursor.so.1()(64bit)",
+        "libXdamage.so.1",
+        "libXdamage.so.1()(64bit)",
+        "libXext.so.6",
+        "libXext.so.6()(64bit)",
+        "libXfixes.so.3",
+        "libXfixes.so.3()(64bit)",
+        "libXi.so.6",
+        "libXi.so.6()(64bit)",
+        "libXrandr.so.2",
+        "libXrandr.so.2()(64bit)",
+        "libXrender.so.1",
+        "libXrender.so.1()(64bit)",
+        "libXss.so.1",
+        "libXss.so.1()(64bit)",
+        "libXtst.so.6",
+        "libXtst.so.6()(64bit)",
+        "libasound.so.2",
+        "libasound.so.2()(64bit)",
+        "libasound.so.2(ALSA_0.9)",
+        "libasound.so.2(ALSA_0.9)(64bit)",
+        "libasound.so.2(ALSA_0.9.0)",
+        "libasound.so.2(ALSA_0.9.0)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc4)",
+        "libasound.so.2(ALSA_0.9.0rc4)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc8)",
+        "libasound.so.2(ALSA_0.9.0rc8)(64bit)",
+        "libasound.so.2(ALSA_0.9.3)",
+        "libasound.so.2(ALSA_0.9.3)(64bit)",
+        "libasound.so.2(ALSA_0.9.5)",
+        "libasound.so.2(ALSA_0.9.5)(64bit)",
+        "libasound.so.2(ALSA_0.9.7)",
+        "libasound.so.2(ALSA_0.9.7)(64bit)",
+        "libatk-1.0.so.0",
+        "libatk-1.0.so.0()(64bit)",
+        "libc.so.6",
+        "libc.so.6()(64bit)",
+        "libc.so.6(GCC_3.0)",
+        "libc.so.6(GLIBC_2.0)",
+        "libc.so.6(GLIBC_2.1)",
+        "libc.so.6(GLIBC_2.1.1)",
+        "libc.so.6(GLIBC_2.1.2)",
+        "libc.so.6(GLIBC_2.1.3)",
+        "libc.so.6(GLIBC_2.10)",
+        "libc.so.6(GLIBC_2.10)(64bit)",
+        "libc.so.6(GLIBC_2.11)",
+        "libc.so.6(GLIBC_2.11)(64bit)",
+        "libc.so.6(GLIBC_2.12)",
+        "libc.so.6(GLIBC_2.12)(64bit)",
+        "libc.so.6(GLIBC_2.13)",
+        "libc.so.6(GLIBC_2.13)(64bit)",
+        "libc.so.6(GLIBC_2.14)",
+        "libc.so.6(GLIBC_2.14)(64bit)",
+        "libc.so.6(GLIBC_2.15)",
+        "libc.so.6(GLIBC_2.15)(64bit)",
+        "libc.so.6(GLIBC_2.16)",
+        "libc.so.6(GLIBC_2.16)(64bit)",
+        "libc.so.6(GLIBC_2.17)",
+        "libc.so.6(GLIBC_2.17)(64bit)",
+        "libc.so.6(GLIBC_2.18)",
+        "libc.so.6(GLIBC_2.18)(64bit)",
+        "libc.so.6(GLIBC_2.2)",
+        "libc.so.6(GLIBC_2.2.1)",
+        "libc.so.6(GLIBC_2.2.2)",
+        "libc.so.6(GLIBC_2.2.3)",
+        "libc.so.6(GLIBC_2.2.4)",
+        "libc.so.6(GLIBC_2.2.5)(64bit)",
+        "libc.so.6(GLIBC_2.2.6)",
+        "libc.so.6(GLIBC_2.2.6)(64bit)",
+        "libc.so.6(GLIBC_2.22)",
+        "libc.so.6(GLIBC_2.22)(64bit)",
+        "libc.so.6(GLIBC_2.3)",
+        "libc.so.6(GLIBC_2.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.2)",
+        "libc.so.6(GLIBC_2.3.2)(64bit)",
+        "libc.so.6(GLIBC_2.3.3)",
+        "libc.so.6(GLIBC_2.3.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.4)",
+        "libc.so.6(GLIBC_2.3.4)(64bit)",
+        "libc.so.6(GLIBC_2.4)",
+        "libc.so.6(GLIBC_2.4)(64bit)",
+        "libc.so.6(GLIBC_2.5)",
+        "libc.so.6(GLIBC_2.5)(64bit)",
+        "libc.so.6(GLIBC_2.6)",
+        "libc.so.6(GLIBC_2.6)(64bit)",
+        "libc.so.6(GLIBC_2.7)",
+        "libc.so.6(GLIBC_2.7)(64bit)",
+        "libc.so.6(GLIBC_2.8)",
+        "libc.so.6(GLIBC_2.8)(64bit)",
+        "libc.so.6(GLIBC_2.9)",
+        "libc.so.6(GLIBC_2.9)(64bit)",
+        "libc.so.6(GLIBC_PRIVATE)",
+        "libcairo.so()(64bit)",
+        "libcairo.so.2",
+        "libcairo.so.2()(64bit)",
+        "libcups.so.2",
+        "libcups.so.2()(64bit)",
+        "libdbus-1.so.3",
+        "libdbus-1.so.3()(64bit)",
+        "libdl.so.2",
+        "libdl.so.2()(64bit)",
+        "libdl.so.2(GLIBC_2.0)",
+        "libdl.so.2(GLIBC_2.1)",
+        "libdl.so.2(GLIBC_2.2.5)(64bit)",
+        "libdl.so.2(GLIBC_2.3.3)",
+        "libdl.so.2(GLIBC_2.3.3)(64bit)",
+        "libdl.so.2(GLIBC_2.3.4)",
+        "libdl.so.2(GLIBC_2.3.4)(64bit)",
+        "libdl.so.2(GLIBC_PRIVATE)",
+        "libexpat.so.0",
+        "libexpat.so.0()(64bit)",
+        "libexpat.so.1",
+        "libexpat.so.1()(64bit)",
+        "libfontconfig.so.1",
+        "libfontconfig.so.1()(64bit)",
+        "libgcc_s.so.1",
+        "libgcc_s.so.1()(64bit)",
+        "libgcc_s.so.1(GCC_3.0)",
+        "libgcc_s.so.1(GCC_3.0)(64bit)",
+        "libgcc_s.so.1(GCC_3.3)",
+        "libgcc_s.so.1(GCC_3.3)(64bit)",
+        "libgcc_s.so.1(GCC_3.3.1)",
+        "libgcc_s.so.1(GCC_3.3.1)(64bit)",
+        "libgcc_s.so.1(GCC_3.4)",
+        "libgcc_s.so.1(GCC_3.4)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.2)",
+        "libgcc_s.so.1(GCC_3.4.2)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.4)(64bit)",
+        "libgcc_s.so.1(GCC_4.0.0)",
+        "libgcc_s.so.1(GCC_4.0.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.2.0)",
+        "libgcc_s.so.1(GCC_4.2.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.3.0)",
+        "libgcc_s.so.1(GCC_4.3.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.4.0)",
+        "libgcc_s.so.1(GCC_4.5.0)",
+        "libgcc_s.so.1(GCC_4.7.0)",
+        "libgcc_s.so.1(GCC_4.7.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.8.0)",
+        "libgcc_s.so.1(GCC_4.8.0)(64bit)",
+        "libgcc_s.so.1(GLIBC_2.0)",
+        "libgconf-2.so.4",
+        "libgconf-2.so.4()(64bit)",
+        "libgdk-3.so.0",
+        "libgdk-3.so.0()(64bit)",
+        "libgdk_pixbuf-2.0.so.0",
+        "libgdk_pixbuf-2.0.so.0()(64bit)",
+        "libgio-2.0.so.0",
+        "libgio-2.0.so.0()(64bit)",
+        "libglib-2.0.so.0",
+        "libglib-2.0.so.0()(64bit)",
+        "libgmodule-2.0.so.0",
+        "libgmodule-2.0.so.0()(64bit)",
+        "libgobject-2.0.so.0",
+        "libgobject-2.0.so.0()(64bit)",
+        "libgtk-3.so.0",
+        "libgtk-3.so.0()(64bit)",
+        "libm.so.6",
+        "libm.so.6()(64bit)",
+        "libm.so.6(GLIBC_2.0)",
+        "libm.so.6(GLIBC_2.1)",
+        "libm.so.6(GLIBC_2.15)",
+        "libm.so.6(GLIBC_2.15)(64bit)",
+        "libm.so.6(GLIBC_2.18)",
+        "libm.so.6(GLIBC_2.18)(64bit)",
+        "libm.so.6(GLIBC_2.2)",
+        "libm.so.6(GLIBC_2.2.5)(64bit)",
+        "libm.so.6(GLIBC_2.4)",
+        "libm.so.6(GLIBC_2.4)(64bit)",
+        "libnspr4.so",
+        "libnspr4.so()(64bit)",
+        "libnss3.so",
+        "libnss3.so()(64bit)",
+        "libnss3.so(NSS_3.10)",
+        "libnss3.so(NSS_3.10)(64bit)",
+        "libnss3.so(NSS_3.10.2)",
+        "libnss3.so(NSS_3.10.2)(64bit)",
+        "libnss3.so(NSS_3.11)",
+        "libnss3.so(NSS_3.11)(64bit)",
+        "libnss3.so(NSS_3.11.1)",
+        "libnss3.so(NSS_3.11.1)(64bit)",
+        "libnss3.so(NSS_3.11.2)",
+        "libnss3.so(NSS_3.11.2)(64bit)",
+        "libnss3.so(NSS_3.11.7)",
+        "libnss3.so(NSS_3.11.7)(64bit)",
+        "libnss3.so(NSS_3.11.9)",
+        "libnss3.so(NSS_3.11.9)(64bit)",
+        "libnss3.so(NSS_3.12)",
+        "libnss3.so(NSS_3.12)(64bit)",
+        "libnss3.so(NSS_3.12.1)",
+        "libnss3.so(NSS_3.12.1)(64bit)",
+        "libnss3.so(NSS_3.12.10)",
+        "libnss3.so(NSS_3.12.10)(64bit)",
+        "libnss3.so(NSS_3.12.3)",
+        "libnss3.so(NSS_3.12.3)(64bit)",
+        "libnss3.so(NSS_3.12.4)",
+        "libnss3.so(NSS_3.12.4)(64bit)",
+        "libnss3.so(NSS_3.12.5)",
+        "libnss3.so(NSS_3.12.5)(64bit)",
+        "libnss3.so(NSS_3.12.6)",
+        "libnss3.so(NSS_3.12.6)(64bit)",
+        "libnss3.so(NSS_3.12.7)",
+        "libnss3.so(NSS_3.12.7)(64bit)",
+        "libnss3.so(NSS_3.12.9)",
+        "libnss3.so(NSS_3.12.9)(64bit)",
+        "libnss3.so(NSS_3.13)",
+        "libnss3.so(NSS_3.13)(64bit)",
+        "libnss3.so(NSS_3.13.2)",
+        "libnss3.so(NSS_3.13.2)(64bit)",
+        "libnss3.so(NSS_3.14)",
+        "libnss3.so(NSS_3.14)(64bit)",
+        "libnss3.so(NSS_3.14.1)",
+        "libnss3.so(NSS_3.14.1)(64bit)",
+        "libnss3.so(NSS_3.14.3)",
+        "libnss3.so(NSS_3.14.3)(64bit)",
+        "libnss3.so(NSS_3.15)",
+        "libnss3.so(NSS_3.15)(64bit)",
+        "libnss3.so(NSS_3.15.4)",
+        "libnss3.so(NSS_3.15.4)(64bit)",
+        "libnss3.so(NSS_3.16.1)",
+        "libnss3.so(NSS_3.16.1)(64bit)",
+        "libnss3.so(NSS_3.16.2)",
+        "libnss3.so(NSS_3.16.2)(64bit)",
+        "libnss3.so(NSS_3.18)",
+        "libnss3.so(NSS_3.18)(64bit)",
+        "libnss3.so(NSS_3.19)",
+        "libnss3.so(NSS_3.19)(64bit)",
+        "libnss3.so(NSS_3.19.1)",
+        "libnss3.so(NSS_3.19.1)(64bit)",
+        "libnss3.so(NSS_3.2)",
+        "libnss3.so(NSS_3.2)(64bit)",
+        "libnss3.so(NSS_3.2.1)",
+        "libnss3.so(NSS_3.2.1)(64bit)",
+        "libnss3.so(NSS_3.21)",
+        "libnss3.so(NSS_3.21)(64bit)",
+        "libnss3.so(NSS_3.22)",
+        "libnss3.so(NSS_3.22)(64bit)",
+        "libnss3.so(NSS_3.3)",
+        "libnss3.so(NSS_3.3)(64bit)",
+        "libnss3.so(NSS_3.3.1)",
+        "libnss3.so(NSS_3.3.1)(64bit)",
+        "libnss3.so(NSS_3.4)",
+        "libnss3.so(NSS_3.4)(64bit)",
+        "libnss3.so(NSS_3.5)",
+        "libnss3.so(NSS_3.5)(64bit)",
+        "libnss3.so(NSS_3.6)",
+        "libnss3.so(NSS_3.6)(64bit)",
+        "libnss3.so(NSS_3.7)",
+        "libnss3.so(NSS_3.7)(64bit)",
+        "libnss3.so(NSS_3.7.1)",
+        "libnss3.so(NSS_3.7.1)(64bit)",
+        "libnss3.so(NSS_3.8)",
+        "libnss3.so(NSS_3.8)(64bit)",
+        "libnss3.so(NSS_3.9)",
+        "libnss3.so(NSS_3.9)(64bit)",
+        "libnss3.so(NSS_3.9.2)",
+        "libnss3.so(NSS_3.9.2)(64bit)",
+        "libnss3.so(NSS_3.9.3)",
+        "libnss3.so(NSS_3.9.3)(64bit)",
+        "libnssutil3.so",
+        "libnssutil3.so()(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12)",
+        "libnssutil3.so(NSSUTIL_3.12)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.3)",
+        "libnssutil3.so(NSSUTIL_3.12.3)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.5)",
+        "libnssutil3.so(NSSUTIL_3.12.5)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.7)",
+        "libnssutil3.so(NSSUTIL_3.12.7)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.13)",
+        "libnssutil3.so(NSSUTIL_3.13)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.14)",
+        "libnssutil3.so(NSSUTIL_3.14)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.15)",
+        "libnssutil3.so(NSSUTIL_3.15)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.17.1)",
+        "libnssutil3.so(NSSUTIL_3.17.1)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.21)",
+        "libnssutil3.so(NSSUTIL_3.21)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.24)",
+        "libnssutil3.so(NSSUTIL_3.24)(64bit)",
+        "libpango-1.0.so.0",
+        "libpango-1.0.so.0()(64bit)",
+        "libpangocairo-1.0.so.0",
+        "libpangocairo-1.0.so.0()(64bit)",
+        "libpthread.so.0",
+        "libpthread.so.0()(64bit)",
+        "libpthread.so.0(GLIBC_2.0)",
+        "libpthread.so.0(GLIBC_2.1)",
+        "libpthread.so.0(GLIBC_2.1.1)",
+        "libpthread.so.0(GLIBC_2.1.2)",
+        "libpthread.so.0(GLIBC_2.11)",
+        "libpthread.so.0(GLIBC_2.11)(64bit)",
+        "libpthread.so.0(GLIBC_2.12)",
+        "libpthread.so.0(GLIBC_2.12)(64bit)",
+        "libpthread.so.0(GLIBC_2.18)",
+        "libpthread.so.0(GLIBC_2.18)(64bit)",
+        "libpthread.so.0(GLIBC_2.2)",
+        "libpthread.so.0(GLIBC_2.2.3)",
+        "libpthread.so.0(GLIBC_2.2.5)(64bit)",
+        "libpthread.so.0(GLIBC_2.2.6)",
+        "libpthread.so.0(GLIBC_2.2.6)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.2)",
+        "libpthread.so.0(GLIBC_2.3.2)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.3)",
+        "libpthread.so.0(GLIBC_2.3.3)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.4)",
+        "libpthread.so.0(GLIBC_2.3.4)(64bit)",
+        "libpthread.so.0(GLIBC_2.4)",
+        "libpthread.so.0(GLIBC_2.4)(64bit)",
+        "libpthread.so.0(GLIBC_PRIVATE)",
+        "librt.so.1",
+        "librt.so.1()(64bit)",
+        "librt.so.1(GLIBC_2.1)",
+        "librt.so.1(GLIBC_2.2)",
+        "librt.so.1(GLIBC_2.2.5)(64bit)",
+        "librt.so.1(GLIBC_2.3.3)(64bit)",
+        "librt.so.1(GLIBC_2.3.4)",
+        "librt.so.1(GLIBC_2.3.4)(64bit)",
+        "librt.so.1(GLIBC_2.4)",
+        "librt.so.1(GLIBC_2.4)(64bit)",
+        "librt.so.1(GLIBC_2.7)",
+        "librt.so.1(GLIBC_2.7)(64bit)",
+        "libsmime3.so",
+        "libsmime3.so()(64bit)",
+        "libsmime3.so(NSS_3.10)",
+        "libsmime3.so(NSS_3.10)(64bit)",
+        "libsmime3.so(NSS_3.12.10)",
+        "libsmime3.so(NSS_3.12.10)(64bit)",
+        "libsmime3.so(NSS_3.12.2)",
+        "libsmime3.so(NSS_3.12.2)(64bit)",
+        "libsmime3.so(NSS_3.13)",
+        "libsmime3.so(NSS_3.13)(64bit)",
+        "libsmime3.so(NSS_3.15)",
+        "libsmime3.so(NSS_3.15)(64bit)",
+        "libsmime3.so(NSS_3.16)",
+        "libsmime3.so(NSS_3.16)(64bit)",
+        "libsmime3.so(NSS_3.18)",
+        "libsmime3.so(NSS_3.18)(64bit)",
+        "libsmime3.so(NSS_3.2)",
+        "libsmime3.so(NSS_3.2)(64bit)",
+        "libsmime3.so(NSS_3.2.1)",
+        "libsmime3.so(NSS_3.2.1)(64bit)",
+        "libsmime3.so(NSS_3.3)",
+        "libsmime3.so(NSS_3.3)(64bit)",
+        "libsmime3.so(NSS_3.4)",
+        "libsmime3.so(NSS_3.4)(64bit)",
+        "libsmime3.so(NSS_3.4.1)",
+        "libsmime3.so(NSS_3.4.1)(64bit)",
+        "libsmime3.so(NSS_3.6)",
+        "libsmime3.so(NSS_3.6)(64bit)",
+        "libsmime3.so(NSS_3.7)",
+        "libsmime3.so(NSS_3.7)(64bit)",
+        "libsmime3.so(NSS_3.7.2)",
+        "libsmime3.so(NSS_3.7.2)(64bit)",
+        "libsmime3.so(NSS_3.8)",
+        "libsmime3.so(NSS_3.8)(64bit)",
+        "libsmime3.so(NSS_3.9)",
+        "libsmime3.so(NSS_3.9)(64bit)",
+        "libsmime3.so(NSS_3.9.3)",
+        "libsmime3.so(NSS_3.9.3)(64bit)",
+        "libxcb.so.1",
+        "libxcb.so.1()(64bit)"
+    ],
+    "openSUSE Leap 42.3": [
+        "ld-linux-x86-64.so.2()(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)",
+        "ld-linux-x86-64.so.2(GLIBC_2.4)(64bit)",
+        "libX11-xcb.so.1",
+        "libX11-xcb.so.1()(64bit)",
+        "libX11.so.6",
+        "libX11.so.6()(64bit)",
+        "libXcomposite.so.1",
+        "libXcomposite.so.1()(64bit)",
+        "libXcursor.so.1",
+        "libXcursor.so.1()(64bit)",
+        "libXdamage.so.1",
+        "libXdamage.so.1()(64bit)",
+        "libXext.so.6",
+        "libXext.so.6()(64bit)",
+        "libXfixes.so.3",
+        "libXfixes.so.3()(64bit)",
+        "libXi.so.6",
+        "libXi.so.6()(64bit)",
+        "libXrandr.so.2",
+        "libXrandr.so.2()(64bit)",
+        "libXrender.so.1",
+        "libXrender.so.1()(64bit)",
+        "libXss.so.1",
+        "libXss.so.1()(64bit)",
+        "libXtst.so.6",
+        "libXtst.so.6()(64bit)",
+        "libasound.so.2",
+        "libasound.so.2()(64bit)",
+        "libasound.so.2(ALSA_0.9)",
+        "libasound.so.2(ALSA_0.9)(64bit)",
+        "libasound.so.2(ALSA_0.9.0)",
+        "libasound.so.2(ALSA_0.9.0)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc4)",
+        "libasound.so.2(ALSA_0.9.0rc4)(64bit)",
+        "libasound.so.2(ALSA_0.9.0rc8)",
+        "libasound.so.2(ALSA_0.9.0rc8)(64bit)",
+        "libasound.so.2(ALSA_0.9.3)",
+        "libasound.so.2(ALSA_0.9.3)(64bit)",
+        "libasound.so.2(ALSA_0.9.5)",
+        "libasound.so.2(ALSA_0.9.5)(64bit)",
+        "libasound.so.2(ALSA_0.9.7)",
+        "libasound.so.2(ALSA_0.9.7)(64bit)",
+        "libatk-1.0.so.0",
+        "libatk-1.0.so.0()(64bit)",
+        "libc.so.6",
+        "libc.so.6()(64bit)",
+        "libc.so.6(GCC_3.0)",
+        "libc.so.6(GLIBC_2.0)",
+        "libc.so.6(GLIBC_2.1)",
+        "libc.so.6(GLIBC_2.1.1)",
+        "libc.so.6(GLIBC_2.1.2)",
+        "libc.so.6(GLIBC_2.1.3)",
+        "libc.so.6(GLIBC_2.10)",
+        "libc.so.6(GLIBC_2.10)(64bit)",
+        "libc.so.6(GLIBC_2.11)",
+        "libc.so.6(GLIBC_2.11)(64bit)",
+        "libc.so.6(GLIBC_2.12)",
+        "libc.so.6(GLIBC_2.12)(64bit)",
+        "libc.so.6(GLIBC_2.13)",
+        "libc.so.6(GLIBC_2.13)(64bit)",
+        "libc.so.6(GLIBC_2.14)",
+        "libc.so.6(GLIBC_2.14)(64bit)",
+        "libc.so.6(GLIBC_2.15)",
+        "libc.so.6(GLIBC_2.15)(64bit)",
+        "libc.so.6(GLIBC_2.16)",
+        "libc.so.6(GLIBC_2.16)(64bit)",
+        "libc.so.6(GLIBC_2.17)",
+        "libc.so.6(GLIBC_2.17)(64bit)",
+        "libc.so.6(GLIBC_2.18)",
+        "libc.so.6(GLIBC_2.18)(64bit)",
+        "libc.so.6(GLIBC_2.2)",
+        "libc.so.6(GLIBC_2.2.1)",
+        "libc.so.6(GLIBC_2.2.2)",
+        "libc.so.6(GLIBC_2.2.3)",
+        "libc.so.6(GLIBC_2.2.4)",
+        "libc.so.6(GLIBC_2.2.5)(64bit)",
+        "libc.so.6(GLIBC_2.2.6)",
+        "libc.so.6(GLIBC_2.2.6)(64bit)",
+        "libc.so.6(GLIBC_2.22)",
+        "libc.so.6(GLIBC_2.22)(64bit)",
+        "libc.so.6(GLIBC_2.3)",
+        "libc.so.6(GLIBC_2.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.2)",
+        "libc.so.6(GLIBC_2.3.2)(64bit)",
+        "libc.so.6(GLIBC_2.3.3)",
+        "libc.so.6(GLIBC_2.3.3)(64bit)",
+        "libc.so.6(GLIBC_2.3.4)",
+        "libc.so.6(GLIBC_2.3.4)(64bit)",
+        "libc.so.6(GLIBC_2.4)",
+        "libc.so.6(GLIBC_2.4)(64bit)",
+        "libc.so.6(GLIBC_2.5)",
+        "libc.so.6(GLIBC_2.5)(64bit)",
+        "libc.so.6(GLIBC_2.6)",
+        "libc.so.6(GLIBC_2.6)(64bit)",
+        "libc.so.6(GLIBC_2.7)",
+        "libc.so.6(GLIBC_2.7)(64bit)",
+        "libc.so.6(GLIBC_2.8)",
+        "libc.so.6(GLIBC_2.8)(64bit)",
+        "libc.so.6(GLIBC_2.9)",
+        "libc.so.6(GLIBC_2.9)(64bit)",
+        "libc.so.6(GLIBC_PRIVATE)",
+        "libcairo.so()(64bit)",
+        "libcairo.so.2",
+        "libcairo.so.2()(64bit)",
+        "libcups.so.2",
+        "libcups.so.2()(64bit)",
+        "libdbus-1.so.3",
+        "libdbus-1.so.3()(64bit)",
+        "libdl.so.2",
+        "libdl.so.2()(64bit)",
+        "libdl.so.2(GLIBC_2.0)",
+        "libdl.so.2(GLIBC_2.1)",
+        "libdl.so.2(GLIBC_2.2.5)(64bit)",
+        "libdl.so.2(GLIBC_2.3.3)",
+        "libdl.so.2(GLIBC_2.3.3)(64bit)",
+        "libdl.so.2(GLIBC_2.3.4)",
+        "libdl.so.2(GLIBC_2.3.4)(64bit)",
+        "libdl.so.2(GLIBC_PRIVATE)",
+        "libexpat.so.0",
+        "libexpat.so.0()(64bit)",
+        "libexpat.so.1",
+        "libexpat.so.1()(64bit)",
+        "libfontconfig.so.1",
+        "libfontconfig.so.1()(64bit)",
+        "libgcc_s.so.1",
+        "libgcc_s.so.1()(64bit)",
+        "libgcc_s.so.1(GCC_3.0)",
+        "libgcc_s.so.1(GCC_3.0)(64bit)",
+        "libgcc_s.so.1(GCC_3.3)",
+        "libgcc_s.so.1(GCC_3.3)(64bit)",
+        "libgcc_s.so.1(GCC_3.3.1)",
+        "libgcc_s.so.1(GCC_3.3.1)(64bit)",
+        "libgcc_s.so.1(GCC_3.4)",
+        "libgcc_s.so.1(GCC_3.4)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.2)",
+        "libgcc_s.so.1(GCC_3.4.2)(64bit)",
+        "libgcc_s.so.1(GCC_3.4.4)(64bit)",
+        "libgcc_s.so.1(GCC_4.0.0)",
+        "libgcc_s.so.1(GCC_4.0.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.2.0)",
+        "libgcc_s.so.1(GCC_4.2.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.3.0)",
+        "libgcc_s.so.1(GCC_4.3.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.4.0)",
+        "libgcc_s.so.1(GCC_4.5.0)",
+        "libgcc_s.so.1(GCC_4.7.0)",
+        "libgcc_s.so.1(GCC_4.7.0)(64bit)",
+        "libgcc_s.so.1(GCC_4.8.0)",
+        "libgcc_s.so.1(GCC_4.8.0)(64bit)",
+        "libgcc_s.so.1(GCC_7.0.0)",
+        "libgcc_s.so.1(GCC_7.0.0)(64bit)",
+        "libgcc_s.so.1(GLIBC_2.0)",
+        "libgconf-2.so.4",
+        "libgconf-2.so.4()(64bit)",
+        "libgdk-3.so.0",
+        "libgdk-3.so.0()(64bit)",
+        "libgdk_pixbuf-2.0.so.0",
+        "libgdk_pixbuf-2.0.so.0()(64bit)",
+        "libgio-2.0.so.0",
+        "libgio-2.0.so.0()(64bit)",
+        "libglib-2.0.so.0",
+        "libglib-2.0.so.0()(64bit)",
+        "libgmodule-2.0.so.0",
+        "libgmodule-2.0.so.0()(64bit)",
+        "libgobject-2.0.so.0",
+        "libgobject-2.0.so.0()(64bit)",
+        "libgtk-3.so.0",
+        "libgtk-3.so.0()(64bit)",
+        "libm.so.6",
+        "libm.so.6()(64bit)",
+        "libm.so.6(GLIBC_2.0)",
+        "libm.so.6(GLIBC_2.1)",
+        "libm.so.6(GLIBC_2.15)",
+        "libm.so.6(GLIBC_2.15)(64bit)",
+        "libm.so.6(GLIBC_2.18)",
+        "libm.so.6(GLIBC_2.18)(64bit)",
+        "libm.so.6(GLIBC_2.2)",
+        "libm.so.6(GLIBC_2.2.5)(64bit)",
+        "libm.so.6(GLIBC_2.4)",
+        "libm.so.6(GLIBC_2.4)(64bit)",
+        "libnspr4.so",
+        "libnspr4.so()(64bit)",
+        "libnss3.so",
+        "libnss3.so()(64bit)",
+        "libnss3.so(NSS_3.10)",
+        "libnss3.so(NSS_3.10)(64bit)",
+        "libnss3.so(NSS_3.10.2)",
+        "libnss3.so(NSS_3.10.2)(64bit)",
+        "libnss3.so(NSS_3.11)",
+        "libnss3.so(NSS_3.11)(64bit)",
+        "libnss3.so(NSS_3.11.1)",
+        "libnss3.so(NSS_3.11.1)(64bit)",
+        "libnss3.so(NSS_3.11.2)",
+        "libnss3.so(NSS_3.11.2)(64bit)",
+        "libnss3.so(NSS_3.11.7)",
+        "libnss3.so(NSS_3.11.7)(64bit)",
+        "libnss3.so(NSS_3.11.9)",
+        "libnss3.so(NSS_3.11.9)(64bit)",
+        "libnss3.so(NSS_3.12)",
+        "libnss3.so(NSS_3.12)(64bit)",
+        "libnss3.so(NSS_3.12.1)",
+        "libnss3.so(NSS_3.12.1)(64bit)",
+        "libnss3.so(NSS_3.12.10)",
+        "libnss3.so(NSS_3.12.10)(64bit)",
+        "libnss3.so(NSS_3.12.3)",
+        "libnss3.so(NSS_3.12.3)(64bit)",
+        "libnss3.so(NSS_3.12.4)",
+        "libnss3.so(NSS_3.12.4)(64bit)",
+        "libnss3.so(NSS_3.12.5)",
+        "libnss3.so(NSS_3.12.5)(64bit)",
+        "libnss3.so(NSS_3.12.6)",
+        "libnss3.so(NSS_3.12.6)(64bit)",
+        "libnss3.so(NSS_3.12.7)",
+        "libnss3.so(NSS_3.12.7)(64bit)",
+        "libnss3.so(NSS_3.12.9)",
+        "libnss3.so(NSS_3.12.9)(64bit)",
+        "libnss3.so(NSS_3.13)",
+        "libnss3.so(NSS_3.13)(64bit)",
+        "libnss3.so(NSS_3.13.2)",
+        "libnss3.so(NSS_3.13.2)(64bit)",
+        "libnss3.so(NSS_3.14)",
+        "libnss3.so(NSS_3.14)(64bit)",
+        "libnss3.so(NSS_3.14.1)",
+        "libnss3.so(NSS_3.14.1)(64bit)",
+        "libnss3.so(NSS_3.14.3)",
+        "libnss3.so(NSS_3.14.3)(64bit)",
+        "libnss3.so(NSS_3.15)",
+        "libnss3.so(NSS_3.15)(64bit)",
+        "libnss3.so(NSS_3.15.4)",
+        "libnss3.so(NSS_3.15.4)(64bit)",
+        "libnss3.so(NSS_3.16.1)",
+        "libnss3.so(NSS_3.16.1)(64bit)",
+        "libnss3.so(NSS_3.16.2)",
+        "libnss3.so(NSS_3.16.2)(64bit)",
+        "libnss3.so(NSS_3.18)",
+        "libnss3.so(NSS_3.18)(64bit)",
+        "libnss3.so(NSS_3.19)",
+        "libnss3.so(NSS_3.19)(64bit)",
+        "libnss3.so(NSS_3.19.1)",
+        "libnss3.so(NSS_3.19.1)(64bit)",
+        "libnss3.so(NSS_3.2)",
+        "libnss3.so(NSS_3.2)(64bit)",
+        "libnss3.so(NSS_3.2.1)",
+        "libnss3.so(NSS_3.2.1)(64bit)",
+        "libnss3.so(NSS_3.21)",
+        "libnss3.so(NSS_3.21)(64bit)",
+        "libnss3.so(NSS_3.22)",
+        "libnss3.so(NSS_3.22)(64bit)",
+        "libnss3.so(NSS_3.3)",
+        "libnss3.so(NSS_3.3)(64bit)",
+        "libnss3.so(NSS_3.3.1)",
+        "libnss3.so(NSS_3.3.1)(64bit)",
+        "libnss3.so(NSS_3.4)",
+        "libnss3.so(NSS_3.4)(64bit)",
+        "libnss3.so(NSS_3.5)",
+        "libnss3.so(NSS_3.5)(64bit)",
+        "libnss3.so(NSS_3.6)",
+        "libnss3.so(NSS_3.6)(64bit)",
+        "libnss3.so(NSS_3.7)",
+        "libnss3.so(NSS_3.7)(64bit)",
+        "libnss3.so(NSS_3.7.1)",
+        "libnss3.so(NSS_3.7.1)(64bit)",
+        "libnss3.so(NSS_3.8)",
+        "libnss3.so(NSS_3.8)(64bit)",
+        "libnss3.so(NSS_3.9)",
+        "libnss3.so(NSS_3.9)(64bit)",
+        "libnss3.so(NSS_3.9.2)",
+        "libnss3.so(NSS_3.9.2)(64bit)",
+        "libnss3.so(NSS_3.9.3)",
+        "libnss3.so(NSS_3.9.3)(64bit)",
+        "libnssutil3.so",
+        "libnssutil3.so()(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12)",
+        "libnssutil3.so(NSSUTIL_3.12)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.3)",
+        "libnssutil3.so(NSSUTIL_3.12.3)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.5)",
+        "libnssutil3.so(NSSUTIL_3.12.5)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.12.7)",
+        "libnssutil3.so(NSSUTIL_3.12.7)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.13)",
+        "libnssutil3.so(NSSUTIL_3.13)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.14)",
+        "libnssutil3.so(NSSUTIL_3.14)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.15)",
+        "libnssutil3.so(NSSUTIL_3.15)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.17.1)",
+        "libnssutil3.so(NSSUTIL_3.17.1)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.21)",
+        "libnssutil3.so(NSSUTIL_3.21)(64bit)",
+        "libnssutil3.so(NSSUTIL_3.24)",
+        "libnssutil3.so(NSSUTIL_3.24)(64bit)",
+        "libpango-1.0.so.0",
+        "libpango-1.0.so.0()(64bit)",
+        "libpangocairo-1.0.so.0",
+        "libpangocairo-1.0.so.0()(64bit)",
+        "libpthread.so.0",
+        "libpthread.so.0()(64bit)",
+        "libpthread.so.0(GLIBC_2.0)",
+        "libpthread.so.0(GLIBC_2.1)",
+        "libpthread.so.0(GLIBC_2.1.1)",
+        "libpthread.so.0(GLIBC_2.1.2)",
+        "libpthread.so.0(GLIBC_2.11)",
+        "libpthread.so.0(GLIBC_2.11)(64bit)",
+        "libpthread.so.0(GLIBC_2.12)",
+        "libpthread.so.0(GLIBC_2.12)(64bit)",
+        "libpthread.so.0(GLIBC_2.18)",
+        "libpthread.so.0(GLIBC_2.18)(64bit)",
+        "libpthread.so.0(GLIBC_2.2)",
+        "libpthread.so.0(GLIBC_2.2.3)",
+        "libpthread.so.0(GLIBC_2.2.5)(64bit)",
+        "libpthread.so.0(GLIBC_2.2.6)",
+        "libpthread.so.0(GLIBC_2.2.6)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.2)",
+        "libpthread.so.0(GLIBC_2.3.2)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.3)",
+        "libpthread.so.0(GLIBC_2.3.3)(64bit)",
+        "libpthread.so.0(GLIBC_2.3.4)",
+        "libpthread.so.0(GLIBC_2.3.4)(64bit)",
+        "libpthread.so.0(GLIBC_2.4)",
+        "libpthread.so.0(GLIBC_2.4)(64bit)",
+        "libpthread.so.0(GLIBC_PRIVATE)",
+        "librt.so.1",
+        "librt.so.1()(64bit)",
+        "librt.so.1(GLIBC_2.1)",
+        "librt.so.1(GLIBC_2.2)",
+        "librt.so.1(GLIBC_2.2.5)(64bit)",
+        "librt.so.1(GLIBC_2.3.3)(64bit)",
+        "librt.so.1(GLIBC_2.3.4)",
+        "librt.so.1(GLIBC_2.3.4)(64bit)",
+        "librt.so.1(GLIBC_2.4)",
+        "librt.so.1(GLIBC_2.4)(64bit)",
+        "librt.so.1(GLIBC_2.7)",
+        "librt.so.1(GLIBC_2.7)(64bit)",
+        "libsmime3.so",
+        "libsmime3.so()(64bit)",
+        "libsmime3.so(NSS_3.10)",
+        "libsmime3.so(NSS_3.10)(64bit)",
+        "libsmime3.so(NSS_3.12.10)",
+        "libsmime3.so(NSS_3.12.10)(64bit)",
+        "libsmime3.so(NSS_3.12.2)",
+        "libsmime3.so(NSS_3.12.2)(64bit)",
+        "libsmime3.so(NSS_3.13)",
+        "libsmime3.so(NSS_3.13)(64bit)",
+        "libsmime3.so(NSS_3.15)",
+        "libsmime3.so(NSS_3.15)(64bit)",
+        "libsmime3.so(NSS_3.16)",
+        "libsmime3.so(NSS_3.16)(64bit)",
+        "libsmime3.so(NSS_3.18)",
+        "libsmime3.so(NSS_3.18)(64bit)",
+        "libsmime3.so(NSS_3.2)",
+        "libsmime3.so(NSS_3.2)(64bit)",
+        "libsmime3.so(NSS_3.2.1)",
+        "libsmime3.so(NSS_3.2.1)(64bit)",
+        "libsmime3.so(NSS_3.3)",
+        "libsmime3.so(NSS_3.3)(64bit)",
+        "libsmime3.so(NSS_3.4)",
+        "libsmime3.so(NSS_3.4)(64bit)",
+        "libsmime3.so(NSS_3.4.1)",
+        "libsmime3.so(NSS_3.4.1)(64bit)",
+        "libsmime3.so(NSS_3.6)",
+        "libsmime3.so(NSS_3.6)(64bit)",
+        "libsmime3.so(NSS_3.7)",
+        "libsmime3.so(NSS_3.7)(64bit)",
+        "libsmime3.so(NSS_3.7.2)",
+        "libsmime3.so(NSS_3.7.2)(64bit)",
+        "libsmime3.so(NSS_3.8)",
+        "libsmime3.so(NSS_3.8)(64bit)",
+        "libsmime3.so(NSS_3.9)",
+        "libsmime3.so(NSS_3.9)(64bit)",
+        "libsmime3.so(NSS_3.9.3)",
+        "libsmime3.so(NSS_3.9.3)(64bit)",
+        "libxcb.so.1",
+        "libxcb.so.1()(64bit)"
+    ]
+}
diff --git a/chrome/installer/linux/rpm/update-package-provides.py b/chrome/installer/linux/rpm/update-package-provides.py
new file mode 100755
index 0000000..4099f4e1d
--- /dev/null
+++ b/chrome/installer/linux/rpm/update-package-provides.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import binascii
+import cStringIO
+import gzip
+import hashlib
+import json
+import os
+import urllib2
+import xml.etree.ElementTree
+
+PACKAGE_FILTER = [
+    "ld-linux-x86-64.so",
+    "libX11-xcb.so",
+    "libX11.so",
+    "libXcomposite.so",
+    "libXcursor.so",
+    "libXdamage.so",
+    "libXext.so",
+    "libXfixes.so",
+    "libXi.so",
+    "libXrandr.so",
+    "libXrender.so",
+    "libXss.so",
+    "libXtst.so",
+    "libasound.so",
+    "libatk-1.0.so",
+    "libc.so",
+    "libcairo.so",
+    "libcups.so",
+    "libdbus-1.so",
+    "libdl.so",
+    "libexpat.so",
+    "libfontconfig.so",
+    "libgcc_s.so",
+    "libgconf-2.so",
+    "libgdk-3.so",
+    "libgdk_pixbuf-2.0.so",
+    "libgio-2.0.so",
+    "libglib-2.0.so",
+    "libgmodule-2.0.so",
+    "libgobject-2.0.so",
+    "libgtk-3.so",
+    "libm.so",
+    "libnspr4.so",
+    "libnss3.so",
+    "libnssutil3.so",
+    "libpango-1.0.so",
+    "libpangocairo-1.0.so",
+    "libpthread.so",
+    "librt.so",
+    "libsmime3.so",
+    "libxcb.so",
+]
+
+SUPPORTED_FEDORA_RELEASES = ['25', '26']
+SUPPORTED_OPENSUSE_LEAP_RELEASES = ['42.2', '42.3']
+
+COMMON_NS = "http://linux.duke.edu/metadata/common"
+RPM_NS = "http://linux.duke.edu/metadata/rpm"
+REPO_NS = "http://linux.duke.edu/metadata/repo"
+
+rpm_sources = {}
+for version in SUPPORTED_FEDORA_RELEASES:
+  rpm_sources['Fedora ' + version] = [
+      "https://download.fedoraproject.org/pub/fedora/linux/releases/%s/Everything/x86_64/os/" % version,
+      # 'updates' must appear after 'releases' since its entries
+      # overwrite the originals.
+      "https://download.fedoraproject.org/pub/fedora/linux/updates/%s/x86_64/" % version,
+  ]
+for version in SUPPORTED_OPENSUSE_LEAP_RELEASES:
+    rpm_sources['openSUSE Leap ' + version] = [
+        "https://download.opensuse.org/distribution/leap/%s/repo/oss/suse/" % version,
+        # 'update' must appear after 'distribution' since its entries
+        # overwrite the originals.
+        "https://download.opensuse.org/update/leap/%s/oss/" % version,
+  ]
+
+provides = {}
+for distro in rpm_sources:
+  distro_provides = {}
+  for source in rpm_sources[distro]:
+    # |source| may redirect to a real download mirror.  However, these
+    # mirrors may be out-of-sync with each other.  Follow the redirect
+    # to ensure the file-references from the metadata file are valid.
+    source = urllib2.urlopen(source).geturl()
+
+    response = urllib2.urlopen(source + "repodata/repomd.xml")
+    repomd = xml.etree.ElementTree.fromstring(response.read())
+    primary = source + repomd.find("./{%s}data[@type='primary']/{%s}location" %
+                                   (REPO_NS, REPO_NS)).attrib['href']
+    expected_checksum = repomd.find(
+        "./{%s}data[@type='primary']/{%s}checksum[@type='sha256']" %
+        (REPO_NS, REPO_NS)).text
+
+    response = urllib2.urlopen(primary)
+    gz_data = response.read()
+
+    sha = hashlib.sha256()
+    sha.update(gz_data)
+    actual_checksum = binascii.hexlify(sha.digest())
+    assert expected_checksum == actual_checksum
+
+    zipped_file = cStringIO.StringIO()
+    zipped_file.write(gz_data)
+    zipped_file.seek(0)
+    contents = gzip.GzipFile(fileobj=zipped_file, mode='rb').read()
+    metadata = xml.etree.ElementTree.fromstring(contents)
+    for package in metadata.findall('./{%s}package' % COMMON_NS):
+      if package.find('./{%s}arch' % COMMON_NS).text != 'x86_64':
+        continue
+      package_name = package.find('./{%s}name' % COMMON_NS).text
+      package_provides = []
+      for entry in package.findall('./{%s}format/{%s}provides/{%s}entry' %
+                                   (COMMON_NS, RPM_NS, RPM_NS)):
+        name = entry.attrib['name']
+        for prefix in PACKAGE_FILTER:
+          if name.startswith(prefix):
+            package_provides.append(name)
+      distro_provides[package_name] = package_provides
+  provides[distro] = sorted(list(set(
+      [package_provides for package in distro_provides
+       for package_provides in distro_provides[package]])))
+
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+with open(os.path.join(script_dir, 'dist-package-provides.json'), 'w') as f:
+  f.write(json.dumps(provides, sort_keys=True, indent=4,
+                     separators=(',', ': ')))
+  f.write('\n')
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 578caab..32c09c1 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -40,6 +40,8 @@
     "cast_quota_permission_context.h",
     "cast_resource_dispatcher_host_delegate.cc",
     "cast_resource_dispatcher_host_delegate.h",
+    "cast_web_contents_manager.cc",
+    "cast_web_contents_manager.h",
     "cast_web_view.cc",
     "cast_web_view.h",
     "devtools/cast_devtools_manager_delegate.cc",
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 506cb34..3f3c14a3 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -223,7 +223,6 @@
     // TODO(714676): this should probably set the no restrictions autoplay
     // policy instead.
     {switches::kIgnoreAutoplayRestrictionsForTests, ""},
-    {switches::kDisableMediaSuspend, ""},
 #else
     // GPU shader disk cache disabling is largely to conserve disk space.
     {switches::kDisableGpuShaderDiskCache, ""},
@@ -265,6 +264,7 @@
     {switches::kEnableUseZoomForDSF, "false"},
     // TODO(halliwell): Revert after fix for b/63101386.
     {switches::kDisallowNonExactResourceReuse, ""},
+    {switches::kEnableMediaSuspend, ""},
 };
 
 void AddDefaultCommandLineSwitches(base::CommandLine* command_line) {
@@ -419,7 +419,8 @@
   breakpad::CrashDumpObserver::Create();
   breakpad::CrashDumpObserver::GetInstance()->RegisterClient(
       base::MakeUnique<breakpad::ChildProcessCrashObserver>(
-          crash_dumps_dir, kAndroidMinidumpDescriptor));
+          crash_dumps_dir, kAndroidMinidumpDescriptor,
+          base::Bind(&base::DoNothing)));
 #else
   base::FilePath home_dir;
   CHECK(PathService::Get(DIR_CAST_HOME, &home_dir));
diff --git a/chromecast/browser/cast_web_contents_manager.cc b/chromecast/browser/cast_web_contents_manager.cc
new file mode 100644
index 0000000..1ab1a2b
--- /dev/null
+++ b/chromecast/browser/cast_web_contents_manager.cc
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/cast_web_contents_manager.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "content/public/browser/media_session.h"
+#include "content/public/browser/web_contents.h"
+
+namespace chromecast {
+
+CastWebContentsManager::CastWebContentsManager(
+    content::BrowserContext* browser_context)
+    : browser_context_(browser_context),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      weak_factory_(this) {
+  DCHECK(browser_context_);
+  DCHECK(task_runner_);
+}
+
+CastWebContentsManager::~CastWebContentsManager() = default;
+
+std::unique_ptr<CastWebView> CastWebContentsManager::CreateWebView(
+    CastWebView::Delegate* delegate,
+    scoped_refptr<content::SiteInstance> site_instance,
+    bool transparent) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return base::MakeUnique<CastWebView>(delegate, this, browser_context_,
+                                       site_instance, transparent);
+}
+
+void CastWebContentsManager::DelayWebContentsDeletion(
+    std::unique_ptr<content::WebContents> web_contents,
+    base::TimeDelta time_delta) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(web_contents);
+  if (time_delta <= base::TimeDelta()) {
+    LOG(INFO) << "Deleting WebContents for " << web_contents->GetVisibleURL();
+    web_contents.reset();
+    return;
+  }
+  auto* web_contents_ptr = web_contents.get();
+  // Suspend the MediaSession to free up media resources for the next content
+  // window.
+  content::MediaSession::Get(web_contents_ptr)
+      ->Suspend(content::MediaSession::SuspendType::SYSTEM);
+  LOG(INFO) << "WebContents for " << web_contents->GetVisibleURL()
+            << " will be deleted in " << time_delta.InMilliseconds()
+            << " milliseconds.";
+  expiring_web_contents_.insert(std::move(web_contents));
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&CastWebContentsManager::DeleteWebContents,
+                     weak_factory_.GetWeakPtr(), web_contents_ptr),
+      time_delta);
+}
+
+void CastWebContentsManager::DeleteWebContents(
+    content::WebContents* web_contents) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(web_contents);
+  LOG(INFO) << "Deleting WebContents for " << web_contents->GetVisibleURL();
+  base::EraseIf(
+      expiring_web_contents_,
+      [web_contents](const std::unique_ptr<content::WebContents>& ptr) {
+        return ptr.get() == web_contents;
+      });
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_web_contents_manager.h b/chromecast/browser/cast_web_contents_manager.h
new file mode 100644
index 0000000..fe89cca
--- /dev/null
+++ b/chromecast/browser/cast_web_contents_manager.h
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_MANAGER_H_
+#define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_MANAGER_H_
+
+#include <memory>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chromecast/browser/cast_web_view.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}  // namespace content
+
+namespace chromecast {
+
+// This class dispenses CastWebView objects which are used to wrap WebContents
+// in cast_shell. This class can take ownership of a WebContents instance when
+// the page is in the process of tearing down; we cannot simply post a delayed
+// task since WebContents may try to use browser objects that get deleted as a
+// result of browser shutdown.
+class CastWebContentsManager {
+ public:
+  explicit CastWebContentsManager(content::BrowserContext* browser_context);
+  ~CastWebContentsManager();
+
+  std::unique_ptr<CastWebView> CreateWebView(
+      CastWebView::Delegate* delegate,
+      scoped_refptr<content::SiteInstance> site_instance,
+      bool transparent);
+
+  // Take ownership of |web_contents| and delete after |time_delta|, or sooner
+  // if necessary.
+  void DelayWebContentsDeletion(
+      std::unique_ptr<content::WebContents> web_contents,
+      base::TimeDelta time_delta);
+
+ private:
+  void DeleteWebContents(content::WebContents* web_contents);
+
+  content::BrowserContext* const browser_context_;
+  base::flat_set<std::unique_ptr<content::WebContents>> expiring_web_contents_;
+
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CastWebContentsManager> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastWebContentsManager);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_WEB_CONTENTS_MANAGER_H_
diff --git a/chromecast/browser/cast_web_view.cc b/chromecast/browser/cast_web_view.cc
index 358c5aa8..113c22d 100644
--- a/chromecast/browser/cast_web_view.cc
+++ b/chromecast/browser/cast_web_view.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromecast/base/metrics/cast_metrics_helper.h"
+#include "chromecast/browser/cast_web_contents_manager.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
@@ -31,9 +32,6 @@
 namespace chromecast {
 
 namespace {
-// The time (in milliseconds) we wait for after a page is closed (i.e.
-// after an app is stopped) before we delete the corresponding WebContents.
-constexpr int kWebContentsDestructionDelayInMs = 50;
 
 std::unique_ptr<content::WebContents> CreateWebContents(
     content::BrowserContext* browser_context,
@@ -55,10 +53,12 @@
 }  // namespace
 
 CastWebView::CastWebView(Delegate* delegate,
+                         CastWebContentsManager* web_contents_manager,
                          content::BrowserContext* browser_context,
                          scoped_refptr<content::SiteInstance> site_instance,
                          bool transparent)
     : delegate_(delegate),
+      web_contents_manager_(web_contents_manager),
       browser_context_(browser_context),
       site_instance_(std::move(site_instance)),
       transparent_(transparent),
@@ -67,6 +67,7 @@
       did_start_navigation_(false),
       weak_factory_(this) {
   DCHECK(delegate_);
+  DCHECK(web_contents_manager_);
   DCHECK(browser_context_);
   DCHECK(window_);
   content::WebContentsObserver::Observe(web_contents_.get());
@@ -83,31 +84,21 @@
                                          ui::PAGE_TRANSITION_TYPED, "");
 }
 
-void CastWebView::ClosePage() {
+void CastWebView::ClosePage(const base::TimeDelta& shutdown_delay) {
+  shutdown_delay_ = shutdown_delay;
   content::WebContentsObserver::Observe(nullptr);
+  web_contents_->DispatchBeforeUnload();
   web_contents_->ClosePage();
 }
 
 void CastWebView::CloseContents(content::WebContents* source) {
   DCHECK_EQ(source, web_contents_.get());
-
+  window_.reset();  // Window destructor requires live web_contents on Android.
   // We need to delay the deletion of web_contents_ to give (and guarantee) the
   // renderer enough time to finish 'onunload' handler (but we don't want to
   // wait any longer than that to delay the starting of next app).
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&CastWebView::DelayedCloseContents,
-                            weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(kWebContentsDestructionDelayInMs));
-}
-
-void CastWebView::DelayedCloseContents() {
-  // Delete the WebContents object here so that the gfx surface will be
-  // deleted as part of destroying RenderWidgetHostViewCast object.
-  // We want to delete the surface before we start the next app because
-  // the next app could be an external one whose Start() function would
-  // destroy the primary gfx plane.
-  window_.reset();  // Window destructor requires live web_contents on Android.
-  web_contents_.reset();
+  web_contents_manager_->DelayWebContentsDeletion(std::move(web_contents_),
+                                                  shutdown_delay_);
   delegate_->OnPageStopped(net::OK);
 }
 
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index b6231c96..61589f32 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "chromecast/browser/cast_content_window.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -22,6 +23,7 @@
 
 namespace chromecast {
 
+class CastWebContentsManager;
 class CastWindowManager;
 
 // A simplified interface for loading and displaying WebContents in cast_shell.
@@ -42,6 +44,7 @@
   // |delegate| and |browser_context| should outlive the lifetime of this
   // object.
   CastWebView(Delegate* delegate,
+              CastWebContentsManager* web_contents_manager,
               content::BrowserContext* browser_context,
               scoped_refptr<content::SiteInstance> site_instance,
               bool transparent);
@@ -57,8 +60,9 @@
 
   // Begins the close process for this page (ie. triggering document.onunload).
   // A consumer of the class can be notified when the process has been finished
-  // via Delegate::OnPageStopped().
-  void ClosePage();
+  // via Delegate::OnPageStopped(). The page will be torn down after
+  // |shutdown_delay| has elapsed, or sooner if required.
+  void ClosePage(const base::TimeDelta& shutdown_delay);
 
   // Makes the page visible to the user.
   void Show(CastWindowManager* window_manager);
@@ -94,15 +98,15 @@
       override;
 #endif  // defined(OS_ANDROID)
 
-  void DelayedCloseContents();
-
   Delegate* const delegate_;
+  CastWebContentsManager* const web_contents_manager_;
   content::BrowserContext* const browser_context_;
   const scoped_refptr<content::SiteInstance> site_instance_;
   const bool transparent_;
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<shell::CastContentWindow> window_;
   bool did_start_navigation_;
+  base::TimeDelta shutdown_delay_;
 
   base::WeakPtrFactory<CastWebView> weak_factory_;
 
diff --git a/chromecast/browser/service/cast_service_simple.cc b/chromecast/browser/service/cast_service_simple.cc
index 56144b7..3770d50b 100644
--- a/chromecast/browser/service/cast_service_simple.cc
+++ b/chromecast/browser/service/cast_service_simple.cc
@@ -9,6 +9,8 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "chromecast/browser/cast_web_contents_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
 #include "net/base/filename_util.h"
@@ -39,7 +41,9 @@
                                      PrefService* pref_service,
                                      CastWindowManager* window_manager)
     : CastService(browser_context, pref_service),
-      window_manager_(window_manager) {
+      window_manager_(window_manager),
+      web_contents_manager_(
+          base::MakeUnique<CastWebContentsManager>(browser_context)) {
   DCHECK(window_manager_);
 }
 
@@ -58,16 +62,15 @@
     return;
   }
 
-  cast_web_view_ = base::MakeUnique<CastWebView>(this, browser_context(),
-                                                 /*site_instance*/ nullptr,
-                                                 /*transparent*/ false);
+  cast_web_view_ = web_contents_manager_->CreateWebView(
+      this, /*site_instance*/ nullptr, /*transparent*/ false);
   cast_web_view_->LoadUrl(startup_url_);
   cast_web_view_->Show(window_manager_);
 }
 
 void CastServiceSimple::StopInternal() {
   if (cast_web_view_) {
-    cast_web_view_->ClosePage();
+    cast_web_view_->ClosePage(base::TimeDelta());
   }
   cast_web_view_.reset();
 }
diff --git a/chromecast/browser/service/cast_service_simple.h b/chromecast/browser/service/cast_service_simple.h
index 1a48b89..c8525a9 100644
--- a/chromecast/browser/service/cast_service_simple.h
+++ b/chromecast/browser/service/cast_service_simple.h
@@ -13,6 +13,7 @@
 #include "url/gurl.h"
 
 namespace chromecast {
+class CastWebContentsManager;
 class CastWindowManager;
 
 namespace shell {
@@ -41,6 +42,7 @@
 
  private:
   CastWindowManager* const window_manager_;
+  const std::unique_ptr<CastWebContentsManager> web_contents_manager_;
   std::unique_ptr<CastWebView> cast_web_view_;
   GURL startup_url_;
 
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index b3c71f5db..9528a10e 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -13,6 +13,7 @@
 #include "chromecast/browser/cast_browser_context.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_content_window.h"
+#include "chromecast/browser/cast_web_contents_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
@@ -44,6 +45,8 @@
   base::RunLoop().RunUntilIdle();
 
   metrics::CastMetricsHelper::GetInstance()->SetDummySessionIdForTesting();
+  web_contents_manager_ = base::MakeUnique<CastWebContentsManager>(
+      CastBrowserProcess::GetInstance()->browser_context());
 }
 
 void CastBrowserTest::PostRunTestOnMainThread() {
@@ -51,9 +54,8 @@
 }
 
 content::WebContents* CastBrowserTest::NavigateToURL(const GURL& url) {
-  cast_web_view_ = base::WrapUnique(new CastWebView(
-      this, CastBrowserProcess::GetInstance()->browser_context(), nullptr,
-      false /*transparent*/));
+  cast_web_view_ = web_contents_manager_->CreateWebView(
+      this, nullptr /*site_instance*/, false /*transparent*/);
 
   content::WebContents* web_contents = cast_web_view_->web_contents();
   content::WaitForLoadStop(web_contents);
diff --git a/chromecast/browser/test/cast_browser_test.h b/chromecast/browser/test/cast_browser_test.h
index 895384e..f86c5f0 100644
--- a/chromecast/browser/test/cast_browser_test.h
+++ b/chromecast/browser/test/cast_browser_test.h
@@ -17,6 +17,9 @@
 }
 
 namespace chromecast {
+
+class CastWebContentsManager;
+
 namespace shell {
 
 // This test allows for running an entire browser-process lifecycle per unit
@@ -44,6 +47,7 @@
   void OnWindowDestroyed() override;
   void OnKeyEvent(const ui::KeyEvent& key_event) override;
 
+  std::unique_ptr<CastWebContentsManager> web_contents_manager_;
   std::unique_ptr<CastWebView> cast_web_view_;
 
   DISALLOW_COPY_AND_ASSIGN(CastBrowserTest);
diff --git a/chromecast/media/cdm/cast_cdm_factory.cc b/chromecast/media/cdm/cast_cdm_factory.cc
index 2d0cca55..a09de7c0 100644
--- a/chromecast/media/cdm/cast_cdm_factory.cc
+++ b/chromecast/media/cdm/cast_cdm_factory.cc
@@ -11,6 +11,7 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/cdm_config.h"
 #include "media/base/cdm_key_information.h"
+#include "url/origin.h"
 
 namespace chromecast {
 namespace media {
@@ -28,7 +29,7 @@
 
 void CastCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const ::media::CdmConfig& cdm_config,
     const ::media::SessionMessageCB& session_message_cb,
     const ::media::SessionClosedCB& session_closed_cb,
@@ -67,7 +68,7 @@
 
 scoped_refptr<CastCdm> CastCdmFactory::CreatePlatformBrowserCdm(
     const CastKeySystem& cast_key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const ::media::CdmConfig& cdm_config) {
   return nullptr;
 }
diff --git a/chromecast/media/cdm/cast_cdm_factory.h b/chromecast/media/cdm/cast_cdm_factory.h
index 24120eb7..cd477d2 100644
--- a/chromecast/media/cdm/cast_cdm_factory.h
+++ b/chromecast/media/cdm/cast_cdm_factory.h
@@ -33,7 +33,7 @@
   // ::media::CdmFactory implementation:
   void Create(
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       const ::media::CdmConfig& cdm_config,
       const ::media::SessionMessageCB& session_message_cb,
       const ::media::SessionClosedCB& session_closed_cb,
@@ -44,7 +44,7 @@
   // Provides a platform-specific BrowserCdm instance.
   virtual scoped_refptr<CastCdm> CreatePlatformBrowserCdm(
       const CastKeySystem& cast_key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       const ::media::CdmConfig& cdm_config);
 
  protected:
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 2478b28..aeb23ca 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9864.0.0
\ No newline at end of file
+9896.0.0
\ No newline at end of file
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index ba889cb8..2f38e9f 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -30,6 +30,8 @@
     "device_status_util.h",
     "disconnect_tethering_operation.cc",
     "disconnect_tethering_operation.h",
+    "disconnect_tethering_request_sender.cc",
+    "disconnect_tethering_request_sender.h",
     "host_connection_metrics_logger.cc",
     "host_connection_metrics_logger.h",
     "host_scan_cache.cc",
@@ -47,6 +49,8 @@
     "host_scanner_operation.h",
     "initializer.cc",
     "initializer.h",
+    "initializer_impl.cc",
+    "initializer_impl.h",
     "keep_alive_operation.cc",
     "keep_alive_operation.h",
     "keep_alive_scheduler.cc",
@@ -115,8 +119,12 @@
     "fake_active_host.h",
     "fake_ble_connection_manager.cc",
     "fake_ble_connection_manager.h",
+    "fake_disconnect_tethering_request_sender.cc",
+    "fake_disconnect_tethering_request_sender.h",
     "fake_host_scan_cache.cc",
     "fake_host_scan_cache.h",
+    "fake_initializer.cc",
+    "fake_initializer.h",
     "fake_network_configuration_remover.cc",
     "fake_network_configuration_remover.h",
     "fake_notification_presenter.cc",
@@ -170,7 +178,7 @@
     "host_scan_scheduler_unittest.cc",
     "host_scanner_operation_unittest.cc",
     "host_scanner_unittest.cc",
-    "initializer_unittest.cc",
+    "initializer_impl_unittest.cc",
     "keep_alive_operation_unittest.cc",
     "keep_alive_scheduler_unittest.cc",
     "master_host_scan_cache_unittest.cc",
diff --git a/chromeos/components/tether/disconnect_tethering_request_sender.cc b/chromeos/components/tether/disconnect_tethering_request_sender.cc
new file mode 100644
index 0000000..543b9e4
--- /dev/null
+++ b/chromeos/components/tether/disconnect_tethering_request_sender.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/disconnect_tethering_request_sender.h"
+
+namespace chromeos {
+
+namespace tether {
+
+DisconnectTetheringRequestSender::DisconnectTetheringRequestSender() {}
+
+DisconnectTetheringRequestSender::~DisconnectTetheringRequestSender() {}
+
+void DisconnectTetheringRequestSender::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void DisconnectTetheringRequestSender::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void DisconnectTetheringRequestSender::
+    NotifyPendingDisconnectRequestsComplete() {
+  for (auto& observer : observer_list_)
+    observer.OnPendingDisconnectRequestsComplete();
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/disconnect_tethering_request_sender.h b/chromeos/components/tether/disconnect_tethering_request_sender.h
new file mode 100644
index 0000000..935b9039
--- /dev/null
+++ b/chromeos/components/tether/disconnect_tethering_request_sender.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_DISCONNECT_TETHERING_REQUEST_SENDER_H_
+#define CHROMEOS_COMPONENTS_TETHER_DISCONNECT_TETHERING_REQUEST_SENDER_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// Sends a DisconnectTetheringRequest to the formerly active host. Supports
+// multiple concurrent messages.
+class DisconnectTetheringRequestSender {
+ public:
+  class Observer {
+   public:
+    Observer() {}
+    virtual ~Observer() {}
+
+    virtual void OnPendingDisconnectRequestsComplete() {}
+  };
+
+  DisconnectTetheringRequestSender();
+  virtual ~DisconnectTetheringRequestSender();
+
+  // Sends a DisconnectTetheringRequest to the device with the given ID.
+  virtual void SendDisconnectRequestToDevice(const std::string& device_id) = 0;
+
+  // Returns whether at least one DisconnectTetheringRequest is still in the
+  // process of being sent.
+  virtual bool HasPendingRequests() = 0;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ protected:
+  void NotifyPendingDisconnectRequestsComplete();
+
+ private:
+  base::ObserverList<Observer> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisconnectTetheringRequestSender);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_DISCONNECT_TETHERING_REQUEST_SENDER_H_
diff --git a/chromeos/components/tether/fake_disconnect_tethering_request_sender.cc b/chromeos/components/tether/fake_disconnect_tethering_request_sender.cc
new file mode 100644
index 0000000..b4234e4
--- /dev/null
+++ b/chromeos/components/tether/fake_disconnect_tethering_request_sender.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/fake_disconnect_tethering_request_sender.h"
+
+namespace chromeos {
+
+namespace tether {
+
+FakeDisconnectTetheringRequestSender::FakeDisconnectTetheringRequestSender() {}
+
+FakeDisconnectTetheringRequestSender::~FakeDisconnectTetheringRequestSender() {}
+
+void FakeDisconnectTetheringRequestSender::SendDisconnectRequestToDevice(
+    const std::string& device_id) {
+  // TODO(lesliewatkins): Flesh out.
+}
+
+bool FakeDisconnectTetheringRequestSender::HasPendingRequests() {
+  // TODO(lesliewatkins): Flesh out.
+  return false;
+}
+
+void FakeDisconnectTetheringRequestSender::
+    NotifyPendingDisconnectRequestsComplete() {
+  DisconnectTetheringRequestSender::NotifyPendingDisconnectRequestsComplete();
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/fake_disconnect_tethering_request_sender.h b/chromeos/components/tether/fake_disconnect_tethering_request_sender.h
new file mode 100644
index 0000000..025414b
--- /dev/null
+++ b/chromeos/components/tether/fake_disconnect_tethering_request_sender.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_FAKE_DISCONNECT_TETHERING_REQUEST_SENDER_H_
+#define CHROMEOS_COMPONENTS_TETHER_FAKE_DISCONNECT_TETHERING_REQUEST_SENDER_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "chromeos/components/tether/disconnect_tethering_request_sender.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// Test double for DisconnectTetheringRequestSender.
+class FakeDisconnectTetheringRequestSender
+    : public DisconnectTetheringRequestSender {
+ public:
+  FakeDisconnectTetheringRequestSender();
+  ~FakeDisconnectTetheringRequestSender() override;
+
+  // DisconnectTetheringRequestSender:
+  void SendDisconnectRequestToDevice(const std::string& device_id) override;
+  bool HasPendingRequests() override;
+
+  void NotifyPendingDisconnectRequestsComplete();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeDisconnectTetheringRequestSender);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_FAKE_DISCONNECT_TETHERING_REQUEST_SENDER_H_
diff --git a/chromeos/components/tether/fake_initializer.cc b/chromeos/components/tether/fake_initializer.cc
new file mode 100644
index 0000000..0b4e109c
--- /dev/null
+++ b/chromeos/components/tether/fake_initializer.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/fake_initializer.h"
+
+namespace chromeos {
+
+namespace tether {
+
+FakeInitializer::FakeInitializer(bool has_asynchronous_shutdown)
+    : has_asynchronous_shutdown_(has_asynchronous_shutdown) {}
+
+FakeInitializer::~FakeInitializer() {}
+
+void FakeInitializer::FinishAsynchronousShutdown() {
+  DCHECK(status() == Initializer::Status::SHUTTING_DOWN);
+  TransitionToStatus(Initializer::Status::SHUT_DOWN);
+}
+
+void FakeInitializer::RequestShutdown() {
+  DCHECK(status() == Initializer::Status::ACTIVE);
+
+  if (has_asynchronous_shutdown_)
+    TransitionToStatus(Initializer::Status::SHUTTING_DOWN);
+  else
+    TransitionToStatus(Initializer::Status::SHUT_DOWN);
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/fake_initializer.h b/chromeos/components/tether/fake_initializer.h
new file mode 100644
index 0000000..b7f4234
--- /dev/null
+++ b/chromeos/components/tether/fake_initializer.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_FAKE_INITIALIZER_H_
+#define CHROMEOS_COMPONENTS_TETHER_FAKE_INITIALIZER_H_
+
+#include "base/macros.h"
+#include "chromeos/components/tether/initializer.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// Test double for Initializer.
+class FakeInitializer : public Initializer {
+ public:
+  explicit FakeInitializer(bool has_asynchronous_shutdown);
+  ~FakeInitializer() override;
+
+  void set_has_asynchronous_shutdown(bool has_asynchronous_shutdown) {
+    has_asynchronous_shutdown_ = has_asynchronous_shutdown;
+  }
+
+  // Should only be called when status() == SHUTTING_DOWN.
+  void FinishAsynchronousShutdown();
+
+  // Initializer:
+  void RequestShutdown() override;
+
+ private:
+  bool has_asynchronous_shutdown_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeInitializer);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_FAKE_INITIALIZER_H_
diff --git a/chromeos/components/tether/initializer.cc b/chromeos/components/tether/initializer.cc
index 1eb416e..495e700 100644
--- a/chromeos/components/tether/initializer.cc
+++ b/chromeos/components/tether/initializer.cc
@@ -1,238 +1,36 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "chromeos/components/tether/initializer.h"
 
-#include "base/bind.h"
-#include "chromeos/components/tether/active_host.h"
-#include "chromeos/components/tether/active_host_network_state_updater.h"
-#include "chromeos/components/tether/ble_connection_manager.h"
-#include "chromeos/components/tether/crash_recovery_manager.h"
-#include "chromeos/components/tether/device_id_tether_network_guid_map.h"
-#include "chromeos/components/tether/host_connection_metrics_logger.h"
-#include "chromeos/components/tether/host_scan_device_prioritizer_impl.h"
-#include "chromeos/components/tether/host_scan_scheduler.h"
-#include "chromeos/components/tether/host_scanner.h"
-#include "chromeos/components/tether/keep_alive_scheduler.h"
-#include "chromeos/components/tether/master_host_scan_cache.h"
-#include "chromeos/components/tether/network_configuration_remover.h"
-#include "chromeos/components/tether/network_connection_handler_tether_delegate.h"
-#include "chromeos/components/tether/network_host_scan_cache.h"
-#include "chromeos/components/tether/notification_presenter.h"
-#include "chromeos/components/tether/notification_remover.h"
-#include "chromeos/components/tether/persistent_host_scan_cache_impl.h"
-#include "chromeos/components/tether/tether_connector.h"
-#include "chromeos/components/tether/tether_disconnector_impl.h"
-#include "chromeos/components/tether/tether_host_fetcher.h"
-#include "chromeos/components/tether/tether_host_response_recorder.h"
-#include "chromeos/components/tether/tether_network_disconnection_handler.h"
-#include "chromeos/components/tether/timer_factory.h"
-#include "chromeos/components/tether/wifi_hotspot_connector.h"
-#include "chromeos/network/managed_network_configuration_handler.h"
-#include "chromeos/network/network_connect.h"
-#include "chromeos/network/network_connection_handler.h"
-#include "chromeos/network/network_state_handler.h"
-#include "components/cryptauth/bluetooth_throttler_impl.h"
-#include "components/cryptauth/cryptauth_service.h"
-#include "components/cryptauth/local_device_data_provider.h"
-#include "components/cryptauth/remote_beacon_seed_fetcher.h"
-#include "components/prefs/pref_service.h"
-#include "components/proximity_auth/logging/logging.h"
-
 namespace chromeos {
 
 namespace tether {
 
-// static
-Initializer* Initializer::instance_ = nullptr;
+Initializer::Initializer() {}
 
-// static
-void Initializer::Init(
-    cryptauth::CryptAuthService* cryptauth_service,
-    NotificationPresenter* notification_presenter,
-    PrefService* pref_service,
-    ProfileOAuth2TokenService* token_service,
-    NetworkStateHandler* network_state_handler,
-    ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
-    NetworkConnect* network_connect,
-    NetworkConnectionHandler* network_connection_handler,
-    scoped_refptr<device::BluetoothAdapter> adapter) {
-  if (instance_) {
-    // The Tether feature has already been initialized. No need to do anything.
+Initializer::~Initializer() {}
+
+void Initializer::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void Initializer::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void Initializer::TransitionToStatus(Status new_status) {
+  if (status_ == new_status)
     return;
-  }
 
-  PA_LOG(INFO) << "Initializing Tether feature.";
-  instance_ =
-      new Initializer(cryptauth_service, std::move(notification_presenter),
-                      pref_service, token_service, network_state_handler,
-                      managed_network_configuration_handler, network_connect,
-                      network_connection_handler, adapter);
-}
+  status_ = new_status;
 
-// static
-void Initializer::Shutdown() {
-  if (instance_) {
-    PA_LOG(INFO) << "Shutting down Tether feature.";
-    delete instance_;
-    instance_ = nullptr;
-  }
-}
-
-// static
-void Initializer::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  ActiveHost::RegisterPrefs(registry);
-  PersistentHostScanCacheImpl::RegisterPrefs(registry);
-  TetherHostResponseRecorder::RegisterPrefs(registry);
-  TetherDisconnectorImpl::RegisterPrefs(registry);
-}
-
-Initializer::Initializer(
-    cryptauth::CryptAuthService* cryptauth_service,
-    NotificationPresenter* notification_presenter,
-    PrefService* pref_service,
-    ProfileOAuth2TokenService* token_service,
-    NetworkStateHandler* network_state_handler,
-    ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
-    NetworkConnect* network_connect,
-    NetworkConnectionHandler* network_connection_handler,
-    scoped_refptr<device::BluetoothAdapter> adapter)
-    : cryptauth_service_(cryptauth_service),
-      notification_presenter_(notification_presenter),
-      pref_service_(pref_service),
-      token_service_(token_service),
-      network_state_handler_(network_state_handler),
-      managed_network_configuration_handler_(
-          managed_network_configuration_handler),
-      network_connect_(network_connect),
-      network_connection_handler_(network_connection_handler),
-      adapter_(adapter),
-      weak_ptr_factory_(this) {
-  if (!token_service_->RefreshTokenIsAvailable(
-          cryptauth_service_->GetAccountId())) {
-    PA_LOG(INFO) << "Refresh token not yet available; "
-                 << "waiting for valid token to initializing tether feature.";
-    token_service_->AddObserver(this);
+  if (status_ != Status::SHUT_DOWN)
     return;
-  }
 
-  PA_LOG(INFO) << "Refresh token is available; initializing tether feature.";
-  CreateComponent();
-}
-
-Initializer::~Initializer() {
-  token_service_->RemoveObserver(this);
-  network_state_handler_->set_tether_sort_delegate(nullptr);
-}
-
-void Initializer::OnRefreshTokensLoaded() {
-  if (!token_service_->RefreshTokenIsAvailable(
-          cryptauth_service_->GetAccountId())) {
-    // If a token for the active account is still not available, continue
-    // waiting for a new token.
-    return;
-  }
-
-  PA_LOG(INFO) << "Refresh token has loaded; initializing tether feature.";
-
-  token_service_->RemoveObserver(this);
-  CreateComponent();
-}
-
-void Initializer::CreateComponent() {
-  PA_LOG(INFO) << "Successfully set Bluetooth advertisement interval. "
-               << "Initializing tether feature.";
-
-  tether_host_fetcher_ =
-      base::MakeUnique<TetherHostFetcher>(cryptauth_service_);
-  local_device_data_provider_ =
-      base::MakeUnique<cryptauth::LocalDeviceDataProvider>(cryptauth_service_);
-  remote_beacon_seed_fetcher_ =
-      base::MakeUnique<cryptauth::RemoteBeaconSeedFetcher>(
-          cryptauth_service_->GetCryptAuthDeviceManager());
-  ble_connection_manager_ = base::MakeUnique<BleConnectionManager>(
-      cryptauth_service_, adapter_, local_device_data_provider_.get(),
-      remote_beacon_seed_fetcher_.get(),
-      cryptauth::BluetoothThrottlerImpl::GetInstance());
-  tether_host_response_recorder_ =
-      base::MakeUnique<TetherHostResponseRecorder>(pref_service_);
-  device_id_tether_network_guid_map_ =
-      base::MakeUnique<DeviceIdTetherNetworkGuidMap>();
-  host_scan_device_prioritizer_ =
-      base::MakeUnique<HostScanDevicePrioritizerImpl>(
-          tether_host_response_recorder_.get(),
-          device_id_tether_network_guid_map_.get());
-  network_state_handler_->set_tether_sort_delegate(
-      host_scan_device_prioritizer_.get());
-  wifi_hotspot_connector_ = base::MakeUnique<WifiHotspotConnector>(
-      network_state_handler_, network_connect_);
-  active_host_ =
-      base::MakeUnique<ActiveHost>(tether_host_fetcher_.get(), pref_service_);
-  active_host_network_state_updater_ =
-      base::MakeUnique<ActiveHostNetworkStateUpdater>(active_host_.get(),
-                                                      network_state_handler_);
-  persistent_host_scan_cache_ =
-      base::MakeUnique<PersistentHostScanCacheImpl>(pref_service_);
-  network_host_scan_cache_ = base::MakeUnique<NetworkHostScanCache>(
-      network_state_handler_, tether_host_response_recorder_.get(),
-      device_id_tether_network_guid_map_.get());
-  master_host_scan_cache_ = base::MakeUnique<MasterHostScanCache>(
-      base::MakeUnique<TimerFactory>(), active_host_.get(),
-      network_host_scan_cache_.get(), persistent_host_scan_cache_.get());
-  notification_remover_ = base::MakeUnique<NotificationRemover>(
-      network_state_handler_, notification_presenter_,
-      master_host_scan_cache_.get(), active_host_.get());
-  keep_alive_scheduler_ = base::MakeUnique<KeepAliveScheduler>(
-      active_host_.get(), ble_connection_manager_.get(),
-      master_host_scan_cache_.get(), device_id_tether_network_guid_map_.get());
-  clock_ = base::MakeUnique<base::DefaultClock>();
-  host_scanner_ = base::MakeUnique<HostScanner>(
-      network_state_handler_, tether_host_fetcher_.get(),
-      ble_connection_manager_.get(), host_scan_device_prioritizer_.get(),
-      tether_host_response_recorder_.get(), notification_presenter_,
-      device_id_tether_network_guid_map_.get(), master_host_scan_cache_.get(),
-      clock_.get());
-  host_scan_scheduler_ = base::MakeUnique<HostScanScheduler>(
-      network_state_handler_, host_scanner_.get());
-  host_connection_metrics_logger_ =
-      base::MakeUnique<HostConnectionMetricsLogger>();
-  tether_connector_ = base::MakeUnique<TetherConnector>(
-      network_state_handler_, wifi_hotspot_connector_.get(), active_host_.get(),
-      tether_host_fetcher_.get(), ble_connection_manager_.get(),
-      tether_host_response_recorder_.get(),
-      device_id_tether_network_guid_map_.get(), master_host_scan_cache_.get(),
-      notification_presenter_, host_connection_metrics_logger_.get());
-  network_configuration_remover_ =
-      base::MakeUnique<NetworkConfigurationRemover>(
-          network_state_handler_, managed_network_configuration_handler_);
-  tether_disconnector_ = base::MakeUnique<TetherDisconnectorImpl>(
-      network_connection_handler_, network_state_handler_, active_host_.get(),
-      ble_connection_manager_.get(), network_configuration_remover_.get(),
-      tether_connector_.get(), device_id_tether_network_guid_map_.get(),
-      tether_host_fetcher_.get(), pref_service_);
-  tether_network_disconnection_handler_ =
-      base::MakeUnique<TetherNetworkDisconnectionHandler>(
-          active_host_.get(), network_state_handler_,
-          network_configuration_remover_.get());
-  network_connection_handler_tether_delegate_ =
-      base::MakeUnique<NetworkConnectionHandlerTetherDelegate>(
-          network_connection_handler_, tether_connector_.get(),
-          tether_disconnector_.get());
-
-  crash_recovery_manager_ = base::MakeUnique<CrashRecoveryManager>(
-      network_state_handler_, active_host_.get(),
-      master_host_scan_cache_.get());
-  crash_recovery_manager_->RestorePreCrashStateIfNecessary(base::Bind(
-      &Initializer::OnPreCrashStateRestored, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void Initializer::OnPreCrashStateRestored() {
-  // |crash_recovery_manager_| is no longer needed since it has completed.
-  crash_recovery_manager_.reset();
-
-  // Start a scan now that the Tether module has started up.
-  host_scan_scheduler_->ScheduleScan();
+  for (auto& observer : observer_list_)
+    observer.OnShutdownComplete();
 }
 
 }  // namespace tether
diff --git a/chromeos/components/tether/initializer.h b/chromeos/components/tether/initializer.h
index c58993c..b4a841b 100644
--- a/chromeos/components/tether/initializer.h
+++ b/chromeos/components/tether/initializer.h
@@ -1,154 +1,50 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CHROMEOS_COMPONENTS_TETHER_INITIALIZER_H_
 #define CHROMEOS_COMPONENTS_TETHER_INITIALIZER_H_
 
-#include <memory>
-
-#include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/default_clock.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "device/bluetooth/bluetooth_adapter.h"
-#include "device/bluetooth/bluetooth_advertisement.h"
-
-class PrefService;
-
-namespace cryptauth {
-class CryptAuthService;
-class LocalDeviceDataProvider;
-class RemoteBeaconSeedFetcher;
-}
+#include "base/observer_list.h"
 
 namespace chromeos {
 
-class ManagedNetworkConfigurationHandler;
-class NetworkConnect;
-class NetworkConnectionHandler;
-class NetworkStateHandler;
-
 namespace tether {
 
-class ActiveHost;
-class ActiveHostNetworkStateUpdater;
-class BleConnectionManager;
-class CrashRecoveryManager;
-class NetworkConnectionHandlerTetherDelegate;
-class DeviceIdTetherNetworkGuidMap;
-class HostScanner;
-class HostScanScheduler;
-class HostScanDevicePrioritizerImpl;
-class KeepAliveScheduler;
-class HostConnectionMetricsLogger;
-class MasterHostScanCache;
-class NetworkConfigurationRemover;
-class NetworkHostScanCache;
-class NotificationPresenter;
-class NotificationRemover;
-class PersistentHostScanCache;
-class TetherConnector;
-class TetherDisconnector;
-class TetherHostFetcher;
-class TetherHostResponseRecorder;
-class TetherNetworkDisconnectionHandler;
-class WifiHotspotConnector;
-
-// Initializes the Tether Chrome OS component.
-class Initializer : public OAuth2TokenService::Observer {
+// Initializes the Tether component.
+class Initializer {
  public:
-  // Initializes the tether feature. If the feature has already been
-  // initialized, this function is a no-op.
-  static void Init(
-      cryptauth::CryptAuthService* cryptauth_service,
-      NotificationPresenter* notification_presenter,
-      PrefService* pref_service,
-      ProfileOAuth2TokenService* token_service,
-      NetworkStateHandler* network_state_handler,
-      ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
-      NetworkConnect* network_connect,
-      NetworkConnectionHandler* network_connection_handler,
-      scoped_refptr<device::BluetoothAdapter> adapter);
+  class Observer {
+   public:
+    Observer() {}
+    virtual ~Observer() {}
 
-  // Shuts down the tether feature, destroying all internal classes. This should
-  // be called before the dependencies passed to Init() are destroyed.
-  static void Shutdown();
+    virtual void OnShutdownComplete() = 0;
+  };
 
-  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+  enum class Status { ACTIVE, SHUTTING_DOWN, SHUT_DOWN };
+
+  Initializer();
+  virtual ~Initializer();
+
+  // Requests that the Tether component shuts down. If the component can be shut
+  // down synchronously, this causes Initializer to transition to the SHUT_DOWN
+  // status immediately. However, if the component requires an asynchronous
+  // shutdown, the class transitions to the SHUTTING_DOWN status; once an
+  // asynchronous shutdown completes, Initializer transitions to the SHUT_DOWN
+  // status and notifies observers.
+  virtual void RequestShutdown() = 0;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  Status status() { return status_; }
+  void TransitionToStatus(Status new_status);
 
  private:
-  friend class InitializerTest;
-
-  static Initializer* instance_;
-
-  Initializer(
-      cryptauth::CryptAuthService* cryptauth_service,
-      NotificationPresenter* notification_presenter,
-      PrefService* pref_service,
-      ProfileOAuth2TokenService* token_service,
-      NetworkStateHandler* network_state_handler,
-      ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
-      NetworkConnect* network_connect,
-      NetworkConnectionHandler* network_connection_handler,
-      scoped_refptr<device::BluetoothAdapter> adapter);
-  ~Initializer() override;
-
-  // OAuth2TokenService::Observer:
-  void OnRefreshTokensLoaded() override;
-
-  void CreateComponent();
-  void OnPreCrashStateRestored();
-
-  cryptauth::CryptAuthService* cryptauth_service_;
-  NotificationPresenter* notification_presenter_;
-  PrefService* pref_service_;
-  ProfileOAuth2TokenService* token_service_;
-  NetworkStateHandler* network_state_handler_;
-  ManagedNetworkConfigurationHandler* managed_network_configuration_handler_;
-  NetworkConnect* network_connect_;
-  NetworkConnectionHandler* network_connection_handler_;
-  scoped_refptr<device::BluetoothAdapter> adapter_;
-
-  // Declare new objects in the order that they will be created during
-  // initialization to ensure that they are destroyed in the correct order. This
-  // order will be enforced by InitializerTest.TestCreateAndDestroy.
-  std::unique_ptr<TetherHostFetcher> tether_host_fetcher_;
-  std::unique_ptr<cryptauth::LocalDeviceDataProvider>
-      local_device_data_provider_;
-  std::unique_ptr<cryptauth::RemoteBeaconSeedFetcher>
-      remote_beacon_seed_fetcher_;
-  std::unique_ptr<BleConnectionManager> ble_connection_manager_;
-  std::unique_ptr<TetherHostResponseRecorder> tether_host_response_recorder_;
-  std::unique_ptr<HostScanDevicePrioritizerImpl> host_scan_device_prioritizer_;
-  std::unique_ptr<WifiHotspotConnector> wifi_hotspot_connector_;
-  std::unique_ptr<ActiveHost> active_host_;
-  std::unique_ptr<ActiveHostNetworkStateUpdater>
-      active_host_network_state_updater_;
-  std::unique_ptr<DeviceIdTetherNetworkGuidMap>
-      device_id_tether_network_guid_map_;
-  std::unique_ptr<PersistentHostScanCache> persistent_host_scan_cache_;
-  std::unique_ptr<NetworkHostScanCache> network_host_scan_cache_;
-  std::unique_ptr<MasterHostScanCache> master_host_scan_cache_;
-  std::unique_ptr<NotificationRemover> notification_remover_;
-  std::unique_ptr<KeepAliveScheduler> keep_alive_scheduler_;
-  std::unique_ptr<base::DefaultClock> clock_;
-  std::unique_ptr<HostScanner> host_scanner_;
-  std::unique_ptr<HostScanScheduler> host_scan_scheduler_;
-  std::unique_ptr<HostConnectionMetricsLogger> host_connection_metrics_logger_;
-  std::unique_ptr<TetherConnector> tether_connector_;
-  std::unique_ptr<TetherDisconnector> tether_disconnector_;
-  std::unique_ptr<NetworkConfigurationRemover> network_configuration_remover_;
-  std::unique_ptr<NetworkConnectionHandlerTetherDelegate>
-      network_connection_handler_tether_delegate_;
-  std::unique_ptr<TetherNetworkDisconnectionHandler>
-      tether_network_disconnection_handler_;
-  std::unique_ptr<CrashRecoveryManager> crash_recovery_manager_;
-
-  base::WeakPtrFactory<Initializer> weak_ptr_factory_;
+  Status status_ = Status::ACTIVE;
+  base::ObserverList<Observer> observer_list_;
 
   DISALLOW_COPY_AND_ASSIGN(Initializer);
 };
diff --git a/chromeos/components/tether/initializer_impl.cc b/chromeos/components/tether/initializer_impl.cc
new file mode 100644
index 0000000..66367f4
--- /dev/null
+++ b/chromeos/components/tether/initializer_impl.cc
@@ -0,0 +1,343 @@
+// 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 "chromeos/components/tether/initializer_impl.h"
+
+#include "base/bind.h"
+#include "chromeos/components/tether/active_host.h"
+#include "chromeos/components/tether/active_host_network_state_updater.h"
+#include "chromeos/components/tether/ble_connection_manager.h"
+#include "chromeos/components/tether/crash_recovery_manager.h"
+#include "chromeos/components/tether/device_id_tether_network_guid_map.h"
+#include "chromeos/components/tether/disconnect_tethering_request_sender.h"
+#include "chromeos/components/tether/host_connection_metrics_logger.h"
+#include "chromeos/components/tether/host_scan_device_prioritizer_impl.h"
+#include "chromeos/components/tether/host_scan_scheduler.h"
+#include "chromeos/components/tether/host_scanner.h"
+#include "chromeos/components/tether/keep_alive_scheduler.h"
+#include "chromeos/components/tether/master_host_scan_cache.h"
+#include "chromeos/components/tether/network_configuration_remover.h"
+#include "chromeos/components/tether/network_connection_handler_tether_delegate.h"
+#include "chromeos/components/tether/network_host_scan_cache.h"
+#include "chromeos/components/tether/notification_presenter.h"
+#include "chromeos/components/tether/notification_remover.h"
+#include "chromeos/components/tether/persistent_host_scan_cache_impl.h"
+#include "chromeos/components/tether/tether_connector.h"
+#include "chromeos/components/tether/tether_disconnector_impl.h"
+#include "chromeos/components/tether/tether_host_fetcher.h"
+#include "chromeos/components/tether/tether_host_response_recorder.h"
+#include "chromeos/components/tether/tether_network_disconnection_handler.h"
+#include "chromeos/components/tether/timer_factory.h"
+#include "chromeos/components/tether/wifi_hotspot_connector.h"
+#include "chromeos/network/managed_network_configuration_handler.h"
+#include "chromeos/network/network_connect.h"
+#include "chromeos/network/network_connection_handler.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/cryptauth/bluetooth_throttler_impl.h"
+#include "components/cryptauth/cryptauth_service.h"
+#include "components/cryptauth/local_device_data_provider.h"
+#include "components/cryptauth/remote_beacon_seed_fetcher.h"
+#include "components/prefs/pref_service.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+// TODO(lesliewatkins): Remove this and use the actual
+// DisconnectTetheringRequestSender.
+class DummyDisconnectTetheringRequestSender
+    : public DisconnectTetheringRequestSender {
+ public:
+  DummyDisconnectTetheringRequestSender() {}
+  ~DummyDisconnectTetheringRequestSender() override {}
+
+  // DisconnectTetheringRequestSender:
+  void SendDisconnectRequestToDevice(const std::string& device_id) override {}
+  bool HasPendingRequests() override { return false; }
+};
+
+}  // namespace
+
+// static
+InitializerImpl::Factory* InitializerImpl::Factory::factory_instance_ = nullptr;
+
+// static
+std::unique_ptr<Initializer> InitializerImpl::Factory::NewInstance(
+    cryptauth::CryptAuthService* cryptauth_service,
+    NotificationPresenter* notification_presenter,
+    PrefService* pref_service,
+    ProfileOAuth2TokenService* token_service,
+    NetworkStateHandler* network_state_handler,
+    ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
+    NetworkConnect* network_connect,
+    NetworkConnectionHandler* network_connection_handler,
+    scoped_refptr<device::BluetoothAdapter> adapter) {
+  if (!factory_instance_) {
+    factory_instance_ = new Factory();
+  }
+  return factory_instance_->BuildInstance(
+      cryptauth_service, notification_presenter, pref_service, token_service,
+      network_state_handler, managed_network_configuration_handler,
+      network_connect, network_connection_handler, adapter);
+}
+
+// static
+void InitializerImpl::Factory::SetInstanceForTesting(Factory* factory) {
+  factory_instance_ = factory;
+}
+
+// static
+void InitializerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  ActiveHost::RegisterPrefs(registry);
+  PersistentHostScanCacheImpl::RegisterPrefs(registry);
+  TetherHostResponseRecorder::RegisterPrefs(registry);
+  TetherDisconnectorImpl::RegisterPrefs(registry);
+}
+
+std::unique_ptr<Initializer> InitializerImpl::Factory::BuildInstance(
+    cryptauth::CryptAuthService* cryptauth_service,
+    NotificationPresenter* notification_presenter,
+    PrefService* pref_service,
+    ProfileOAuth2TokenService* token_service,
+    NetworkStateHandler* network_state_handler,
+    ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
+    NetworkConnect* network_connect,
+    NetworkConnectionHandler* network_connection_handler,
+    scoped_refptr<device::BluetoothAdapter> adapter) {
+  return base::WrapUnique(new InitializerImpl(
+      cryptauth_service, notification_presenter, pref_service, token_service,
+      network_state_handler, managed_network_configuration_handler,
+      network_connect, network_connection_handler, adapter));
+}
+
+InitializerImpl::InitializerImpl(
+    cryptauth::CryptAuthService* cryptauth_service,
+    NotificationPresenter* notification_presenter,
+    PrefService* pref_service,
+    ProfileOAuth2TokenService* token_service,
+    NetworkStateHandler* network_state_handler,
+    ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
+    NetworkConnect* network_connect,
+    NetworkConnectionHandler* network_connection_handler,
+    scoped_refptr<device::BluetoothAdapter> adapter)
+    : cryptauth_service_(cryptauth_service),
+      notification_presenter_(notification_presenter),
+      pref_service_(pref_service),
+      token_service_(token_service),
+      network_state_handler_(network_state_handler),
+      managed_network_configuration_handler_(
+          managed_network_configuration_handler),
+      network_connect_(network_connect),
+      network_connection_handler_(network_connection_handler),
+      adapter_(adapter),
+      weak_ptr_factory_(this) {
+  if (!token_service_->RefreshTokenIsAvailable(
+          cryptauth_service_->GetAccountId())) {
+    PA_LOG(INFO) << "Refresh token not yet available; "
+                 << "waiting for valid token to initializing tether feature.";
+    token_service_->AddObserver(this);
+    return;
+  }
+
+  PA_LOG(INFO) << "Refresh token is available; initializing tether feature.";
+  CreateComponent();
+}
+
+InitializerImpl::~InitializerImpl() {
+  token_service_->RemoveObserver(this);
+  network_state_handler_->set_tether_sort_delegate(nullptr);
+
+  if (disconnect_tethering_request_sender_)
+    disconnect_tethering_request_sender_->RemoveObserver(this);
+}
+
+void InitializerImpl::RequestShutdown() {
+  DCHECK(status() == Initializer::Status::ACTIVE);
+
+  if (!disconnect_tethering_request_sender_ ||
+      !disconnect_tethering_request_sender_->HasPendingRequests()) {
+    TransitionToStatus(Initializer::Status::SHUT_DOWN);
+    return;
+  }
+
+  TransitionToStatus(Initializer::Status::SHUTTING_DOWN);
+  StartAsynchronousShutdown();
+}
+
+void InitializerImpl::OnRefreshTokensLoaded() {
+  if (!token_service_->RefreshTokenIsAvailable(
+          cryptauth_service_->GetAccountId())) {
+    // If a token for the active account is still not available, continue
+    // waiting for a new token.
+    return;
+  }
+
+  PA_LOG(INFO) << "Refresh token has loaded; initializing tether feature.";
+
+  token_service_->RemoveObserver(this);
+  CreateComponent();
+}
+
+void InitializerImpl::OnPendingDisconnectRequestsComplete() {
+  DCHECK(status() == Initializer::Status::SHUTTING_DOWN);
+  disconnect_tethering_request_sender_->RemoveObserver(this);
+
+  // Shutdown has completed. It is now safe to delete the objects that were
+  // shutting down asynchronously.
+  disconnect_tethering_request_sender_ =
+      base::MakeUnique<DummyDisconnectTetheringRequestSender>();
+  ble_connection_manager_ = base::MakeUnique<BleConnectionManager>(
+      cryptauth_service_, adapter_, local_device_data_provider_.get(),
+      remote_beacon_seed_fetcher_.get(),
+      cryptauth::BluetoothThrottlerImpl::GetInstance());
+  remote_beacon_seed_fetcher_ =
+      base::MakeUnique<cryptauth::RemoteBeaconSeedFetcher>(
+          cryptauth_service_->GetCryptAuthDeviceManager());
+  local_device_data_provider_ =
+      base::MakeUnique<cryptauth::LocalDeviceDataProvider>(cryptauth_service_);
+  tether_host_fetcher_ =
+      base::MakeUnique<TetherHostFetcher>(cryptauth_service_);
+
+  TransitionToStatus(Initializer::Status::SHUT_DOWN);
+}
+
+void InitializerImpl::CreateComponent() {
+  PA_LOG(INFO) << "Successfully set Bluetooth advertisement interval. "
+               << "Initializing tether feature.";
+
+  tether_host_fetcher_ =
+      base::MakeUnique<TetherHostFetcher>(cryptauth_service_);
+  local_device_data_provider_ =
+      base::MakeUnique<cryptauth::LocalDeviceDataProvider>(cryptauth_service_);
+  remote_beacon_seed_fetcher_ =
+      base::MakeUnique<cryptauth::RemoteBeaconSeedFetcher>(
+          cryptauth_service_->GetCryptAuthDeviceManager());
+  ble_connection_manager_ = base::MakeUnique<BleConnectionManager>(
+      cryptauth_service_, adapter_, local_device_data_provider_.get(),
+      remote_beacon_seed_fetcher_.get(),
+      cryptauth::BluetoothThrottlerImpl::GetInstance());
+  // TODO(lesliewatkins): Use actual DisconnectTetheringRequestSender.
+  disconnect_tethering_request_sender_ =
+      base::MakeUnique<DummyDisconnectTetheringRequestSender>();
+  tether_host_response_recorder_ =
+      base::MakeUnique<TetherHostResponseRecorder>(pref_service_);
+  device_id_tether_network_guid_map_ =
+      base::MakeUnique<DeviceIdTetherNetworkGuidMap>();
+  host_scan_device_prioritizer_ =
+      base::MakeUnique<HostScanDevicePrioritizerImpl>(
+          tether_host_response_recorder_.get(),
+          device_id_tether_network_guid_map_.get());
+  network_state_handler_->set_tether_sort_delegate(
+      host_scan_device_prioritizer_.get());
+  wifi_hotspot_connector_ = base::MakeUnique<WifiHotspotConnector>(
+      network_state_handler_, network_connect_);
+  active_host_ =
+      base::MakeUnique<ActiveHost>(tether_host_fetcher_.get(), pref_service_);
+  active_host_network_state_updater_ =
+      base::MakeUnique<ActiveHostNetworkStateUpdater>(active_host_.get(),
+                                                      network_state_handler_);
+  persistent_host_scan_cache_ =
+      base::MakeUnique<PersistentHostScanCacheImpl>(pref_service_);
+  network_host_scan_cache_ = base::MakeUnique<NetworkHostScanCache>(
+      network_state_handler_, tether_host_response_recorder_.get(),
+      device_id_tether_network_guid_map_.get());
+  master_host_scan_cache_ = base::MakeUnique<MasterHostScanCache>(
+      base::MakeUnique<TimerFactory>(), active_host_.get(),
+      network_host_scan_cache_.get(), persistent_host_scan_cache_.get());
+  notification_remover_ = base::MakeUnique<NotificationRemover>(
+      network_state_handler_, notification_presenter_,
+      master_host_scan_cache_.get(), active_host_.get());
+  keep_alive_scheduler_ = base::MakeUnique<KeepAliveScheduler>(
+      active_host_.get(), ble_connection_manager_.get(),
+      master_host_scan_cache_.get(), device_id_tether_network_guid_map_.get());
+  clock_ = base::MakeUnique<base::DefaultClock>();
+  host_scanner_ = base::MakeUnique<HostScanner>(
+      network_state_handler_, tether_host_fetcher_.get(),
+      ble_connection_manager_.get(), host_scan_device_prioritizer_.get(),
+      tether_host_response_recorder_.get(), notification_presenter_,
+      device_id_tether_network_guid_map_.get(), master_host_scan_cache_.get(),
+      clock_.get());
+  host_scan_scheduler_ = base::MakeUnique<HostScanScheduler>(
+      network_state_handler_, host_scanner_.get());
+  host_connection_metrics_logger_ =
+      base::MakeUnique<HostConnectionMetricsLogger>();
+  tether_connector_ = base::MakeUnique<TetherConnector>(
+      network_state_handler_, wifi_hotspot_connector_.get(), active_host_.get(),
+      tether_host_fetcher_.get(), ble_connection_manager_.get(),
+      tether_host_response_recorder_.get(),
+      device_id_tether_network_guid_map_.get(), master_host_scan_cache_.get(),
+      notification_presenter_, host_connection_metrics_logger_.get());
+  network_configuration_remover_ =
+      base::MakeUnique<NetworkConfigurationRemover>(
+          network_state_handler_, managed_network_configuration_handler_);
+  tether_disconnector_ = base::MakeUnique<TetherDisconnectorImpl>(
+      network_connection_handler_, network_state_handler_, active_host_.get(),
+      ble_connection_manager_.get(), network_configuration_remover_.get(),
+      tether_connector_.get(), device_id_tether_network_guid_map_.get(),
+      tether_host_fetcher_.get(), pref_service_);
+  tether_network_disconnection_handler_ =
+      base::MakeUnique<TetherNetworkDisconnectionHandler>(
+          active_host_.get(), network_state_handler_,
+          network_configuration_remover_.get());
+  network_connection_handler_tether_delegate_ =
+      base::MakeUnique<NetworkConnectionHandlerTetherDelegate>(
+          network_connection_handler_, tether_connector_.get(),
+          tether_disconnector_.get());
+
+  crash_recovery_manager_ = base::MakeUnique<CrashRecoveryManager>(
+      network_state_handler_, active_host_.get(),
+      master_host_scan_cache_.get());
+  crash_recovery_manager_->RestorePreCrashStateIfNecessary(
+      base::Bind(&InitializerImpl::OnPreCrashStateRestored,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void InitializerImpl::OnPreCrashStateRestored() {
+  // |crash_recovery_manager_| is no longer needed since it has completed.
+  crash_recovery_manager_.reset();
+
+  // Start a scan now that the Tether module has started up.
+  host_scan_scheduler_->ScheduleScan();
+}
+
+void InitializerImpl::StartAsynchronousShutdown() {
+  DCHECK(status() == Initializer::Status::SHUTTING_DOWN);
+  DCHECK(disconnect_tethering_request_sender_->HasPendingRequests());
+
+  // Currently, the only task which needs to be performed asynchronously during
+  // shutdown is the DisconnectTetheringRequestSender. Observer this class so
+  // that when it finishes sending messages, Initializer can shut down.
+  disconnect_tethering_request_sender_->AddObserver(this);
+
+  // All objects which are not dependencies of
+  // |disconnect_tethering_request_sender_| are
+  crash_recovery_manager_.reset();
+  network_connection_handler_tether_delegate_.reset();
+  tether_network_disconnection_handler_.reset();
+  tether_disconnector_.reset();
+  network_configuration_remover_.reset();
+  tether_connector_.reset();
+  host_connection_metrics_logger_.reset();
+  host_scan_scheduler_.reset();
+  host_scanner_.reset();
+  clock_.reset();
+  keep_alive_scheduler_.reset();
+  notification_remover_.reset();
+  master_host_scan_cache_.reset();
+  network_host_scan_cache_.reset();
+  persistent_host_scan_cache_.reset();
+  active_host_network_state_updater_.reset();
+  active_host_.reset();
+  wifi_hotspot_connector_.reset();
+  host_scan_device_prioritizer_.reset();
+  device_id_tether_network_guid_map_.reset();
+  tether_host_response_recorder_.reset();
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/initializer_impl.h b/chromeos/components/tether/initializer_impl.h
new file mode 100644
index 0000000..82235f1b
--- /dev/null
+++ b/chromeos/components/tether/initializer_impl.h
@@ -0,0 +1,188 @@
+// 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 CHROMEOS_COMPONENTS_TETHER_INITIALIZER_IMPL_H_
+#define CHROMEOS_COMPONENTS_TETHER_INITIALIZER_IMPL_H_
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/default_clock.h"
+#include "chromeos/components/tether/disconnect_tethering_request_sender.h"
+#include "chromeos/components/tether/initializer.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+
+class PrefService;
+
+namespace cryptauth {
+class CryptAuthService;
+class LocalDeviceDataProvider;
+class RemoteBeaconSeedFetcher;
+}  // namespace cryptauth
+
+namespace chromeos {
+
+class ManagedNetworkConfigurationHandler;
+class NetworkConnect;
+class NetworkConnectionHandler;
+class NetworkStateHandler;
+
+namespace tether {
+
+class ActiveHost;
+class ActiveHostNetworkStateUpdater;
+class BleConnectionManager;
+class CrashRecoveryManager;
+class NetworkConnectionHandlerTetherDelegate;
+class DeviceIdTetherNetworkGuidMap;
+class HostScanner;
+class HostScanScheduler;
+class HostScanDevicePrioritizerImpl;
+class KeepAliveScheduler;
+class HostConnectionMetricsLogger;
+class MasterHostScanCache;
+class NetworkConfigurationRemover;
+class NetworkHostScanCache;
+class NotificationPresenter;
+class NotificationRemover;
+class PersistentHostScanCache;
+class TetherConnector;
+class TetherDisconnector;
+class TetherHostFetcher;
+class TetherHostResponseRecorder;
+class TetherNetworkDisconnectionHandler;
+class WifiHotspotConnector;
+
+// Initializes the Tether Chrome OS component.
+class InitializerImpl : public Initializer,
+                        public OAuth2TokenService::Observer,
+                        public DisconnectTetheringRequestSender::Observer {
+ public:
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  class Factory {
+   public:
+    static std::unique_ptr<Initializer> NewInstance(
+        cryptauth::CryptAuthService* cryptauth_service,
+        NotificationPresenter* notification_presenter,
+        PrefService* pref_service,
+        ProfileOAuth2TokenService* token_service,
+        NetworkStateHandler* network_state_handler,
+        ManagedNetworkConfigurationHandler*
+            managed_network_configuration_handler,
+        NetworkConnect* network_connect,
+        NetworkConnectionHandler* network_connection_handler,
+        scoped_refptr<device::BluetoothAdapter> adapter);
+
+    static void SetInstanceForTesting(Factory* factory);
+
+   protected:
+    virtual std::unique_ptr<Initializer> BuildInstance(
+        cryptauth::CryptAuthService* cryptauth_service,
+        NotificationPresenter* notification_presenter,
+        PrefService* pref_service,
+        ProfileOAuth2TokenService* token_service,
+        NetworkStateHandler* network_state_handler,
+        ManagedNetworkConfigurationHandler*
+            managed_network_configuration_handler,
+        NetworkConnect* network_connect,
+        NetworkConnectionHandler* network_connection_handler,
+        scoped_refptr<device::BluetoothAdapter> adapter);
+
+   private:
+    static Factory* factory_instance_;
+  };
+
+  InitializerImpl(
+      cryptauth::CryptAuthService* cryptauth_service,
+      NotificationPresenter* notification_presenter,
+      PrefService* pref_service,
+      ProfileOAuth2TokenService* token_service,
+      NetworkStateHandler* network_state_handler,
+      ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
+      NetworkConnect* network_connect,
+      NetworkConnectionHandler* network_connection_handler,
+      scoped_refptr<device::BluetoothAdapter> adapter);
+  ~InitializerImpl() override;
+
+ protected:
+  // Initializer:
+  void RequestShutdown() override;
+
+  // OAuth2TokenService::Observer:
+  void OnRefreshTokensLoaded() override;
+
+  // DisconnectTetheringRequestSender::Observer:
+  void OnPendingDisconnectRequestsComplete() override;
+
+ private:
+  friend class InitializerImplTest;
+
+  void CreateComponent();
+  void OnPreCrashStateRestored();
+  void StartAsynchronousShutdown();
+
+  cryptauth::CryptAuthService* cryptauth_service_;
+  NotificationPresenter* notification_presenter_;
+  PrefService* pref_service_;
+  ProfileOAuth2TokenService* token_service_;
+  NetworkStateHandler* network_state_handler_;
+  ManagedNetworkConfigurationHandler* managed_network_configuration_handler_;
+  NetworkConnect* network_connect_;
+  NetworkConnectionHandler* network_connection_handler_;
+  scoped_refptr<device::BluetoothAdapter> adapter_;
+
+  // Declare new objects in the order that they will be created during
+  // initialization to ensure that they are destroyed in the correct order. This
+  // order will be enforced by InitializerTest.TestCreateAndDestroy.
+  std::unique_ptr<TetherHostFetcher> tether_host_fetcher_;
+  std::unique_ptr<cryptauth::LocalDeviceDataProvider>
+      local_device_data_provider_;
+  std::unique_ptr<cryptauth::RemoteBeaconSeedFetcher>
+      remote_beacon_seed_fetcher_;
+  std::unique_ptr<BleConnectionManager> ble_connection_manager_;
+  std::unique_ptr<DisconnectTetheringRequestSender>
+      disconnect_tethering_request_sender_;
+  std::unique_ptr<TetherHostResponseRecorder> tether_host_response_recorder_;
+  std::unique_ptr<DeviceIdTetherNetworkGuidMap>
+      device_id_tether_network_guid_map_;
+  std::unique_ptr<HostScanDevicePrioritizerImpl> host_scan_device_prioritizer_;
+  std::unique_ptr<WifiHotspotConnector> wifi_hotspot_connector_;
+  std::unique_ptr<ActiveHost> active_host_;
+  std::unique_ptr<ActiveHostNetworkStateUpdater>
+      active_host_network_state_updater_;
+  std::unique_ptr<PersistentHostScanCache> persistent_host_scan_cache_;
+  std::unique_ptr<NetworkHostScanCache> network_host_scan_cache_;
+  std::unique_ptr<MasterHostScanCache> master_host_scan_cache_;
+  std::unique_ptr<NotificationRemover> notification_remover_;
+  std::unique_ptr<KeepAliveScheduler> keep_alive_scheduler_;
+  std::unique_ptr<base::DefaultClock> clock_;
+  std::unique_ptr<HostScanner> host_scanner_;
+  std::unique_ptr<HostScanScheduler> host_scan_scheduler_;
+  std::unique_ptr<HostConnectionMetricsLogger> host_connection_metrics_logger_;
+  std::unique_ptr<TetherConnector> tether_connector_;
+  std::unique_ptr<NetworkConfigurationRemover> network_configuration_remover_;
+  std::unique_ptr<TetherDisconnector> tether_disconnector_;
+  std::unique_ptr<TetherNetworkDisconnectionHandler>
+      tether_network_disconnection_handler_;
+  std::unique_ptr<NetworkConnectionHandlerTetherDelegate>
+      network_connection_handler_tether_delegate_;
+  std::unique_ptr<CrashRecoveryManager> crash_recovery_manager_;
+
+  base::WeakPtrFactory<InitializerImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(InitializerImpl);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_INITIALIZER_IMPL_H_
diff --git a/chromeos/components/tether/initializer_unittest.cc b/chromeos/components/tether/initializer_impl_unittest.cc
similarity index 97%
rename from chromeos/components/tether/initializer_unittest.cc
rename to chromeos/components/tether/initializer_impl_unittest.cc
index ae8266bb6..c683af9 100644
--- a/chromeos/components/tether/initializer_unittest.cc
+++ b/chromeos/components/tether/initializer_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/tether/initializer.h"
+#include "chromeos/components/tether/initializer_impl.h"
 
 #include <memory>
 
@@ -129,7 +129,7 @@
         NetworkStateHandler::TECHNOLOGY_ENABLED);
 
     test_pref_service_ = base::MakeUnique<TestingPrefServiceSimple>();
-    Initializer::RegisterProfilePrefs(test_pref_service_->registry());
+    InitializerImpl::RegisterProfilePrefs(test_pref_service_->registry());
   }
 
   void TearDown() override {
@@ -148,7 +148,7 @@
       NetworkConnect* network_connect,
       NetworkConnectionHandler* network_connection_handler,
       scoped_refptr<device::BluetoothAdapter> adapter) {
-    Initializer* initializer = new Initializer(
+    Initializer* initializer = new InitializerImpl(
         cryptauth_service, notification_presenter, pref_service, token_service,
         network_state_handler, managed_network_configuration_handler,
         network_connect, network_connection_handler, adapter);
diff --git a/components/crash/content/browser/child_process_crash_observer_android.cc b/components/crash/content/browser/child_process_crash_observer_android.cc
index b59a0dd..ca3fb794 100644
--- a/components/crash/content/browser/child_process_crash_observer_android.cc
+++ b/components/crash/content/browser/child_process_crash_observer_android.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
+#include "base/task_runner_util.h"
 #include "base/task_scheduler/post_task.h"
 #include "components/crash/content/app/breakpad_linux.h"
 #include "components/crash/content/browser/crash_dump_manager_android.h"
@@ -14,8 +15,18 @@
 
 ChildProcessCrashObserver::ChildProcessCrashObserver(
     const base::FilePath crash_dump_dir,
-    int descriptor_id)
-    : crash_dump_dir_(crash_dump_dir), descriptor_id_(descriptor_id) {}
+    int descriptor_id,
+    const base::Closure& increase_crash_cb)
+    : crash_dump_dir_(crash_dump_dir),
+      descriptor_id_(descriptor_id),
+      increase_crash_cb_(base::Bind(
+          [](base::Closure cb, bool run_cb) {
+            if (run_cb)
+              cb.Run();
+          },
+          increase_crash_cb)),
+      background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BACKGROUND})) {}
 
 ChildProcessCrashObserver::~ChildProcessCrashObserver() {}
 
@@ -41,12 +52,14 @@
   // This might be called twice for a given child process, with a
   // NOTIFICATION_RENDERER_PROCESS_TERMINATED and then with
   // NOTIFICATION_RENDERER_PROCESS_CLOSED.
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+
+  base::PostTaskAndReplyWithResult(
+      background_task_runner_.get(), FROM_HERE,
       base::Bind(&CrashDumpManager::ProcessMinidumpFileFromChild,
                  base::Unretained(CrashDumpManager::GetInstance()),
                  crash_dump_dir_, child_process_id, process_type,
-                 termination_status, app_state));
+                 termination_status, app_state),
+      increase_crash_cb_);
 }
 
 }  // namespace breakpad
diff --git a/components/crash/content/browser/child_process_crash_observer_android.h b/components/crash/content/browser/child_process_crash_observer_android.h
index fad32f2..76a4fdb2 100644
--- a/components/crash/content/browser/child_process_crash_observer_android.h
+++ b/components/crash/content/browser/child_process_crash_observer_android.h
@@ -6,14 +6,19 @@
 #define COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_PROCESS_CRASH_OBSERVER_ANDROID_H_
 
 #include "base/files/file_path.h"
+#include "base/sequenced_task_runner.h"
 #include "components/crash/content/browser/crash_dump_observer_android.h"
 
 namespace breakpad {
 
 class ChildProcessCrashObserver : public breakpad::CrashDumpObserver::Client {
  public:
+  // |increase_crash_cb is| the callback to run after processing minidump file.
+  // For now this callback is used to increase render crash counter based on
+  // processing minidump result.
   ChildProcessCrashObserver(const base::FilePath crash_dump_dir,
-                            int descriptor_id);
+                            int descriptor_id,
+                            const base::Closure& increase_crash_cb);
   ~ChildProcessCrashObserver() override;
 
   // breakpad::CrashDumpObserver::Client implementation:
@@ -31,6 +36,10 @@
   // descriptor mappings passed to the child process.
   int descriptor_id_;
 
+  base::Callback<void(bool)> increase_crash_cb_;
+
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(ChildProcessCrashObserver);
 };
 
diff --git a/components/crash/content/browser/crash_dump_manager_android.cc b/components/crash/content/browser/crash_dump_manager_android.cc
index d7cadd74..6d204696 100644
--- a/components/crash/content/browser/crash_dump_manager_android.cc
+++ b/components/crash/content/browser/crash_dump_manager_android.cc
@@ -60,18 +60,19 @@
   return base::ScopedFD(minidump_file.TakePlatformFile());
 }
 
-void CrashDumpManager::ProcessMinidumpFileFromChild(
+bool CrashDumpManager::ProcessMinidumpFileFromChild(
     base::FilePath crash_dump_dir,
     base::ProcessHandle pid,
     content::ProcessType process_type,
     base::TerminationStatus termination_status,
     base::android::ApplicationState app_state) {
   base::ThreadRestrictions::AssertIOAllowed();
+  bool increase_crash_count = false;
   base::FilePath minidump_path;
   // If the minidump for a given child process has already been
   // processed, then there is no more work to do.
   if (!GetMinidumpPath(pid, &minidump_path))
-    return;
+    return increase_crash_count;
 
   int64_t file_size = 0;
   int r = base::GetFileSize(minidump_path, &file_size);
@@ -108,6 +109,9 @@
     }
     if (process_type == content::PROCESS_TYPE_RENDERER) {
       if (termination_status == base::TERMINATION_STATUS_OOM_PROTECTED) {
+        // There is a delay for OOM flag to be removed when app goes to
+        // background, so we can't just check for OOM_PROTECTED flag.
+        increase_crash_count = is_running || is_paused;
         UMA_HISTOGRAM_ENUMERATION("Tab.RendererDetailedExitStatus",
                                   exit_status,
                                   ExitStatus::MINIDUMP_STATUS_COUNT);
@@ -128,14 +132,14 @@
     r = base::DeleteFile(minidump_path, false);
     DCHECK(r) << "Failed to delete temporary minidump file "
               << minidump_path.value();
-    return;
+    return increase_crash_count;
   }
 
   // We are dealing with a valid minidump. Copy it to the crash report
   // directory from where Java code will upload it later on.
   if (crash_dump_dir.empty()) {
     NOTREACHED() << "Failed to retrieve the crash dump directory.";
-    return;
+    return increase_crash_count;
   }
   const uint64_t rand = base::RandUint64();
   const std::string filename =
@@ -147,7 +151,7 @@
     LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
                << " to " << dest_path.value();
     base::DeleteFile(minidump_path, false);
-    return;
+    return increase_crash_count;
   }
   VLOG(1) << "Crash minidump successfully generated: " << dest_path.value();
 
@@ -158,6 +162,7 @@
   base::android::ScopedJavaLocalRef<jstring> j_dest_path =
       base::android::ConvertUTF8ToJavaString(env, dest_path.value());
   Java_CrashDumpManager_tryToUploadMinidump(env, j_dest_path);
+  return increase_crash_count;
 }
 
 void CrashDumpManager::SetMinidumpPath(int child_process_id,
diff --git a/components/crash/content/browser/crash_dump_manager_android.h b/components/crash/content/browser/crash_dump_manager_android.h
index 6aa5885..5c54c38 100644
--- a/components/crash/content/browser/crash_dump_manager_android.h
+++ b/components/crash/content/browser/crash_dump_manager_android.h
@@ -31,7 +31,8 @@
  public:
   static CrashDumpManager* GetInstance();
 
-  void ProcessMinidumpFileFromChild(base::FilePath crash_dump_dir,
+  // Returns the condition whether we should write the crash to stability proto.
+  bool ProcessMinidumpFileFromChild(base::FilePath crash_dump_dir,
                                     base::ProcessHandle pid,
                                     content::ProcessType process_type,
                                     base::TerminationStatus termination_status,
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 8dd663c..faf3387 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -109,6 +109,7 @@
     "//components/prefs",
     "//components/variations",
     "//components/version_info:version_info",
+    "//extensions/features:features",
     "//third_party/zlib/google:compression_utils",
   ]
 
@@ -409,6 +410,7 @@
     "//components/metrics/public/cpp:call_stack_unit_tests",
     "//components/prefs:test_support",
     "//components/variations",
+    "//extensions/features:features",
     "//mojo/public/cpp/bindings",
     "//net:test_support",
     "//services/service_manager/public/cpp",
diff --git a/components/metrics/DEPS b/components/metrics/DEPS
index bd7bd415..711120e 100644
--- a/components/metrics/DEPS
+++ b/components/metrics/DEPS
@@ -10,6 +10,7 @@
   "+components/variations",
   "+components/version_info",
   "+content/public/test",
+  "+extensions/features",
   "+mojo/public/cpp",
   "+services/service_manager/public/cpp",
   "+third_party/zlib/google",
diff --git a/components/metrics/stability_metrics_helper.cc b/components/metrics/stability_metrics_helper.cc
index 1ba27b9..cf54081 100644
--- a/components/metrics/stability_metrics_helper.cc
+++ b/components/metrics/stability_metrics_helper.cc
@@ -13,10 +13,12 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/user_metrics.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/proto/system_profile.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "extensions/features/features.h"
 
 #if defined(OS_WIN)
 #include <windows.h>  // Needed for STATUS_* codes
@@ -164,6 +166,14 @@
   registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0);
 }
 
+// static
+void StabilityMetricsHelper::IncreaseRendererCrashCount(
+    PrefService* local_state) {
+  // It doesn't use IncrementPrefValue() because the function is static.
+  int value = local_state->GetInteger(prefs::kStabilityRendererCrashCount);
+  local_state->SetInteger(prefs::kStabilityRendererCrashCount, value + 1);
+}
+
 void StabilityMetricsHelper::BrowserChildProcessCrashed() {
   IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
 }
@@ -189,6 +199,9 @@
     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
     case base::TERMINATION_STATUS_OOM:
       if (was_extension_process) {
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
+        NOTREACHED();
+#endif
         IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount);
 
         UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension",
diff --git a/components/metrics/stability_metrics_helper.h b/components/metrics/stability_metrics_helper.h
index bc9aaed0..a67dad2 100644
--- a/components/metrics/stability_metrics_helper.h
+++ b/components/metrics/stability_metrics_helper.h
@@ -48,11 +48,14 @@
   // Registers local state prefs used by this class.
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
+  // Increments the RendererCrash pref.
+  static void IncreaseRendererCrashCount(PrefService* local_state);
+
  private:
-  // Increment an Integer pref value specified by |path|.
+  // Increments an Integer pref value specified by |path|.
   void IncrementPrefValue(const char* path);
 
-  // Increment a 64-bit Integer pref value specified by |path|.
+  // Increments a 64-bit Integer pref value specified by |path|.
   void IncrementLongPrefsValue(const char* path);
 
   PrefService* local_state_;
diff --git a/components/metrics/stability_metrics_helper_unittest.cc b/components/metrics/stability_metrics_helper_unittest.cc
index 018906d..edc9aaa 100644
--- a/components/metrics/stability_metrics_helper_unittest.cc
+++ b/components/metrics/stability_metrics_helper_unittest.cc
@@ -6,10 +6,12 @@
 
 #include "base/macros.h"
 #include "base/test/histogram_tester.h"
+#include "build/build_config.h"
 #include "components/metrics/proto/system_profile.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
+#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace metrics {
@@ -90,7 +92,29 @@
   EXPECT_EQ(1, system_profile.stability().renderer_failed_launch_count());
   EXPECT_EQ(0, system_profile.stability().extension_renderer_crash_count());
 
-  helper.ClearSavedStabilityMetrics();
+  histogram_tester.ExpectUniqueSample("CrashExitCodes.Renderer", 1, 3);
+  histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildCrashes",
+                                     RENDERER_TYPE_RENDERER, 3);
+
+  // One launch failure each.
+  histogram_tester.ExpectBucketCount(
+      "BrowserRenderProcessHost.ChildLaunchFailures", RENDERER_TYPE_RENDERER,
+      1);
+
+  // TERMINATION_STATUS_PROCESS_WAS_KILLED for a renderer.
+  histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildKills",
+                                     RENDERER_TYPE_RENDERER, 1);
+  histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildKills",
+                                     RENDERER_TYPE_EXTENSION, 0);
+  histogram_tester.ExpectBucketCount(
+      "BrowserRenderProcessHost.ChildLaunchFailureCodes", 1, 1);
+}
+
+// Note: ENABLE_EXTENSIONS is set to false in Android
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+TEST_F(StabilityMetricsHelperTest, LogRendererCrashEnableExtensions) {
+  StabilityMetricsHelper helper(prefs());
+  base::HistogramTester histogram_tester;
 
   // Crash and abnormal termination should increment extension crash count.
   helper.LogRendererCrash(true, base::TERMINATION_STATUS_PROCESS_CRASHED, 1);
@@ -101,7 +125,7 @@
   // Failed launch increments extension failed launch count.
   helper.LogRendererCrash(true, base::TERMINATION_STATUS_LAUNCH_FAILED, 1);
 
-  system_profile.Clear();
+  metrics::SystemProfileProto system_profile;
   helper.ProvideStabilityMetrics(&system_profile);
 
   EXPECT_EQ(0, system_profile.stability().renderer_crash_count());
@@ -109,32 +133,15 @@
   EXPECT_EQ(
       1, system_profile.stability().extension_renderer_failed_launch_count());
 
-  // TERMINATION_STATUS_PROCESS_CRASHED, TERMINATION_STATUS_ABNORMAL_TERMINATION
-  // and TERMINATION_STATUS_OOM = 3.
-  histogram_tester.ExpectUniqueSample("CrashExitCodes.Renderer", 1, 3);
-  histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildCrashes",
-                                     RENDERER_TYPE_RENDERER, 3);
-
-  // TERMINATION_STATUS_PROCESS_CRASHED and TERMINATION_STATUS_OOM = 2.
+  histogram_tester.ExpectBucketCount(
+      "BrowserRenderProcessHost.ChildLaunchFailureCodes", 1, 1);
   histogram_tester.ExpectUniqueSample("CrashExitCodes.Extension", 1, 2);
   histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildCrashes",
                                      RENDERER_TYPE_EXTENSION, 2);
-
-  // One launch failure each.
-  histogram_tester.ExpectBucketCount(
-      "BrowserRenderProcessHost.ChildLaunchFailures", RENDERER_TYPE_RENDERER,
-      1);
   histogram_tester.ExpectBucketCount(
       "BrowserRenderProcessHost.ChildLaunchFailures", RENDERER_TYPE_EXTENSION,
       1);
-  histogram_tester.ExpectBucketCount(
-      "BrowserRenderProcessHost.ChildLaunchFailureCodes", 1, 2);
-
-  // TERMINATION_STATUS_PROCESS_WAS_KILLED for a renderer.
-  histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildKills",
-                                     RENDERER_TYPE_RENDERER, 1);
-  histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildKills",
-                                     RENDERER_TYPE_EXTENSION, 0);
 }
+#endif
 
 }  // namespace metrics
diff --git a/components/url_formatter/url_formatter.cc b/components/url_formatter/url_formatter.cc
index abfb716f..bad3a45a 100644
--- a/components/url_formatter/url_formatter.cc
+++ b/components/url_formatter/url_formatter.cc
@@ -68,7 +68,7 @@
     std::string domain_and_registry =
         net::registry_controlled_domains::GetDomainAndRegistry(
             component_text,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+            net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
 
     base::OffsetAdjuster::Adjustments trivial_subdomains_adjustments;
     base::StringTokenizer tokenizer(
diff --git a/components/url_formatter/url_formatter_unittest.cc b/components/url_formatter/url_formatter_unittest.cc
index 6cc9b19..f08a17b3 100644
--- a/components/url_formatter/url_formatter_unittest.cc
+++ b/components/url_formatter/url_formatter_unittest.cc
@@ -950,6 +950,9 @@
        "http://en.m.wikipedia.org/",
        kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
        L"http://en.wikipedia.org/", 7},
+      {"omit trivial subdomains - exclude private registries",
+       "http://www.blogspot.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+       net::UnescapeRule::NORMAL, L"http://blogspot.com/", 7},
       {"omit trivial subdomains - don't do blind substring matches for www",
        "http://wwww.google.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
        net::UnescapeRule::NORMAL, L"http://wwww.google.com/", 7},
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index ebf10e6..d2f4f26 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -479,7 +479,7 @@
 
 }  // namespace
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 namespace internal {
 
 // Forwards GPUInfo updates to ui::XVisualManager
@@ -883,7 +883,7 @@
 
   GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
   // GpuDataManagerVisualProxy() just adds itself as an observer of
   // |gpu_data_manager|, which is safe to do before Initialize().
   gpu_data_manager_visual_proxy_.reset(
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 1afdc00..d341e72d 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -105,7 +105,7 @@
 class ScreenOrientationDelegate;
 #endif
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 namespace internal {
 class GpuDataManagerVisualProxy;
 }
@@ -303,7 +303,7 @@
   // Torn down in ShutdownThreadsAndCleanUp.
   std::unique_ptr<base::MemoryPressureMonitor> memory_pressure_monitor_;
   std::unique_ptr<SwapMetricsDriver> swap_metrics_driver_;
-#if defined(USE_X11) && !(OS_CHROMEOS)
+#if defined(USE_X11)
   std::unique_ptr<internal::GpuDataManagerVisualProxy>
       gpu_data_manager_visual_proxy_;
 #endif
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 0abd4a0f..4722f36 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -61,7 +61,7 @@
 #include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "url/origin.h"
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 #include "base/nix/xdg_util.h"
 #endif
 
@@ -219,7 +219,7 @@
   }
 };
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 base::FilePath GetTemporaryDownloadDirectory() {
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   return base::nix::GetXDGDirectory(env.get(), "XDG_DATA_HOME", ".local/share");
@@ -410,7 +410,7 @@
   }
 
   base::FilePath default_download_directory;
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
   // TODO(thomasanderson): Remove this when all Linux distros with
   // versions of GTK lower than 3.14.7 are no longer supported.  This
   // should happen when support for Ubuntu Trusty and Debian Jessie
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index ab96db1..0aedd18 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -547,7 +547,7 @@
   EXPECT_CALL(GetMockDownloadManagerDelegate(), GetNextId(_))
       .WillOnce(RunCallback<0>(local_id));
 
-#if !defined(USE_X11) || defined(OS_CHROMEOS)
+#if !defined(USE_X11)
   // Doing nothing will set the default download directory to null.
   EXPECT_CALL(GetMockDownloadManagerDelegate(), GetSaveDir(_, _, _, _));
 #endif
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index ee72196..279dcd5 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/optional.h"
 #include "base/strings/string_util.h"
 #include "content/browser/appcache/appcache_navigation_handle.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
@@ -424,7 +425,7 @@
     // Create a navigation handle so that the correct error code can be set on
     // it by OnRequestFailed().
     CreateNavigationHandle();
-    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT);
+    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT, base::nullopt, false);
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -438,7 +439,7 @@
     // Create a navigation handle so that the correct error code can be set on
     // it by OnRequestFailed().
     CreateNavigationHandle();
-    OnRequestFailed(false, net::ERR_ABORTED);
+    OnRequestFailed(false, net::ERR_ABORTED, base::nullopt, false);
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -597,7 +598,7 @@
   // otherwise block.
   if (CheckContentSecurityPolicyFrameSrc(true /* is redirect */) ==
       CONTENT_SECURITY_POLICY_CHECK_FAILED) {
-    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT);
+    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT, base::nullopt, false);
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -608,7 +609,7 @@
           CredentialedSubresourceCheckResult::BLOCK_REQUEST ||
       CheckLegacyProtocolInSubresource() ==
           LegacyProtocolInSubresourceCheckResult::BLOCK_REQUEST) {
-    OnRequestFailed(false, net::ERR_ABORTED);
+    OnRequestFailed(false, net::ERR_ABORTED, base::nullopt, false);
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -740,9 +741,15 @@
                  base::Unretained(this)));
 }
 
-void NavigationRequest::OnRequestFailed(bool has_stale_copy_in_cache,
-                                        int net_error) {
+// TODO(crbug.com/751941): Pass certificate_error_info to navigation throttles.
+void NavigationRequest::OnRequestFailed(
+    bool has_stale_copy_in_cache,
+    int net_error,
+    const base::Optional<net::SSLInfo>& ssl_info,
+    bool should_ssl_errors_be_fatal) {
   DCHECK(state_ == STARTED || state_ == RESPONSE_STARTED);
+  // TODO(https://crbug.com/757633): Check that ssl_info.has_value() if
+  // net_error is a certificate error.
   TRACE_EVENT_ASYNC_STEP_INTO1("navigation", "NavigationRequest", this,
                                "OnRequestFailed", "error", net_error);
   state_ = FAILED;
@@ -838,10 +845,10 @@
     // is no onbeforeunload handler or if a NavigationThrottle cancelled it,
     // then this could cause reentrancy into NavigationController. So use a
     // PostTask to avoid that.
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&NavigationRequest::OnRequestFailed,
-                       weak_factory_.GetWeakPtr(), false, error_code));
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                            base::BindOnce(&NavigationRequest::OnRequestFailed,
+                                           weak_factory_.GetWeakPtr(), false,
+                                           error_code, base::nullopt, false));
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -936,7 +943,7 @@
   if (result == NavigationThrottle::CANCEL_AND_IGNORE ||
       result == NavigationThrottle::CANCEL) {
     // TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE if needed.
-    OnRequestFailed(false, net::ERR_ABORTED);
+    OnRequestFailed(false, net::ERR_ABORTED, base::nullopt, false);
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -945,7 +952,7 @@
 
   if (result == NavigationThrottle::BLOCK_REQUEST ||
       result == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE) {
-    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT);
+    OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT, base::nullopt, false);
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
     return;
@@ -969,7 +976,7 @@
   if (result == NavigationThrottle::CANCEL_AND_IGNORE ||
       result == NavigationThrottle::CANCEL || !response_should_be_rendered_) {
     // TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE.
-    OnRequestFailed(false, net::ERR_ABORTED);
+    OnRequestFailed(false, net::ERR_ABORTED, base::nullopt, false);
 
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
@@ -977,7 +984,7 @@
   }
 
   if (result == NavigationThrottle::BLOCK_RESPONSE) {
-    OnRequestFailed(false, net::ERR_BLOCKED_BY_RESPONSE);
+    OnRequestFailed(false, net::ERR_BLOCKED_BY_RESPONSE, base::nullopt, false);
     // DO NOT ADD CODE after this. The previous call to OnRequestFailed has
     // destroyed the NavigationRequest.
     return;
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 53971d2..82cbaf5 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -209,7 +209,10 @@
                          bool is_stream,
                          mojom::URLLoaderFactoryPtrInfo
                              subresource_url_loader_factory_info) override;
-  void OnRequestFailed(bool has_stale_copy_in_cache, int net_error) override;
+  void OnRequestFailed(bool has_stale_copy_in_cache,
+                       int net_error,
+                       const base::Optional<net::SSLInfo>& ssl_info,
+                       bool should_ssl_errors_be_fatal) override;
   void OnRequestStarted(base::TimeTicks timestamp) override;
 
   // Called when the NavigationThrottles have been checked by the
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index cdb4a406..2d46fc8d 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -249,7 +249,7 @@
   info->Set("diagnostics", std::move(dx_info));
 #endif
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
   basic_info->Append(NewDescriptionValuePair(
       "System visual ID", base::Uint64ToString(gpu_info.system_visual)));
   basic_info->Append(NewDescriptionValuePair(
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index e1a3860..40ffc80 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -91,7 +91,7 @@
 #include "ui/ozone/public/ozone_switches.h"
 #endif
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 #include "ui/gfx/x/x11_switches.h"  // nogncheck
 #endif
 
@@ -163,7 +163,7 @@
 #if defined(USE_OZONE)
     switches::kOzonePlatform,
 #endif
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
     switches::kX11Display,
 #endif
     switches::kGpuTestingGLVendor,
diff --git a/content/browser/loader/navigation_resource_handler.cc b/content/browser/loader/navigation_resource_handler.cc
index c6f84ef..4a522b1 100644
--- a/content/browser/loader/navigation_resource_handler.cc
+++ b/content/browser/loader/navigation_resource_handler.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "content/browser/loader/navigation_url_loader_impl_core.h"
 #include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_loader.h"
@@ -21,7 +22,23 @@
 #include "content/public/browser/stream_handle.h"
 #include "content/public/common/resource_response.h"
 #include "net/base/net_errors.h"
+#include "net/http/transport_security_state.h"
+#include "net/ssl/ssl_info.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "url/gurl.h"
+
+namespace {
+
+// TODO(crbug.com/757633): Attach this information to net::SSLInfo instead of
+// calculating it here.
+bool ShouldSSLErrorsBeFatal(net::URLRequest* request) {
+  net::TransportSecurityState* state =
+      request->context()->transport_security_state();
+  return state->ShouldSSLErrorsBeFatal(request->url().host());
+}
+
+}  // namespace
 
 namespace content {
 
@@ -47,7 +64,7 @@
 
 NavigationResourceHandler::~NavigationResourceHandler() {
   if (core_) {
-    core_->NotifyRequestFailed(false, net::ERR_ABORTED);
+    core_->NotifyRequestFailed(false, net::ERR_ABORTED, base::nullopt, false);
     DetachFromCore();
   }
 }
@@ -144,9 +161,16 @@
     const net::URLRequestStatus& status,
     std::unique_ptr<ResourceController> controller) {
   if (core_) {
-    DCHECK_NE(net::OK, status.error());
-    core_->NotifyRequestFailed(request()->response_info().was_cached,
-                               status.error());
+    int net_error = status.error();
+    DCHECK_NE(net::OK, net_error);
+
+    base::Optional<net::SSLInfo> ssl_info;
+    if (net::IsCertStatusError(request()->ssl_info().cert_status)) {
+      ssl_info = request()->ssl_info();
+    }
+
+    core_->NotifyRequestFailed(request()->response_info().was_cached, net_error,
+                               ssl_info, ShouldSSLErrorsBeFatal(request()));
     DetachFromCore();
   }
   next_handler_->OnResponseCompleted(status, std::move(controller));
diff --git a/content/browser/loader/navigation_url_loader_delegate.h b/content/browser/loader/navigation_url_loader_delegate.h
index 34723cf..2dbb64f 100644
--- a/content/browser/loader/navigation_url_loader_delegate.h
+++ b/content/browser/loader/navigation_url_loader_delegate.h
@@ -15,6 +15,7 @@
 
 namespace net {
 struct RedirectInfo;
+class SSLInfo;
 }
 
 namespace content {
@@ -55,8 +56,18 @@
 
   // Called if the request fails before receving a response. |net_error| is a
   // network error code for the failure. |has_stale_copy_in_cache| is true if
-  // there is a stale copy of the unreachable page in cache.
-  virtual void OnRequestFailed(bool has_stale_copy_in_cache, int net_error) = 0;
+  // there is a stale copy of the unreachable page in cache. |ssl_info| is the
+  // SSL info for the request. |should_ssl_errors_be_fatal| indicates
+  // whether SSL errors for the request should be fatal.
+  // If |net_error| is a certificate error, the caller should pass a value for
+  // |ssl_info|. If |net_error| is not a certificate error, |ssl_info| and
+  // |fatal_cert_error| are ignored.
+  // TODO(https://crbug.com/757633): Change "should pass a value for |ssl_info|"
+  // to "must pass..."
+  virtual void OnRequestFailed(bool has_stale_copy_in_cache,
+                               int net_error,
+                               const base::Optional<net::SSLInfo>& ssl_info,
+                               bool should_ssl_errors_be_fatal) = 0;
 
   // Called after the network request has begun on the IO thread at time
   // |timestamp|. This is just a thread hop but is used to compare timing
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 556294f..6371909 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/location.h"
+#include "base/optional.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/appcache/appcache_navigation_handle.h"
 #include "content/browser/appcache/appcache_navigation_handle_core.h"
@@ -107,12 +108,15 @@
       ssl_status, std::move(navigation_data), request_id, is_download,
       is_stream, mojom::URLLoaderFactoryPtrInfo());
 }
-
-void NavigationURLLoaderImpl::NotifyRequestFailed(bool in_cache,
-                                                  int net_error) {
+void NavigationURLLoaderImpl::NotifyRequestFailed(
+    bool in_cache,
+    int net_error,
+    base::Optional<net::SSLInfo> ssl_info,
+    bool should_ssl_errors_be_fatal) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  delegate_->OnRequestFailed(in_cache, net_error);
+  delegate_->OnRequestFailed(in_cache, net_error, ssl_info,
+                             should_ssl_errors_be_fatal);
 }
 
 void NavigationURLLoaderImpl::NotifyRequestStarted(base::TimeTicks timestamp) {
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index 5b6c2a3c6..3349458 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -10,11 +10,13 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "content/browser/loader/navigation_url_loader.h"
 
 namespace net {
 struct RedirectInfo;
+class SSLInfo;
 }
 
 namespace content {
@@ -60,8 +62,14 @@
                              bool is_download,
                              bool is_stream);
 
-  // Notifies the delegate the request failed to return a response.
-  void NotifyRequestFailed(bool in_cache, int net_error);
+  // Notifies the delegate the the request has failed. If |net_error| is a
+  // certificate error, the caller must pass valid values for |ssl_info| and
+  // |fatal_cert_error|. If |net_error| is not a certificate error, |ssl_info|
+  // and |fatal_cert_error| are ignored.
+  void NotifyRequestFailed(bool in_cache,
+                           int net_error,
+                           base::Optional<net::SSLInfo> ssl_info,
+                           bool should_ssl_errors_be_fatal);
 
   // Notifies the delegate the begin navigation request was handled and a
   // potential first network request is about to be made.
diff --git a/content/browser/loader/navigation_url_loader_impl_core.cc b/content/browser/loader/navigation_url_loader_impl_core.cc
index a505821..0166832 100644
--- a/content/browser/loader/navigation_url_loader_impl_core.cc
+++ b/content/browser/loader/navigation_url_loader_impl_core.cc
@@ -136,8 +136,11 @@
                      is_stream));
 }
 
-void NavigationURLLoaderImplCore::NotifyRequestFailed(bool in_cache,
-                                                      int net_error) {
+void NavigationURLLoaderImplCore::NotifyRequestFailed(
+    bool in_cache,
+    int net_error,
+    const base::Optional<net::SSLInfo>& ssl_info,
+    bool should_ssl_errors_be_fatal) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   TRACE_EVENT_ASYNC_END0("navigation", "Navigation redirectDelay", this);
   TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this,
@@ -147,7 +150,8 @@
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::BindOnce(&NavigationURLLoaderImpl::NotifyRequestFailed, loader_,
-                     in_cache, net_error));
+                     in_cache, net_error, ssl_info,
+                     should_ssl_errors_be_fatal));
 }
 
 }  // namespace content
diff --git a/content/browser/loader/navigation_url_loader_impl_core.h b/content/browser/loader/navigation_url_loader_impl_core.h
index b4044b23..2d5d5f88 100644
--- a/content/browser/loader/navigation_url_loader_impl_core.h
+++ b/content/browser/loader/navigation_url_loader_impl_core.h
@@ -83,7 +83,10 @@
                              bool is_stream);
 
   // Notifies |loader_| on the UI thread that the request failed.
-  void NotifyRequestFailed(bool in_cache, int net_error);
+  void NotifyRequestFailed(bool in_cache,
+                           int net_error,
+                           const base::Optional<net::SSLInfo>& ssl_info,
+                           bool should_ssl_errors_be_fatal);
 
  private:
   friend class base::RefCountedThreadSafe<NavigationURLLoaderImplCore>;
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index a61d394..9e11659 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -548,10 +548,16 @@
     TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted",
                            this, "&NavigationURLLoaderNetworkService", this,
                            "success", false);
-
-    delegate_->OnRequestFailed(completion_status.exists_in_cache,
-                               completion_status.error_code);
   }
+
+  // TODO(https://crbug.com/757633): Pass real values in the case of cert
+  // errors.
+  base::Optional<net::SSLInfo> ssl_info = base::nullopt;
+  bool should_ssl_errors_be_fatal = true;
+
+  delegate_->OnRequestFailed(completion_status.exists_in_cache,
+                             completion_status.error_code, ssl_info,
+                             should_ssl_errors_be_fatal);
 }
 
 }  // namespace content
diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc
index 5cb278b..8c5c860 100644
--- a/content/browser/loader/navigation_url_loader_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_unittest.cc
@@ -31,7 +31,9 @@
 #include "content/test/test_navigation_url_loader_delegate.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
+#include "net/cert/cert_status_flags.h"
 #include "net/http/http_response_headers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request.h"
@@ -115,6 +117,7 @@
         url::Origin(url));
     CommonNavigationParams common_params;
     common_params.url = url;
+
     std::unique_ptr<NavigationRequestInfo> request_info(
         new NavigationRequestInfo(common_params, begin_params, url, true, false,
                                   false, -1, false, false,
@@ -172,7 +175,7 @@
 }
 
 // Tests that request failures are propagated correctly.
-TEST_F(NavigationURLLoaderTest, RequestFailed) {
+TEST_F(NavigationURLLoaderTest, RequestFailedNoCertError) {
   TestNavigationURLLoaderDelegate delegate;
   std::unique_ptr<NavigationURLLoader> loader =
       MakeTestLoader(GURL("bogus:bogus"), &delegate);
@@ -180,6 +183,65 @@
   // Wait for the request to fail as expected.
   delegate.WaitForRequestFailed();
   EXPECT_EQ(net::ERR_UNKNOWN_URL_SCHEME, delegate.net_error());
+  EXPECT_FALSE(delegate.ssl_info().has_value());
+  EXPECT_EQ(1, delegate.on_request_handled_counter());
+}
+
+// Tests that request failures are propagated correctly for a (non-fatal) cert
+// error:
+// - |ssl_info| has the expected values.
+// - |should_ssl_errors_be_fatal| is false.
+TEST_F(NavigationURLLoaderTest, RequestFailedCertError) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+  ASSERT_TRUE(https_server.Start());
+
+  TestNavigationURLLoaderDelegate delegate;
+  std::unique_ptr<NavigationURLLoader> loader =
+      MakeTestLoader(https_server.GetURL("/"), &delegate);
+
+  // Wait for the request to fail as expected.
+  delegate.WaitForRequestFailed();
+  ASSERT_EQ(net::ERR_ABORTED, delegate.net_error());
+  net::SSLInfo ssl_info = delegate.ssl_info().value();
+  EXPECT_TRUE(net::MapCertStatusToNetError(ssl_info.is_valid()));
+  EXPECT_TRUE(https_server.GetCertificate()->Equals(ssl_info.cert.get()));
+  EXPECT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
+            net::MapCertStatusToNetError(ssl_info.cert_status));
+  EXPECT_FALSE(delegate.should_ssl_errors_be_fatal());
+  EXPECT_EQ(1, delegate.on_request_handled_counter());
+}
+
+// Tests that request failures are propagated correctly for a fatal cert error:
+// - |ssl_info| has the expected values.
+// - |should_ssl_errors_be_fatal| is true.
+TEST_F(NavigationURLLoaderTest, RequestFailedCertErrorFatal) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+  ASSERT_TRUE(https_server.Start());
+  GURL url = https_server.GetURL("/");
+
+  // Set HSTS for the test domain in order to make SSL errors fatal.
+  net::TransportSecurityState* transport_security_state =
+      browser_context_->GetResourceContext()
+          ->GetRequestContext()
+          ->transport_security_state();
+  base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
+  bool include_subdomains = false;
+  transport_security_state->AddHSTS(url.host(), expiry, include_subdomains);
+
+  TestNavigationURLLoaderDelegate delegate;
+  std::unique_ptr<NavigationURLLoader> loader = MakeTestLoader(url, &delegate);
+
+  // Wait for the request to fail as expected.
+  delegate.WaitForRequestFailed();
+  ASSERT_EQ(net::ERR_ABORTED, delegate.net_error());
+  net::SSLInfo ssl_info = delegate.ssl_info().value();
+  EXPECT_TRUE(net::MapCertStatusToNetError(ssl_info.is_valid()));
+  EXPECT_TRUE(https_server.GetCertificate()->Equals(ssl_info.cert.get()));
+  EXPECT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
+            net::MapCertStatusToNetError(ssl_info.cert_status));
+  EXPECT_TRUE(delegate.should_ssl_errors_be_fatal());
   EXPECT_EQ(1, delegate.on_request_handled_counter());
 }
 
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 1d4db3f..a3ae030 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -2106,7 +2106,7 @@
           info.common_params.url,
           resource_type,
           resource_context))) {
-    loader->NotifyRequestFailed(false, net::ERR_ABORTED);
+    loader->NotifyRequestFailed(false, net::ERR_ABORTED, base::nullopt, false);
     return;
   }
 
@@ -2152,7 +2152,8 @@
   if (body) {
     if (!GetBodyBlobDataHandles(body, resource_context, &blob_handles)) {
       new_request->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
-      loader->NotifyRequestFailed(false, net::ERR_ABORTED);
+      loader->NotifyRequestFailed(false, net::ERR_ABORTED, base::nullopt,
+                                  false);
       return;
     }
     new_request->set_upload(UploadDataStreamBuilder::Build(
diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS
index 8a92367..4b90552 100644
--- a/content/browser/renderer_host/OWNERS
+++ b/content/browser/renderer_host/OWNERS
@@ -19,6 +19,9 @@
 sadrul@chromium.org
 tdresser@chromium.org
 
+# For surface ID propagation and synchronization
+fsamuel@chromium.org
+
 # Linux sandboxing
 per-file render_sandbox_host_linux.*=jln@chromium.org
 per-file render_sandbox_host_linux.*=jorgelo@chromium.org
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index bcc553a..dda1da68 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -113,7 +113,7 @@
 #include "ui/gfx/gdi_util.h"
 #endif
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 #include "content/browser/accessibility/browser_accessibility_auralinux.h"
 #endif
 
@@ -526,61 +526,12 @@
 
 void RenderWidgetHostViewAura::Show() {
   window_->Show();
-
-  if (!host_->is_hidden())
-    return;
-
-  bool has_saved_frame =
-      delegated_frame_host_ ? delegated_frame_host_->HasSavedFrame() : false;
-  ui::LatencyInfo renderer_latency_info, browser_latency_info;
-  if (has_saved_frame) {
-    browser_latency_info.AddLatencyNumber(
-        ui::TAB_SHOW_COMPONENT, host_->GetLatencyComponentId(), 0);
-  } else {
-    renderer_latency_info.AddLatencyNumber(
-        ui::TAB_SHOW_COMPONENT, host_->GetLatencyComponentId(), 0);
-  }
-  host_->WasShown(renderer_latency_info);
-
-  aura::Window* root = window_->GetRootWindow();
-  if (root) {
-    aura::client::CursorClient* cursor_client =
-        aura::client::GetCursorClient(root);
-    if (cursor_client)
-      NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible());
-  }
-
-  if (delegated_frame_host_)
-    delegated_frame_host_->WasShown(browser_latency_info);
-
-#if defined(OS_WIN)
-  UpdateLegacyWin();
-#endif
+  WasUnOccluded();
 }
 
 void RenderWidgetHostViewAura::Hide() {
   window_->Hide();
-
-  if (!host_->is_hidden()) {
-    host_->WasHidden();
-    if (delegated_frame_host_)
-      delegated_frame_host_->WasHidden();
-
-#if defined(OS_WIN)
-    aura::WindowTreeHost* host = window_->GetHost();
-    if (host) {
-      // We reparent the legacy Chrome_RenderWidgetHostHWND window to the global
-      // hidden window on the same lines as Windowed plugin windows.
-      if (legacy_render_widget_host_HWND_)
-        legacy_render_widget_host_HWND_->UpdateParent(ui::GetHiddenWindow());
-    }
-#endif
-  }
-
-#if defined(OS_WIN)
-  if (legacy_render_widget_host_HWND_)
-    legacy_render_widget_host_HWND_->Hide();
-#endif
+  WasOccluded();
 }
 
 void RenderWidgetHostViewAura::SetSize(const gfx::Size& size) {
@@ -632,7 +583,7 @@
       host_->GetOrCreateRootBrowserAccessibilityManager();
   if (manager)
     return ToBrowserAccessibilityWin(manager->GetRoot())->GetCOM();
-#elif defined(USE_X11) && !defined(OS_CHROMEOS)
+#elif defined(USE_X11)
   BrowserAccessibilityManager* manager =
       host_->GetOrCreateRootBrowserAccessibilityManager();
   if (manager)
@@ -715,6 +666,61 @@
   return window_->IsVisible();
 }
 
+void RenderWidgetHostViewAura::WasUnOccluded() {
+  if (!host_->is_hidden())
+    return;
+
+  bool has_saved_frame =
+      delegated_frame_host_ ? delegated_frame_host_->HasSavedFrame() : false;
+  ui::LatencyInfo renderer_latency_info, browser_latency_info;
+  if (has_saved_frame) {
+    browser_latency_info.AddLatencyNumber(ui::TAB_SHOW_COMPONENT,
+                                          host_->GetLatencyComponentId(), 0);
+  } else {
+    renderer_latency_info.AddLatencyNumber(ui::TAB_SHOW_COMPONENT,
+                                           host_->GetLatencyComponentId(), 0);
+  }
+  host_->WasShown(renderer_latency_info);
+
+  aura::Window* root = window_->GetRootWindow();
+  if (root) {
+    aura::client::CursorClient* cursor_client =
+        aura::client::GetCursorClient(root);
+    if (cursor_client)
+      NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible());
+  }
+
+  if (delegated_frame_host_)
+    delegated_frame_host_->WasShown(browser_latency_info);
+
+#if defined(OS_WIN)
+  UpdateLegacyWin();
+#endif
+}
+
+void RenderWidgetHostViewAura::WasOccluded() {
+  if (!host_->is_hidden()) {
+    host_->WasHidden();
+    if (delegated_frame_host_)
+      delegated_frame_host_->WasHidden();
+
+#if defined(OS_WIN)
+    aura::WindowTreeHost* host = window_->GetHost();
+    if (host) {
+      // We reparent the legacy Chrome_RenderWidgetHostHWND window to the global
+      // hidden window on the same lines as Windowed plugin windows.
+      if (legacy_render_widget_host_HWND_)
+        legacy_render_widget_host_HWND_->UpdateParent(ui::GetHiddenWindow());
+    }
+#endif
+  }
+
+#if defined(OS_WIN)
+  if (legacy_render_widget_host_HWND_)
+    legacy_render_widget_host_HWND_->Hide();
+#endif
+}
+
 gfx::Rect RenderWidgetHostViewAura::GetViewBounds() const {
   return window_->GetBoundsInScreen();
 }
@@ -2389,7 +2395,7 @@
   if (!focused_view)
     return;
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
   const TextInputManager::TextSelection* selection =
       GetTextInputManager()->GetTextSelection(focused_view);
   if (selection->selected_text().length()) {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index a6a4297..243679d 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -109,6 +109,8 @@
   void Show() override;
   void Hide() override;
   bool IsShowing() override;
+  void WasUnOccluded() override;
+  void WasOccluded() override;
   gfx::Rect GetViewBounds() const override;
   void SetBackgroundColor(SkColor color) override;
   SkColor background_color() const override;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 2675ab47..2ce5362 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -1746,6 +1746,43 @@
   EXPECT_EQ(InputMsg_HandleInputEvent::ID, sink_->GetMessageAt(1)->type());
 }
 
+// Checks that WasOcculded/WasUnOccluded notifies RenderWidgetHostImpl.
+TEST_F(RenderWidgetHostViewAuraTest, WasOccluded) {
+  view_->InitAsChild(nullptr);
+  view_->Show();
+  EXPECT_FALSE(widget_host_->is_hidden());
+
+  // Verifies WasOccluded sets RenderWidgetHostImpl as hidden and WasUnOccluded
+  // resets the state.
+  view_->WasOccluded();
+  EXPECT_TRUE(widget_host_->is_hidden());
+  view_->WasUnOccluded();
+  EXPECT_FALSE(widget_host_->is_hidden());
+
+  // Verifies WasOccluded sets RenderWidgetHostImpl as hidden and Show resets
+  // the state.
+  view_->WasOccluded();
+  EXPECT_TRUE(widget_host_->is_hidden());
+  view_->Show();
+  EXPECT_FALSE(widget_host_->is_hidden());
+
+  // WasOccluded and WasUnOccluded are not in pairs. The last one dictates
+  // the final state.
+  for (int i = 0; i < 2; ++i) {
+    view_->WasOccluded();
+    EXPECT_TRUE(widget_host_->is_hidden());
+  }
+  view_->WasUnOccluded();
+  EXPECT_FALSE(widget_host_->is_hidden());
+
+  for (int i = 0; i < 4; ++i) {
+    view_->WasUnOccluded();
+    EXPECT_FALSE(widget_host_->is_hidden());
+  }
+  view_->WasOccluded();
+  EXPECT_TRUE(widget_host_->is_hidden());
+}
+
 // Checks that touch-event state is maintained correctly.
 TEST_F(RenderWidgetHostViewAuraRafAlignedTouchDisabledTest, TouchEventState) {
   view_->InitAsChild(nullptr);
@@ -6264,7 +6301,7 @@
   }
 }
 
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
 // This test will verify that after selection, the selected text is written to
 // the clipboard from the focused widget.
 TEST_F(InputMethodStateAuraTest, SelectedTextCopiedToClipboard) {
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index a11f6f08..bb30a92 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2495,7 +2495,7 @@
   RunAllPendingInMessageLoop();
   EXPECT_TRUE(deleted);
 
-  // Now try again but this time crash the intersitial after it was shown.
+  // Now try again but this time crash the interstitial after it was shown.
   interstitial =
       new TestInterstitialPage(contents(), true, url, &state, &deleted);
   interstitial->Show();
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 374c7a0..f407726a 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -150,7 +150,7 @@
   DISALLOW_COPY_AND_ASSIGN(WebDragSourceAura);
 };
 
-#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN)
+#if defined(USE_X11) || defined(OS_WIN)
 // Fill out the OSExchangeData with a file contents, synthesizing a name if
 // necessary.
 void PrepareDragForFileContents(const DropData& drop_data,
@@ -286,7 +286,7 @@
   if (!drop_data.download_metadata.empty())
     PrepareDragForDownload(drop_data, provider, web_contents);
 #endif
-#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN)
+#if defined(USE_X11) || defined(OS_WIN)
   // We set the file contents before the URL because the URL also sets file
   // contents (to a .URL shortcut).  We want to prefer file content data over
   // a shortcut so we add it first.
@@ -1158,7 +1158,7 @@
     // Linux window managers like to handle raise-on-click themselves.  If we
     // raise-on-click manually, this may override user settings that prevent
     // focus-stealing.
-#if !defined(USE_X11) || defined (OS_CHROMEOS)
+#if !defined(USE_X11)
     web_contents_->GetDelegate()->ActivateContents(web_contents_);
 #endif
   }
diff --git a/content/common/feature_policy/feature_policy.cc b/content/common/feature_policy/feature_policy.cc
index 6d82e7b..8255175 100644
--- a/content/common/feature_policy/feature_policy.cc
+++ b/content/common/feature_policy/feature_policy.cc
@@ -235,6 +235,8 @@
                            {blink::WebFeaturePolicyFeature::kSyncXHR,
                             FeaturePolicy::FeatureDefault::EnableForAll},
                            {blink::WebFeaturePolicyFeature::kUsb,
+                            FeaturePolicy::FeatureDefault::EnableForSelf},
+                           {blink::WebFeaturePolicyFeature::kWebVr,
                             FeaturePolicy::FeatureDefault::EnableForSelf}}));
   return default_feature_list;
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
index 10fb514..346d306 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
@@ -651,7 +651,7 @@
         int id = item.getItemId();
         int groupId = item.getGroupId();
 
-        if (BuildInfo.isAtLeastO()) {
+        if (BuildInfo.isAtLeastO() && id == android.R.id.textAssist) {
             doAssistAction();
             mode.finish();
         } else if (id == R.id.select_action_menu_select_all) {
diff --git a/content/public/test/simple_url_loader_test_helper.cc b/content/public/test/simple_url_loader_test_helper.cc
new file mode 100644
index 0000000..85f74f5f
--- /dev/null
+++ b/content/public/test/simple_url_loader_test_helper.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/test/simple_url_loader_test_helper.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+
+namespace content {
+
+SimpleURLLoaderTestHelper::SimpleURLLoaderTestHelper()
+    : weak_ptr_factory_(this) {}
+
+SimpleURLLoaderTestHelper::~SimpleURLLoaderTestHelper() {}
+
+SimpleURLLoader::BodyAsStringCallback SimpleURLLoaderTestHelper::GetCallback() {
+  DCHECK(!callback_created_);
+  callback_created_ = true;
+
+  return base::Bind(&SimpleURLLoaderTestHelper::OnCompleteCallback,
+                    weak_ptr_factory_.GetWeakPtr());
+}
+
+void SimpleURLLoaderTestHelper::WaitForCallback() {
+  DCHECK(!wait_started_);
+  wait_started_ = true;
+  run_loop_.Run();
+}
+
+void SimpleURLLoaderTestHelper::OnCompleteCallback(
+    std::unique_ptr<std::string> response_body) {
+  DCHECK(!response_body_);
+
+  response_body_ = std::move(response_body);
+  run_loop_.Quit();
+}
+
+}  // namespace content
diff --git a/content/public/test/simple_url_loader_test_helper.h b/content/public/test/simple_url_loader_test_helper.h
new file mode 100644
index 0000000..b3cec95d
--- /dev/null
+++ b/content/public/test/simple_url_loader_test_helper.h
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_TEST_SIMPLE_URL_LOADER_TEST_HELPER_H_
+#define CONTENT_PUBLIC_TEST_SIMPLE_URL_LOADER_TEST_HELPER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "content/public/common/simple_url_loader.h"
+
+namespace content {
+
+// Test utility class for waiting until a SimpleURLLoader has received a
+// response body.
+class SimpleURLLoaderTestHelper {
+ public:
+  SimpleURLLoaderTestHelper();
+  ~SimpleURLLoaderTestHelper();
+
+  // Returns a BodyAsStringCallback for use with a SimpleURLLoader. May be
+  // called only once.
+  SimpleURLLoader::BodyAsStringCallback GetCallback();
+
+  // Waits until the callback returned by GetCallback() is invoked.
+  void WaitForCallback();
+
+  // Response body passed to the callback returned by GetCallback, if there was
+  // one.
+  const std::string* response_body() const { return response_body_.get(); }
+
+ private:
+  // Called back GetCallback().  Stores the response body and quits the message
+  // loop.
+  void OnCompleteCallback(std::unique_ptr<std::string> response_body);
+
+  // Used to ensure GetCallback() is called only once.
+  bool callback_created_ = false;
+  // Used to ensure WaitForCallback() is called only once.
+  bool wait_started_ = false;
+
+  base::RunLoop run_loop_;
+
+  std::unique_ptr<std::string> response_body_;
+
+  base::WeakPtrFactory<SimpleURLLoaderTestHelper> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleURLLoaderTestHelper);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_TEST_SIMPLE_URL_LOADER_TEST_HELPER_H_
diff --git a/content/renderer/media/cdm/pepper_cdm_wrapper.h b/content/renderer/media/cdm/pepper_cdm_wrapper.h
index 1f4c934..b925439 100644
--- a/content/renderer/media/cdm/pepper_cdm_wrapper.h
+++ b/content/renderer/media/cdm/pepper_cdm_wrapper.h
@@ -17,7 +17,9 @@
 #include "base/callback.h"
 #include "base/macros.h"
 
-class GURL;
+namespace url {
+class Origin;
+}
 
 namespace content {
 class ContentDecryptorDelegate;
@@ -41,7 +43,7 @@
 // Pepper CDM can not be created.
 typedef base::Callback<std::unique_ptr<PepperCdmWrapper>(
     const std::string& pluginType,
-    const GURL& security_origin)>
+    const url::Origin& security_origin)>
     CreatePepperCdmCB;
 
 }  // namespace content
diff --git a/content/renderer/media/cdm/pepper_cdm_wrapper_impl.cc b/content/renderer/media/cdm/pepper_cdm_wrapper_impl.cc
index 33172fa..5cad0ab0 100644
--- a/content/renderer/media/cdm/pepper_cdm_wrapper_impl.cc
+++ b/content/renderer/media/cdm/pepper_cdm_wrapper_impl.cc
@@ -32,7 +32,7 @@
 std::unique_ptr<PepperCdmWrapper> PepperCdmWrapperImpl::Create(
     blink::WebLocalFrame* frame,
     const std::string& pluginType,
-    const GURL& security_origin) {
+    const url::Origin& security_origin) {
   DCHECK(frame);
 
   // The frame security origin could be different from the origin where the CDM
@@ -40,8 +40,8 @@
   // Note: The code will continue after navigation to the "same" origin, even
   // though the CDM is no longer necessary.
   // TODO: Consider avoiding this possibility entirely. http://crbug.com/575236
-  GURL frame_security_origin(url::Origin(frame->GetSecurityOrigin()).GetURL());
-  if (frame_security_origin != security_origin) {
+  const url::Origin frame_security_origin(frame->GetSecurityOrigin());
+  if (!security_origin.IsSameOriginWith(frame_security_origin)) {
     LOG(ERROR) << "Frame has a different origin than the EME call.";
     return std::unique_ptr<PepperCdmWrapper>();
   }
@@ -61,9 +61,8 @@
   if (!plugin_instance.get())
     return std::unique_ptr<PepperCdmWrapper>();
 
-  GURL plugin_url(plugin_instance->container()->GetDocument().Url());
-  GURL plugin_security_origin = plugin_url.GetOrigin();
-  CHECK_EQ(security_origin, plugin_security_origin)
+  CHECK(security_origin.IsSameOriginWith(
+      plugin_instance->container()->GetDocument().GetSecurityOrigin()))
       << "Pepper instance has a different origin than the EME call.";
 
   if (!plugin_instance->GetContentDecryptorDelegate())
diff --git a/content/renderer/media/cdm/pepper_cdm_wrapper_impl.h b/content/renderer/media/cdm/pepper_cdm_wrapper_impl.h
index ba0efca..34d972d0 100644
--- a/content/renderer/media/cdm/pepper_cdm_wrapper_impl.h
+++ b/content/renderer/media/cdm/pepper_cdm_wrapper_impl.h
@@ -23,6 +23,10 @@
 class WebLocalFrame;
 }
 
+namespace url {
+class Origin;
+}
+
 namespace content {
 
 class ContentDecryptorDelegate;
@@ -44,9 +48,10 @@
 // blink:: objects.
 class PepperCdmWrapperImpl : public PepperCdmWrapper {
  public:
-  static std::unique_ptr<PepperCdmWrapper> Create(blink::WebLocalFrame* frame,
-                                                  const std::string& pluginType,
-                                                  const GURL& security_origin);
+  static std::unique_ptr<PepperCdmWrapper> Create(
+      blink::WebLocalFrame* frame,
+      const std::string& pluginType,
+      const url::Origin& security_origin);
 
   ~PepperCdmWrapperImpl() override;
 
diff --git a/content/renderer/media/cdm/ppapi_decryptor.cc b/content/renderer/media/cdm/ppapi_decryptor.cc
index 7eaeda13..7faec01c 100644
--- a/content/renderer/media/cdm/ppapi_decryptor.cc
+++ b/content/renderer/media/cdm/ppapi_decryptor.cc
@@ -23,12 +23,13 @@
 #include "media/base/key_systems.h"
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_frame.h"
+#include "url/origin.h"
 
 namespace content {
 
 void PpapiDecryptor::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     bool allow_distinctive_identifier,
     bool allow_persistent_state,
     const CreatePepperCdmCB& create_pepper_cdm_cb,
diff --git a/content/renderer/media/cdm/ppapi_decryptor.h b/content/renderer/media/cdm/ppapi_decryptor.h
index 3355b872..524bad5 100644
--- a/content/renderer/media/cdm/ppapi_decryptor.h
+++ b/content/renderer/media/cdm/ppapi_decryptor.h
@@ -21,12 +21,14 @@
 #include "media/base/decryptor.h"
 #include "media/base/video_decoder_config.h"
 
-class GURL;
-
 namespace base {
 class SingleThreadTaskRunner;
 }
 
+namespace url {
+class Origin;
+}
+
 namespace content {
 class ContentDecryptorDelegate;
 
@@ -39,7 +41,7 @@
  public:
   static void Create(
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       bool allow_distinctive_identifier,
       bool allow_persistent_state,
       const CreatePepperCdmCB& create_pepper_cdm_cb,
diff --git a/content/renderer/media/cdm/render_cdm_factory.cc b/content/renderer/media/cdm/render_cdm_factory.cc
index f4c1e95..6f17c04 100644
--- a/content/renderer/media/cdm/render_cdm_factory.cc
+++ b/content/renderer/media/cdm/render_cdm_factory.cc
@@ -17,7 +17,7 @@
 #include "media/base/key_systems.h"
 #include "media/cdm/aes_decryptor.h"
 #include "media/media_features.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "content/renderer/media/cdm/ppapi_decryptor.h"
@@ -39,7 +39,7 @@
 
 void RenderCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const media::CdmConfig& cdm_config,
     const media::SessionMessageCB& session_message_cb,
     const media::SessionClosedCB& session_closed_cb,
@@ -48,7 +48,7 @@
     const media::CdmCreatedCB& cdm_created_cb) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!security_origin.is_valid()) {
+  if (security_origin.unique()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(cdm_created_cb, nullptr, "Invalid origin."));
     return;
diff --git a/content/renderer/media/cdm/render_cdm_factory.h b/content/renderer/media/cdm/render_cdm_factory.h
index b9dcf0f..f1223fc0 100644
--- a/content/renderer/media/cdm/render_cdm_factory.h
+++ b/content/renderer/media/cdm/render_cdm_factory.h
@@ -17,12 +17,6 @@
 #include "content/renderer/media/cdm/pepper_cdm_wrapper.h"
 #endif
 
-class GURL;
-
-namespace media {
-struct CdmConfig;
-}  // namespace media
-
 namespace content {
 
 // CdmFactory implementation in content/renderer. This class is not thread safe
@@ -40,7 +34,7 @@
   // CdmFactory implementation.
   void Create(
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       const media::CdmConfig& cdm_config,
       const media::SessionMessageCB& session_message_cb,
       const media::SessionClosedCB& session_closed_cb,
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 2ccdca1..0fc99e4 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -37,6 +37,7 @@
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/WebKit/public/web/WebKit.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "url/origin.h"
 
 #if defined(OS_ANDROID)
 #include "content/renderer/media/android/media_player_renderer_client_factory.h"
@@ -45,6 +46,7 @@
 #include "content/renderer/media/android/stream_texture_wrapper_impl.h"
 #include "media/base/android/media_codec_util.h"
 #include "media/base/media.h"
+#include "url/gurl.h"
 #endif
 
 #if BUILDFLAG(ENABLE_MOJO_MEDIA)
diff --git a/content/renderer/media/media_factory.h b/content/renderer/media/media_factory.h
index 3b67219..8dddde7a 100644
--- a/content/renderer/media/media_factory.h
+++ b/content/renderer/media/media_factory.h
@@ -21,7 +21,6 @@
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/platform/WebSetSinkIdCallbacks.h"
 #include "third_party/WebKit/public/platform/WebString.h"
-#include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_MOJO_MEDIA)
 #include "media/mojo/interfaces/interface_factory.mojom.h"  // nogncheck
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index fdec645..f1cf732c 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1460,14 +1460,8 @@
                        RenderThreadImpl::current()->sync_message_filter(),
                        compositor_->GetSourceFrameNumber());
 
-  if (swap_promise) {
+  if (swap_promise)
     compositor_->QueueSwapPromise(std::move(swap_promise));
-    // Request a main frame. This might either A) request a commit ahead of time
-    // or B) request a commit which is not needed because there are not pending
-    // updates. If B) then the frame will be aborted early and the swap promises
-    // will be broken (see EarlyOut_NoUpdates).
-    compositor_->SetNeedsBeginFrame();
-  }
 }
 
 void RenderWidget::DidChangeCursor(const WebCursorInfo& cursor_info) {
diff --git a/content/renderer/webclipboard_impl.cc b/content/renderer/webclipboard_impl.cc
index efe4152..e914aef 100644
--- a/content/renderer/webclipboard_impl.cc
+++ b/content/renderer/webclipboard_impl.cc
@@ -220,7 +220,7 @@
     case kBufferStandard:
       break;
     case kBufferSelection:
-#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#if defined(USE_X11)
       *result = ui::CLIPBOARD_TYPE_SELECTION;
       break;
 #else
diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc
index 32524409..1f25cd74ff 100644
--- a/content/shell/browser/shell_browser_main_parts.cc
+++ b/content/shell/browser/shell_browser_main_parts.cc
@@ -175,7 +175,8 @@
             switches::kCrashDumpsDir);
     breakpad::CrashDumpObserver::GetInstance()->RegisterClient(
         base::MakeUnique<breakpad::ChildProcessCrashObserver>(
-            crash_dumps_dir, kAndroidMinidumpDescriptor));
+            crash_dumps_dir, kAndroidMinidumpDescriptor,
+            base::Bind(&base::DoNothing)));
   }
 
   return 0;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c7cebf0..9cf97e0 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -104,6 +104,8 @@
     "../public/test/render_view_test.h",
     "../public/test/service_worker_test_helpers.cc",
     "../public/test/service_worker_test_helpers.h",
+    "../public/test/simple_url_loader_test_helper.cc",
+    "../public/test/simple_url_loader_test_helper.h",
     "../public/test/test_browser_context.cc",
     "../public/test/test_browser_context.h",
     "../public/test/test_browser_thread.cc",
diff --git a/content/test/test_navigation_url_loader.cc b/content/test/test_navigation_url_loader.cc
index 1c07a2f3..367e0f90 100644
--- a/content/test/test_navigation_url_loader.cc
+++ b/content/test/test_navigation_url_loader.cc
@@ -49,7 +49,7 @@
 }
 
 void TestNavigationURLLoader::SimulateError(int error_code) {
-  delegate_->OnRequestFailed(false, error_code);
+  delegate_->OnRequestFailed(false, error_code, base::nullopt, false);
 }
 
 void TestNavigationURLLoader::CallOnRequestRedirected(
diff --git a/content/test/test_navigation_url_loader_delegate.cc b/content/test/test_navigation_url_loader_delegate.cc
index 26ec48c..7097a81 100644
--- a/content/test/test_navigation_url_loader_delegate.cc
+++ b/content/test/test_navigation_url_loader_delegate.cc
@@ -73,9 +73,14 @@
     response_started_->Quit();
 }
 
-void TestNavigationURLLoaderDelegate::OnRequestFailed(bool in_cache,
-                                                      int net_error) {
+void TestNavigationURLLoaderDelegate::OnRequestFailed(
+    bool in_cache,
+    int net_error,
+    const base::Optional<net::SSLInfo>& ssl_info,
+    bool should_ssl_errors_be_fatal) {
   net_error_ = net_error;
+  ssl_info_ = ssl_info;
+  should_ssl_errors_be_fatal_ = should_ssl_errors_be_fatal;
   if (request_failed_)
     request_failed_->Quit();
 }
diff --git a/content/test/test_navigation_url_loader_delegate.h b/content/test/test_navigation_url_loader_delegate.h
index 8289309..b3723960 100644
--- a/content/test/test_navigation_url_loader_delegate.h
+++ b/content/test/test_navigation_url_loader_delegate.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "mojo/public/cpp/system/data_pipe.h"
@@ -38,6 +39,10 @@
   ResourceResponse* response() const { return response_.get(); }
   StreamHandle* body() const { return body_.get(); }
   int net_error() const { return net_error_; }
+  base::Optional<net::SSLInfo> ssl_info() const { return ssl_info_; }
+  bool should_ssl_errors_be_fatal() const {
+    return should_ssl_errors_be_fatal_;
+  }
   int on_request_handled_counter() const { return on_request_handled_counter_; }
 
   // Waits for various navigation events.
@@ -65,7 +70,10 @@
       bool is_download,
       bool is_stream,
       mojom::URLLoaderFactoryPtrInfo loader_factory_ptr_info) override;
-  void OnRequestFailed(bool in_cache, int net_error) override;
+  void OnRequestFailed(bool in_cache,
+                       int net_error,
+                       const base::Optional<net::SSLInfo>& ssl_info,
+                       bool should_ssl_errors_be_fatal) override;
   void OnRequestStarted(base::TimeTicks timestamp) override;
 
  private:
@@ -75,6 +83,8 @@
   std::unique_ptr<StreamHandle> body_;
   mojo::ScopedDataPipeConsumerHandle handle_;
   int net_error_;
+  base::Optional<net::SSLInfo> ssl_info_;
+  bool should_ssl_errors_be_fatal_;
   int on_request_handled_counter_;
 
   std::unique_ptr<base::RunLoop> request_redirected_;
diff --git a/device/hid/BUILD.gn b/device/hid/BUILD.gn
index 6a958172..ae135754 100644
--- a/device/hid/BUILD.gn
+++ b/device/hid/BUILD.gn
@@ -21,8 +21,6 @@
     "hid_device_filter.h",
     "hid_device_info.cc",
     "hid_device_info.h",
-    "hid_device_info_linux.cc",
-    "hid_device_info_linux.h",
     "hid_report_descriptor.cc",
     "hid_report_descriptor.h",
     "hid_report_descriptor_item.cc",
@@ -44,6 +42,10 @@
     "//net",
   ]
 
+  public_deps = [
+    "//device/hid/public/interfaces",
+  ]
+
   if (is_linux && use_udev) {
     sources += [
       "fake_input_service_linux.cc",
diff --git a/device/hid/hid_connection_unittest.cc b/device/hid/hid_connection_unittest.cc
index 96f2194..806ccc7 100644
--- a/device/hid/hid_connection_unittest.cc
+++ b/device/hid/hid_connection_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/test/test_io_thread.h"
 #include "device/hid/hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/test/test_device_client.h"
 #include "device/test/usb_test_gadget.h"
 #include "device/usb/usb_device.h"
@@ -54,10 +55,10 @@
  private:
   void OnEnumerationComplete(
       HidService* hid_service,
-      const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
-    for (const scoped_refptr<HidDeviceInfo>& device_info : devices) {
-      if (device_info->serial_number() == serial_number_) {
-        device_guid_ = device_info->device_guid();
+      std::vector<device::mojom::HidDeviceInfoPtr> devices) {
+    for (auto& device_info : devices) {
+      if (device_info->serial_number == serial_number_) {
+        device_guid_ = device_info->guid;
         run_loop_.Quit();
         break;
       }
@@ -65,9 +66,9 @@
     observer_.Add(hid_service);
   }
 
-  void OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) override {
-    if (device_info->serial_number() == serial_number_) {
-      device_guid_ = device_info->device_guid();
+  void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device_info) override {
+    if (device_info->serial_number == serial_number_) {
+      device_guid_ = device_info->guid;
       run_loop_.Quit();
     }
   }
diff --git a/device/hid/hid_device_filter.cc b/device/hid/hid_device_filter.cc
index 3be57ab..9917fd5 100644
--- a/device/hid/hid_device_filter.cc
+++ b/device/hid/hid_device_filter.cc
@@ -39,20 +39,20 @@
 }
 
 bool HidDeviceFilter::Matches(
-    scoped_refptr<const HidDeviceInfo> device_info) const {
+    const device::mojom::HidDeviceInfo& device_info) const {
   if (vendor_id_set_) {
-    if (device_info->vendor_id() != vendor_id_) {
+    if (device_info.vendor_id != vendor_id_) {
       return false;
     }
 
-    if (product_id_set_ && device_info->product_id() != product_id_) {
+    if (product_id_set_ && device_info.product_id != product_id_) {
       return false;
     }
   }
 
   if (usage_page_set_) {
     bool found_matching_collection = false;
-    for (const HidCollectionInfo& collection : device_info->collections()) {
+    for (const HidCollectionInfo& collection : device_info.collections) {
       if (collection.usage.usage_page != usage_page_) {
         continue;
       }
@@ -70,8 +70,9 @@
 }
 
 // static
-bool HidDeviceFilter::MatchesAny(scoped_refptr<const HidDeviceInfo> device_info,
-                                 const std::vector<HidDeviceFilter>& filters) {
+bool HidDeviceFilter::MatchesAny(
+    const device::mojom::HidDeviceInfo& device_info,
+    const std::vector<HidDeviceFilter>& filters) {
   for (const HidDeviceFilter& filter : filters) {
     if (filter.Matches(device_info)) {
       return true;
diff --git a/device/hid/hid_device_filter.h b/device/hid/hid_device_filter.h
index fbffd2b..98ae6fc4 100644
--- a/device/hid/hid_device_filter.h
+++ b/device/hid/hid_device_filter.h
@@ -9,11 +9,10 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 
 namespace device {
 
-class HidDeviceInfo;
-
 class HidDeviceFilter {
  public:
   HidDeviceFilter();
@@ -24,9 +23,9 @@
   void SetUsagePage(uint16_t usage_page);
   void SetUsage(uint16_t usage);
 
-  bool Matches(scoped_refptr<const HidDeviceInfo> device_info) const;
+  bool Matches(const device::mojom::HidDeviceInfo& device_info) const;
 
-  static bool MatchesAny(scoped_refptr<const HidDeviceInfo> device_info,
+  static bool MatchesAny(const device::mojom::HidDeviceInfo& device_info,
                          const std::vector<HidDeviceFilter>& filters);
 
  private:
diff --git a/device/hid/hid_device_filter_unittest.cc b/device/hid/hid_device_filter_unittest.cc
index c19a7346..3a8eff3 100644
--- a/device/hid/hid_device_filter_unittest.cc
+++ b/device/hid/hid_device_filter_unittest.cc
@@ -7,6 +7,7 @@
 #include "build/build_config.h"
 #include "device/hid/hid_device_filter.h"
 #include "device/hid/hid_device_info.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/hid/test_report_descriptors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -27,7 +28,7 @@
   void SetUp() override {
     device_info_ = new HidDeviceInfo(
         kTestDeviceId, 0x046d, 0xc31c, "Test Keyboard", "123ABC",
-        kHIDBusTypeUSB,
+        device::mojom::HidBusType::kHIDBusTypeUSB,
         std::vector<uint8_t>(kKeyboard, kKeyboard + kKeyboardSize));
   }
 
@@ -37,71 +38,71 @@
 
 TEST_F(HidFilterTest, MatchAny) {
   HidDeviceFilter filter;
-  ASSERT_TRUE(filter.Matches(device_info_));
+  ASSERT_TRUE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchVendorId) {
   HidDeviceFilter filter;
   filter.SetVendorId(0x046d);
-  ASSERT_TRUE(filter.Matches(device_info_));
+  ASSERT_TRUE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchVendorIdNegative) {
   HidDeviceFilter filter;
   filter.SetVendorId(0x18d1);
-  ASSERT_FALSE(filter.Matches(device_info_));
+  ASSERT_FALSE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchProductId) {
   HidDeviceFilter filter;
   filter.SetVendorId(0x046d);
   filter.SetProductId(0xc31c);
-  ASSERT_TRUE(filter.Matches(device_info_));
+  ASSERT_TRUE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchProductIdNegative) {
   HidDeviceFilter filter;
   filter.SetVendorId(0x046d);
   filter.SetProductId(0x0801);
-  ASSERT_FALSE(filter.Matches(device_info_));
+  ASSERT_FALSE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchUsagePage) {
   HidDeviceFilter filter;
   filter.SetUsagePage(HidUsageAndPage::kPageGenericDesktop);
-  ASSERT_TRUE(filter.Matches(device_info_));
+  ASSERT_TRUE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchUsagePageNegative) {
   HidDeviceFilter filter;
   filter.SetUsagePage(HidUsageAndPage::kPageLed);
-  ASSERT_FALSE(filter.Matches(device_info_));
+  ASSERT_FALSE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchVendorAndUsagePage) {
   HidDeviceFilter filter;
   filter.SetVendorId(0x046d);
   filter.SetUsagePage(HidUsageAndPage::kPageGenericDesktop);
-  ASSERT_TRUE(filter.Matches(device_info_));
+  ASSERT_TRUE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchUsageAndPage) {
   HidDeviceFilter filter;
   filter.SetUsagePage(HidUsageAndPage::kPageGenericDesktop);
   filter.SetUsage(HidUsageAndPage::kGenericDesktopKeyboard);
-  ASSERT_TRUE(filter.Matches(device_info_));
+  ASSERT_TRUE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchUsageAndPageNegative) {
   HidDeviceFilter filter;
   filter.SetUsagePage(HidUsageAndPage::kPageGenericDesktop);
   filter.SetUsage(0x02);
-  ASSERT_FALSE(filter.Matches(device_info_));
+  ASSERT_FALSE(filter.Matches(*device_info_->device()));
 }
 
 TEST_F(HidFilterTest, MatchEmptyFilterListNegative) {
   std::vector<HidDeviceFilter> filters;
-  ASSERT_FALSE(HidDeviceFilter::MatchesAny(device_info_, filters));
+  ASSERT_FALSE(HidDeviceFilter::MatchesAny(*device_info_->device(), filters));
 }
 
 TEST_F(HidFilterTest, MatchFilterList) {
@@ -109,7 +110,7 @@
   HidDeviceFilter filter;
   filter.SetUsagePage(HidUsageAndPage::kPageGenericDesktop);
   filters.push_back(filter);
-  ASSERT_TRUE(HidDeviceFilter::MatchesAny(device_info_, filters));
+  ASSERT_TRUE(HidDeviceFilter::MatchesAny(*device_info_->device(), filters));
 }
 
 TEST_F(HidFilterTest, MatchFilterListNegative) {
@@ -117,7 +118,7 @@
   HidDeviceFilter filter;
   filter.SetUsagePage(HidUsageAndPage::kPageLed);
   filters.push_back(filter);
-  ASSERT_FALSE(HidDeviceFilter::MatchesAny(device_info_, filters));
+  ASSERT_FALSE(HidDeviceFilter::MatchesAny(*device_info_->device(), filters));
 }
 
 }  // namespace device
diff --git a/device/hid/hid_device_info.cc b/device/hid/hid_device_info.cc
index 8369f11..fd787ec 100644
--- a/device/hid/hid_device_info.cc
+++ b/device/hid/hid_device_info.cc
@@ -15,20 +15,26 @@
                              uint16_t product_id,
                              const std::string& product_name,
                              const std::string& serial_number,
-                             HidBusType bus_type,
-                             const std::vector<uint8_t> report_descriptor)
-    : guid_(base::GenerateGUID()),
-      platform_device_id_(platform_device_id),
-      vendor_id_(vendor_id),
-      product_id_(product_id),
-      product_name_(product_name),
-      serial_number_(serial_number),
-      bus_type_(bus_type),
-      report_descriptor_(report_descriptor) {
-  HidReportDescriptor descriptor_parser(report_descriptor_);
-  descriptor_parser.GetDetails(
-      &collections_, &has_report_id_, &max_input_report_size_,
-      &max_output_report_size_, &max_feature_report_size_);
+                             device::mojom::HidBusType bus_type,
+                             const std::vector<uint8_t> report_descriptor,
+                             std::string device_node)
+    : platform_device_id_(platform_device_id) {
+  std::vector<HidCollectionInfo> collections;
+  bool has_report_id;
+  size_t max_input_report_size;
+  size_t max_output_report_size;
+  size_t max_feature_report_size;
+
+  HidReportDescriptor descriptor_parser(report_descriptor);
+  descriptor_parser.GetDetails(&collections, &has_report_id,
+                               &max_input_report_size, &max_output_report_size,
+                               &max_feature_report_size);
+
+  device_ = device::mojom::HidDeviceInfo::New(
+      base::GenerateGUID(), vendor_id, product_id, product_name, serial_number,
+      bus_type, report_descriptor, collections, has_report_id,
+      max_input_report_size, max_output_report_size, max_feature_report_size,
+      device_node);
 }
 
 HidDeviceInfo::HidDeviceInfo(const HidPlatformDeviceId& platform_device_id,
@@ -36,23 +42,22 @@
                              uint16_t product_id,
                              const std::string& product_name,
                              const std::string& serial_number,
-                             HidBusType bus_type,
+                             device::mojom::HidBusType bus_type,
                              const HidCollectionInfo& collection,
                              size_t max_input_report_size,
                              size_t max_output_report_size,
                              size_t max_feature_report_size)
-    : guid_(base::GenerateGUID()),
-      platform_device_id_(platform_device_id),
-      vendor_id_(vendor_id),
-      product_id_(product_id),
-      product_name_(product_name),
-      serial_number_(serial_number),
-      bus_type_(bus_type),
-      max_input_report_size_(max_input_report_size),
-      max_output_report_size_(max_output_report_size),
-      max_feature_report_size_(max_feature_report_size) {
-  collections_.push_back(collection);
-  has_report_id_ = !collection.report_ids.empty();
+    : platform_device_id_(platform_device_id) {
+  std::vector<HidCollectionInfo> collections;
+  collections.push_back(collection);
+  bool has_report_id = !collection.report_ids.empty();
+
+  std::vector<uint8_t> report_descriptor;
+  device_ = device::mojom::HidDeviceInfo::New(
+      base::GenerateGUID(), vendor_id, product_id, product_name, serial_number,
+      bus_type, report_descriptor, collections, has_report_id,
+      max_input_report_size, max_output_report_size, max_feature_report_size,
+      "");
 }
 
 HidDeviceInfo::~HidDeviceInfo() {}
diff --git a/device/hid/hid_device_info.h b/device/hid/hid_device_info.h
index 8e856b59..632bbeb 100644
--- a/device/hid/hid_device_info.h
+++ b/device/hid/hid_device_info.h
@@ -15,14 +15,10 @@
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
 #include "device/hid/hid_collection_info.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 
 namespace device {
 
-enum HidBusType {
-  kHIDBusTypeUSB = 0,
-  kHIDBusTypeBluetooth = 1,
-};
-
 #if defined(OS_MACOSX)
 typedef uint64_t HidPlatformDeviceId;
 #else
@@ -36,44 +32,54 @@
                 uint16_t product_id,
                 const std::string& product_name,
                 const std::string& serial_number,
-                HidBusType bus_type,
-                const std::vector<uint8_t> report_descriptor);
+                device::mojom::HidBusType bus_type,
+                const std::vector<uint8_t> report_descriptor,
+                std::string device_node = "");
 
   HidDeviceInfo(const HidPlatformDeviceId& platform_device_id,
                 uint16_t vendor_id,
                 uint16_t product_id,
                 const std::string& product_name,
                 const std::string& serial_number,
-                HidBusType bus_type,
+                device::mojom::HidBusType bus_type,
                 const HidCollectionInfo& collection,
                 size_t max_input_report_size,
                 size_t max_output_report_size,
                 size_t max_feature_report_size);
 
+  const device::mojom::HidDeviceInfoPtr& device() { return device_; }
+
   // Device identification.
-  const std::string& device_guid() const { return guid_; }
+  const std::string& device_guid() const { return device_->guid; }
   const HidPlatformDeviceId& platform_device_id() const {
     return platform_device_id_;
   }
-  uint16_t vendor_id() const { return vendor_id_; }
-  uint16_t product_id() const { return product_id_; }
-  const std::string& product_name() const { return product_name_; }
-  const std::string& serial_number() const { return serial_number_; }
-  HidBusType bus_type() const { return bus_type_; }
+  uint16_t vendor_id() const { return device_->vendor_id; }
+  uint16_t product_id() const { return device_->product_id; }
+  const std::string& product_name() const { return device_->product_name; }
+  const std::string& serial_number() const { return device_->serial_number; }
+  device::mojom::HidBusType bus_type() const { return device_->bus_type; }
 
   // Top-Level Collections information.
   const std::vector<HidCollectionInfo>& collections() const {
-    return collections_;
+    return device_->collections;
   }
-  bool has_report_id() const { return has_report_id_; };
-  size_t max_input_report_size() const { return max_input_report_size_; }
-  size_t max_output_report_size() const { return max_output_report_size_; }
-  size_t max_feature_report_size() const { return max_feature_report_size_; }
+  bool has_report_id() const { return device_->has_report_id; };
+  size_t max_input_report_size() const {
+    return device_->max_input_report_size;
+  }
+  size_t max_output_report_size() const {
+    return device_->max_output_report_size;
+  }
+  size_t max_feature_report_size() const {
+    return device_->max_feature_report_size;
+  }
 
   // The raw HID report descriptor is not available on Windows.
   const std::vector<uint8_t>& report_descriptor() const {
-    return report_descriptor_;
+    return device_->report_descriptor;
   }
+  const std::string& device_node() const { return device_->device_node; }
 
  protected:
   virtual ~HidDeviceInfo();
@@ -81,22 +87,8 @@
  private:
   friend class base::RefCountedThreadSafe<HidDeviceInfo>;
 
-  // Device identification for client.
-  std::string guid_;
   HidPlatformDeviceId platform_device_id_;
-  uint16_t vendor_id_;
-  uint16_t product_id_;
-  std::string product_name_;
-  std::string serial_number_;
-  HidBusType bus_type_;
-  std::vector<uint8_t> report_descriptor_;
-
-  // Top-Level Collections information.
-  std::vector<HidCollectionInfo> collections_;
-  bool has_report_id_;
-  size_t max_input_report_size_;
-  size_t max_output_report_size_;
-  size_t max_feature_report_size_;
+  device::mojom::HidDeviceInfoPtr device_;
 
   DISALLOW_COPY_AND_ASSIGN(HidDeviceInfo);
 };
diff --git a/device/hid/hid_device_info_linux.cc b/device/hid/hid_device_info_linux.cc
deleted file mode 100644
index a3e53bd..0000000
--- a/device/hid/hid_device_info_linux.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "hid_device_info_linux.h"
-
-namespace device {
-
-HidDeviceInfoLinux::HidDeviceInfoLinux(
-    const HidPlatformDeviceId& platform_device_id,
-    const std::string& device_node,
-    uint16_t vendor_id,
-    uint16_t product_id,
-    const std::string& product_name,
-    const std::string& serial_number,
-    HidBusType bus_type,
-    const std::vector<uint8_t> report_descriptor)
-    : HidDeviceInfo(platform_device_id,
-                    vendor_id,
-                    product_id,
-                    product_name,
-                    serial_number,
-                    bus_type,
-                    report_descriptor),
-      device_node_(device_node) {}
-
-HidDeviceInfoLinux::~HidDeviceInfoLinux() {
-}
-
-}  // namespace device
diff --git a/device/hid/hid_device_info_linux.h b/device/hid/hid_device_info_linux.h
deleted file mode 100644
index 86839f5..0000000
--- a/device/hid/hid_device_info_linux.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef DEVICE_HID_HID_DEVICE_INFO_LINUX_H_
-#define DEVICE_HID_HID_DEVICE_INFO_LINUX_H_
-
-#include <stdint.h>
-
-#include "device/hid/hid_device_info.h"
-
-namespace device {
-
-class HidDeviceInfoLinux : public HidDeviceInfo {
- public:
-  HidDeviceInfoLinux(const HidPlatformDeviceId& platform_device_id,
-                     const std::string& device_node,
-                     uint16_t vendor_id,
-                     uint16_t product_id,
-                     const std::string& product_name,
-                     const std::string& serial_number,
-                     HidBusType bus_type,
-                     const std::vector<uint8_t> report_descriptor);
-
-  const std::string& device_node() const { return device_node_; }
-
- private:
-  ~HidDeviceInfoLinux() override;
-
-  std::string device_node_;
-};
-
-}  // namespace device
-
-#endif  // DEVICE_HID_HID_DEVICE_INFO_LINUX_H_
diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc
index 325cc1b..8f3b40a 100644
--- a/device/hid/hid_service.cc
+++ b/device/hid/hid_service.cc
@@ -25,12 +25,10 @@
 namespace device {
 
 void HidService::Observer::OnDeviceAdded(
-    scoped_refptr<HidDeviceInfo> device_info) {
-}
+    device::mojom::HidDeviceInfoPtr device_info) {}
 
 void HidService::Observer::OnDeviceRemoved(
-    scoped_refptr<HidDeviceInfo> device_info) {
-}
+    device::mojom::HidDeviceInfoPtr device_info) {}
 
 // static
 constexpr base::TaskTraits HidService::kBlockingTaskTraits;
@@ -67,16 +65,6 @@
   observer_list_.RemoveObserver(observer);
 }
 
-scoped_refptr<HidDeviceInfo> HidService::GetDeviceInfo(
-    const std::string& device_guid) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DeviceMap::const_iterator it = devices_.find(device_guid);
-  if (it == devices_.end()) {
-    return nullptr;
-  }
-  return it->second;
-}
-
 HidService::HidService() = default;
 
 HidService::~HidService() {
@@ -101,7 +89,7 @@
 
     if (enumeration_ready_) {
       for (auto& observer : observer_list_)
-        observer.OnDeviceAdded(device_info);
+        observer.OnDeviceAdded(device_info->device()->Clone());
     }
   }
 }
@@ -115,10 +103,10 @@
                   << "'";
     DCHECK(base::ContainsKey(devices_, device_guid));
 
-    scoped_refptr<HidDeviceInfo> device = devices_[device_guid];
+    scoped_refptr<HidDeviceInfo> device_info = devices_[device_guid];
     if (enumeration_ready_) {
       for (auto& observer : observer_list_)
-        observer.OnDeviceRemoved(device);
+        observer.OnDeviceRemoved(device_info->device()->Clone());
     }
     devices_.erase(device_guid);
   }
@@ -128,14 +116,16 @@
   DCHECK(enumeration_ready_);
   DCHECK(!pending_enumerations_.empty());
 
-  std::vector<scoped_refptr<HidDeviceInfo>> devices;
-  for (const auto& map_entry : devices_)
-    devices.push_back(map_entry.second);
-
   std::vector<GetDevicesCallback> callbacks;
   callbacks.swap(pending_enumerations_);
-  for (const auto& callback : callbacks)
-    callback.Run(devices);
+
+  // Clone and pass device::mojom::HidDeviceInfoPtr vector for each clients.
+  for (const auto& callback : callbacks) {
+    std::vector<device::mojom::HidDeviceInfoPtr> devices;
+    for (const auto& map_entry : devices_)
+      devices.push_back(map_entry.second->device()->Clone());
+    callback.Run(std::move(devices));
+  }
 }
 
 void HidService::FirstEnumerationComplete() {
diff --git a/device/hid/hid_service.h b/device/hid/hid_service.h
index 7cc9454f..906db27 100644
--- a/device/hid/hid_service.h
+++ b/device/hid/hid_service.h
@@ -20,6 +20,7 @@
 #include "base/task_scheduler/task_traits.h"
 #include "base/threading/thread_checker.h"
 #include "device/hid/hid_device_info.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 
 namespace device {
 
@@ -38,15 +39,15 @@
   // the observer immediately after calling the GetDevicesCallback.
   class Observer {
    public:
-    virtual void OnDeviceAdded(scoped_refptr<HidDeviceInfo> info);
+    virtual void OnDeviceAdded(device::mojom::HidDeviceInfoPtr info);
     // Notifies all observers that a device is being removed, called before
     // removing the device from HidService. Observers should not depend on the
     // order in which they are notified of the OnDeviceRemove event.
-    virtual void OnDeviceRemoved(scoped_refptr<HidDeviceInfo> info);
+    virtual void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr info);
   };
 
   using GetDevicesCallback =
-      base::Callback<void(const std::vector<scoped_refptr<HidDeviceInfo>>&)>;
+      base::Callback<void(std::vector<device::mojom::HidDeviceInfoPtr>)>;
   using ConnectCallback =
       base::Callback<void(scoped_refptr<HidConnection> connection)>;
 
@@ -66,9 +67,6 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  scoped_refptr<HidDeviceInfo> GetDeviceInfo(
-      const std::string& device_guid) const;
-
   // Opens a connection to a device. The callback will be run with null on
   // failure.
   virtual void Connect(const std::string& device_guid,
diff --git a/device/hid/hid_service_linux.cc b/device/hid/hid_service_linux.cc
index ede6113..1e26493 100644
--- a/device/hid/hid_service_linux.cc
+++ b/device/hid/hid_service_linux.cc
@@ -30,7 +30,6 @@
 #include "build/build_config.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/hid/hid_connection_linux.h"
-#include "device/hid/hid_device_info_linux.h"
 #include "device/udev_linux/scoped_udev.h"
 #include "device/udev_linux/udev_watcher.h"
 
@@ -53,7 +52,7 @@
 }  // namespace
 
 struct HidServiceLinux::ConnectParams {
-  ConnectParams(scoped_refptr<HidDeviceInfoLinux> device_info,
+  ConnectParams(scoped_refptr<HidDeviceInfo> device_info,
                 const ConnectCallback& callback)
       : device_info(std::move(device_info)),
         callback(callback),
@@ -62,7 +61,7 @@
             base::CreateSequencedTaskRunnerWithTraits(kBlockingTaskTraits)) {}
   ~ConnectParams() {}
 
-  scoped_refptr<HidDeviceInfoLinux> device_info;
+  scoped_refptr<HidDeviceInfo> device_info;
   ConnectCallback callback;
   scoped_refptr<base::SequencedTaskRunner> task_runner;
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
@@ -155,12 +154,13 @@
     if (!base::ReadFileToString(report_descriptor_path, &report_descriptor_str))
       return;
 
-    scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfoLinux(
-        platform_device_id, device_node, vendor_id, product_id, product_name,
-        serial_number,
-        kHIDBusTypeUSB,  // TODO(reillyg): Detect Bluetooth. crbug.com/443335
+    scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfo(
+        platform_device_id, vendor_id, product_id, product_name, serial_number,
+        // TODO(reillyg): Detect Bluetooth. crbug.com/443335
+        device::mojom::HidBusType::kHIDBusTypeUSB,
         std::vector<uint8_t>(report_descriptor_str.begin(),
-                             report_descriptor_str.end())));
+                             report_descriptor_str.end()),
+        device_node));
 
     task_runner_->PostTask(FROM_HERE, base::Bind(&HidServiceLinux::AddDevice,
                                                  service_, device_info));
@@ -214,8 +214,7 @@
         FROM_HERE, base::Bind(callback, nullptr));
     return;
   }
-  scoped_refptr<HidDeviceInfoLinux> device_info =
-      static_cast<HidDeviceInfoLinux*>(map_entry->second.get());
+  scoped_refptr<HidDeviceInfo> device_info = map_entry->second;
 
   auto params = std::make_unique<ConnectParams>(device_info, callback);
 
diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc
index a394ec2..7ecedb7 100644
--- a/device/hid/hid_service_mac.cc
+++ b/device/hid/hid_service_mac.cc
@@ -85,8 +85,8 @@
       GetIntProperty(service, CFSTR(kIOHIDProductIDKey)),
       GetStringProperty(service, CFSTR(kIOHIDProductKey)),
       GetStringProperty(service, CFSTR(kIOHIDSerialNumberKey)),
-      kHIDBusTypeUSB,  // TODO(reillyg): Detect Bluetooth. crbug.com/443335
-      report_descriptor);
+      // TODO(reillyg): Detect Bluetooth. crbug.com/443335
+      device::mojom::HidBusType::kHIDBusTypeUSB, report_descriptor);
 }
 
 }  // namespace
diff --git a/device/hid/hid_service_unittest.cc b/device/hid/hid_service_unittest.cc
index 4da84d48..17ac871 100644
--- a/device/hid/hid_service_unittest.cc
+++ b/device/hid/hid_service_unittest.cc
@@ -6,6 +6,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/test/test_device_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,7 +26,7 @@
 };
 
 void OnGetDevices(const base::Closure& quit_closure,
-                  const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
+                  std::vector<device::mojom::HidDeviceInfoPtr> devices) {
   // Since there's no guarantee that any devices are connected at the moment
   // this test doesn't assume anything about the result but it at least verifies
   // that devices can be enumerated without the application crashing.
diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc
index 9be84112..0357ea7 100644
--- a/device/hid/hid_service_win.cc
+++ b/device/hid/hid_service_win.cc
@@ -260,9 +260,9 @@
   scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfo(
       device_path, attrib.VendorID, attrib.ProductID, product_name,
       serial_number,
-      kHIDBusTypeUSB,  // TODO(reillyg): Detect Bluetooth. crbug.com/443335
-      collection_info, max_input_report_size, max_output_report_size,
-      max_feature_report_size));
+      // TODO(reillyg): Detect Bluetooth. crbug.com/443335
+      device::mojom::HidBusType::kHIDBusTypeUSB, collection_info,
+      max_input_report_size, max_output_report_size, max_feature_report_size));
 
   HidD_FreePreparsedData(preparsed_data);
   task_runner->PostTask(
diff --git a/device/hid/hid_usage_and_page.h b/device/hid/hid_usage_and_page.h
index 1015854..2ed9113 100644
--- a/device/hid/hid_usage_and_page.h
+++ b/device/hid/hid_usage_and_page.h
@@ -119,6 +119,7 @@
     kGenericDesktopSystemDisplaySwap = 0xb6,
   };
 
+  HidUsageAndPage() {}
   HidUsageAndPage(uint16_t usage, Page usage_page)
       : usage(usage), usage_page(usage_page) {}
   ~HidUsageAndPage() {}
diff --git a/device/hid/public/interfaces/BUILD.gn b/device/hid/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..3791874
--- /dev/null
+++ b/device/hid/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "hid.mojom",
+  ]
+}
diff --git a/device/hid/public/interfaces/OWNERS b/device/hid/public/interfaces/OWNERS
new file mode 100644
index 0000000..51ec2e6
--- /dev/null
+++ b/device/hid/public/interfaces/OWNERS
@@ -0,0 +1,8 @@
+# Changes to Mojo interfaces require a security review to avoid
+# introducing new sandbox escapes.
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/device/hid/public/interfaces/hid.mojom b/device/hid/public/interfaces/hid.mojom
new file mode 100644
index 0000000..25cf4cdb
--- /dev/null
+++ b/device/hid/public/interfaces/hid.mojom
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module device.mojom;
+
+enum HidBusType {
+  kHIDBusTypeUSB = 0,
+  kHIDBusTypeBluetooth = 1,
+};
+
+enum HidPage {
+    PageUndefined = 0x00,
+    PageGenericDesktop = 0x01,
+    PageSimulation = 0x02,
+    PageVirtualReality = 0x03,
+    PageSport = 0x04,
+    PageGame = 0x05,
+    PageKeyboard = 0x07,
+    PageLed = 0x08,
+    PageButton = 0x09,
+    PageOrdinal = 0x0A,
+    PageTelephony = 0x0B,
+    PageConsumer = 0x0C,
+    PageDigitizer = 0x0D,
+    PagePidPage = 0x0F,
+    PageUnicode = 0x10,
+    PageAlphanumericDisplay = 0x14,
+    PageMedicalInstruments = 0x40,
+    PageMonitor0 = 0x80,
+    PageMonitor1 = 0x81,
+    PageMonitor2 = 0x82,
+    PageMonitor3 = 0x83,
+    PagePower0 = 0x84,
+    PagePower1 = 0x85,
+    PagePower2 = 0x86,
+    PagePower3 = 0x87,
+    PageBarCodeScanner = 0x8C,
+    PageScale = 0x8D,
+    PageMagneticStripeReader = 0x8E,
+    PageReservedPointOfSale = 0x8F,
+    PageCameraControl = 0x90,
+    PageArcade = 0x91,
+    PageVendor = 0xFF00,
+    PageMediaCenter = 0xFFBC
+};
+
+struct HidUsageAndPage {
+  uint16 usage;
+  HidPage usage_page;
+};
+
+struct HidCollectionInfo {
+  HidUsageAndPage usage;
+  array<int32> report_ids;
+};
+
+struct HidDeviceInfo {
+  string guid;
+  uint16 vendor_id;
+  uint16 product_id;
+  string product_name;
+  string serial_number;
+  HidBusType bus_type;
+  array<uint8> report_descriptor;
+  array<HidCollectionInfo> collections;
+  bool has_report_id;
+  uint64 max_input_report_size;
+  uint64 max_output_report_size;
+  uint64 max_feature_report_size;
+  string device_node;
+};
diff --git a/device/hid/public/interfaces/hid.typemap b/device/hid/public/interfaces/hid.typemap
new file mode 100644
index 0000000..4ef18a05
--- /dev/null
+++ b/device/hid/public/interfaces/hid.typemap
@@ -0,0 +1,22 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//device/hid/public/interfaces/hid.mojom"
+public_headers = [
+  "//device/hid/hid_collection_info.h",
+  "//device/hid/hid_usage_and_page.h",
+]
+traits_headers = [ "//device/hid/public/interfaces/hid_struct_traits.h" ]
+sources = [
+  "//device/hid/public/interfaces/hid_struct_traits.cc",
+]
+deps = [
+  "//mojo/public/cpp/bindings",
+]
+
+type_mappings = [
+  "device.mojom.HidPage=device::HidUsageAndPage::Page",
+  "device.mojom.HidUsageAndPage=device::HidUsageAndPage",
+  "device.mojom.HidCollectionInfo=device::HidCollectionInfo",
+]
diff --git a/device/hid/public/interfaces/hid_struct_traits.cc b/device/hid/public/interfaces/hid_struct_traits.cc
new file mode 100644
index 0000000..6c56ca8
--- /dev/null
+++ b/device/hid/public/interfaces/hid_struct_traits.cc
@@ -0,0 +1,226 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/public/interfaces/hid_struct_traits.h"
+
+namespace mojo {
+
+// static
+device::mojom::HidPage
+EnumTraits<device::mojom::HidPage, device::HidUsageAndPage::Page>::ToMojom(
+    device::HidUsageAndPage::Page input) {
+  switch (input) {
+    case device::HidUsageAndPage::Page::kPageUndefined:
+      return device::mojom::HidPage::PageUndefined;
+    case device::HidUsageAndPage::Page::kPageGenericDesktop:
+      return device::mojom::HidPage::PageGenericDesktop;
+    case device::HidUsageAndPage::Page::kPageSimulation:
+      return device::mojom::HidPage::PageSimulation;
+    case device::HidUsageAndPage::Page::kPageVirtualReality:
+      return device::mojom::HidPage::PageVirtualReality;
+    case device::HidUsageAndPage::Page::kPageSport:
+      return device::mojom::HidPage::PageSport;
+    case device::HidUsageAndPage::Page::kPageGame:
+      return device::mojom::HidPage::PageGame;
+    case device::HidUsageAndPage::Page::kPageKeyboard:
+      return device::mojom::HidPage::PageKeyboard;
+    case device::HidUsageAndPage::Page::kPageLed:
+      return device::mojom::HidPage::PageLed;
+    case device::HidUsageAndPage::Page::kPageButton:
+      return device::mojom::HidPage::PageButton;
+    case device::HidUsageAndPage::Page::kPageOrdinal:
+      return device::mojom::HidPage::PageOrdinal;
+    case device::HidUsageAndPage::Page::kPageTelephony:
+      return device::mojom::HidPage::PageTelephony;
+    case device::HidUsageAndPage::Page::kPageConsumer:
+      return device::mojom::HidPage::PageConsumer;
+    case device::HidUsageAndPage::Page::kPageDigitizer:
+      return device::mojom::HidPage::PageDigitizer;
+    case device::HidUsageAndPage::Page::kPagePidPage:
+      return device::mojom::HidPage::PagePidPage;
+    case device::HidUsageAndPage::Page::kPageUnicode:
+      return device::mojom::HidPage::PageUnicode;
+    case device::HidUsageAndPage::Page::kPageAlphanumericDisplay:
+      return device::mojom::HidPage::PageAlphanumericDisplay;
+    case device::HidUsageAndPage::Page::kPageMedicalInstruments:
+      return device::mojom::HidPage::PageMedicalInstruments;
+    case device::HidUsageAndPage::Page::kPageMonitor0:
+      return device::mojom::HidPage::PageMonitor0;
+    case device::HidUsageAndPage::Page::kPageMonitor1:
+      return device::mojom::HidPage::PageMonitor1;
+    case device::HidUsageAndPage::Page::kPageMonitor2:
+      return device::mojom::HidPage::PageMonitor2;
+    case device::HidUsageAndPage::Page::kPageMonitor3:
+      return device::mojom::HidPage::PageMonitor3;
+    case device::HidUsageAndPage::Page::kPagePower0:
+      return device::mojom::HidPage::PagePower0;
+    case device::HidUsageAndPage::Page::kPagePower1:
+      return device::mojom::HidPage::PagePower1;
+    case device::HidUsageAndPage::Page::kPagePower2:
+      return device::mojom::HidPage::PagePower2;
+    case device::HidUsageAndPage::Page::kPagePower3:
+      return device::mojom::HidPage::PagePower3;
+    case device::HidUsageAndPage::Page::kPageBarCodeScanner:
+      return device::mojom::HidPage::PageBarCodeScanner;
+    case device::HidUsageAndPage::Page::kPageScale:
+      return device::mojom::HidPage::PageScale;
+    case device::HidUsageAndPage::Page::kPageMagneticStripeReader:
+      return device::mojom::HidPage::PageMagneticStripeReader;
+    case device::HidUsageAndPage::Page::kPageReservedPointOfSale:
+      return device::mojom::HidPage::PageReservedPointOfSale;
+    case device::HidUsageAndPage::Page::kPageCameraControl:
+      return device::mojom::HidPage::PageCameraControl;
+    case device::HidUsageAndPage::Page::kPageArcade:
+      return device::mojom::HidPage::PageArcade;
+    case device::HidUsageAndPage::Page::kPageVendor:
+      return device::mojom::HidPage::PageVendor;
+    case device::HidUsageAndPage::Page::kPageMediaCenter:
+      return device::mojom::HidPage::PageMediaCenter;
+  }
+
+  NOTREACHED();
+  return device::mojom::HidPage::PageUndefined;
+}
+
+// static
+bool EnumTraits<device::mojom::HidPage, device::HidUsageAndPage::Page>::
+    FromMojom(device::mojom::HidPage input,
+              device::HidUsageAndPage::Page* output) {
+  switch (input) {
+    case device::mojom::HidPage::PageUndefined:
+      *output = device::HidUsageAndPage::Page::kPageUndefined;
+      return true;
+    case device::mojom::HidPage::PageGenericDesktop:
+      *output = device::HidUsageAndPage::Page::kPageGenericDesktop;
+      return true;
+    case device::mojom::HidPage::PageSimulation:
+      *output = device::HidUsageAndPage::Page::kPageSimulation;
+      return true;
+    case device::mojom::HidPage::PageVirtualReality:
+      *output = device::HidUsageAndPage::Page::kPageVirtualReality;
+      return true;
+    case device::mojom::HidPage::PageSport:
+      *output = device::HidUsageAndPage::Page::kPageSport;
+      return true;
+    case device::mojom::HidPage::PageGame:
+      *output = device::HidUsageAndPage::Page::kPageGame;
+      return true;
+    case device::mojom::HidPage::PageKeyboard:
+      *output = device::HidUsageAndPage::Page::kPageKeyboard;
+      return true;
+    case device::mojom::HidPage::PageLed:
+      *output = device::HidUsageAndPage::Page::kPageLed;
+      return true;
+    case device::mojom::HidPage::PageButton:
+      *output = device::HidUsageAndPage::Page::kPageButton;
+      return true;
+    case device::mojom::HidPage::PageOrdinal:
+      *output = device::HidUsageAndPage::Page::kPageOrdinal;
+      return true;
+    case device::mojom::HidPage::PageTelephony:
+      *output = device::HidUsageAndPage::Page::kPageTelephony;
+      return true;
+    case device::mojom::HidPage::PageConsumer:
+      *output = device::HidUsageAndPage::Page::kPageConsumer;
+      return true;
+    case device::mojom::HidPage::PageDigitizer:
+      *output = device::HidUsageAndPage::Page::kPageDigitizer;
+      return true;
+    case device::mojom::HidPage::PagePidPage:
+      *output = device::HidUsageAndPage::Page::kPagePidPage;
+      return true;
+    case device::mojom::HidPage::PageUnicode:
+      *output = device::HidUsageAndPage::Page::kPageUnicode;
+      return true;
+    case device::mojom::HidPage::PageAlphanumericDisplay:
+      *output = device::HidUsageAndPage::Page::kPageAlphanumericDisplay;
+      return true;
+    case device::mojom::HidPage::PageMedicalInstruments:
+      *output = device::HidUsageAndPage::Page::kPageMedicalInstruments;
+      return true;
+    case device::mojom::HidPage::PageMonitor0:
+      *output = device::HidUsageAndPage::Page::kPageMonitor0;
+      return true;
+    case device::mojom::HidPage::PageMonitor1:
+      *output = device::HidUsageAndPage::Page::kPageMonitor1;
+      return true;
+    case device::mojom::HidPage::PageMonitor2:
+      *output = device::HidUsageAndPage::Page::kPageMonitor2;
+      return true;
+    case device::mojom::HidPage::PageMonitor3:
+      *output = device::HidUsageAndPage::Page::kPageMonitor3;
+      return true;
+    case device::mojom::HidPage::PagePower0:
+      *output = device::HidUsageAndPage::Page::kPagePower0;
+      return true;
+    case device::mojom::HidPage::PagePower1:
+      *output = device::HidUsageAndPage::Page::kPagePower1;
+      return true;
+    case device::mojom::HidPage::PagePower2:
+      *output = device::HidUsageAndPage::Page::kPagePower2;
+      return true;
+    case device::mojom::HidPage::PagePower3:
+      *output = device::HidUsageAndPage::Page::kPagePower3;
+      return true;
+    case device::mojom::HidPage::PageBarCodeScanner:
+      *output = device::HidUsageAndPage::Page::kPageBarCodeScanner;
+      return true;
+    case device::mojom::HidPage::PageScale:
+      *output = device::HidUsageAndPage::Page::kPageScale;
+      return true;
+    case device::mojom::HidPage::PageMagneticStripeReader:
+      *output = device::HidUsageAndPage::Page::kPageMagneticStripeReader;
+      return true;
+    case device::mojom::HidPage::PageReservedPointOfSale:
+      *output = device::HidUsageAndPage::Page::kPageReservedPointOfSale;
+      return true;
+    case device::mojom::HidPage::PageCameraControl:
+      *output = device::HidUsageAndPage::Page::kPageCameraControl;
+      return true;
+    case device::mojom::HidPage::PageArcade:
+      *output = device::HidUsageAndPage::Page::kPageArcade;
+      return true;
+    case device::mojom::HidPage::PageVendor:
+      *output = device::HidUsageAndPage::Page::kPageVendor;
+      return true;
+    case device::mojom::HidPage::PageMediaCenter:
+      *output = device::HidUsageAndPage::Page::kPageMediaCenter;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// static
+bool StructTraits<
+    device::mojom::HidUsageAndPageDataView,
+    device::HidUsageAndPage>::Read(device::mojom::HidUsageAndPageDataView data,
+                                   device::HidUsageAndPage* out) {
+  out->usage = data.usage();
+
+  if (!data.ReadUsagePage(&out->usage_page))
+    return false;
+
+  return true;
+}
+
+// static
+bool StructTraits<device::mojom::HidCollectionInfoDataView,
+                  device::HidCollectionInfo>::
+    Read(device::mojom::HidCollectionInfoDataView data,
+         device::HidCollectionInfo* out) {
+  if (!data.ReadUsage(&out->usage))
+    return false;
+
+  std::vector<int> vec;
+  if (!data.ReadReportIds(&vec))
+    return false;
+
+  out->report_ids.clear();
+  out->report_ids.insert(vec.begin(), vec.end());
+  return true;
+}
+
+}  // namespace mojo
diff --git a/device/hid/public/interfaces/hid_struct_traits.h b/device/hid/public/interfaces/hid_struct_traits.h
new file mode 100644
index 0000000..ff46a4f4
--- /dev/null
+++ b/device/hid/public/interfaces/hid_struct_traits.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_PUBLIC_INTERFACES_HID_STRUCT_TRAITS_H_
+#define DEVICE_HID_PUBLIC_INTERFACES_HID_STRUCT_TRAITS_H_
+
+#include <stddef.h>
+
+#include "device/hid/hid_collection_info.h"
+#include "device/hid/hid_usage_and_page.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
+#include "mojo/public/cpp/bindings/array_traits_stl.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<device::mojom::HidPage, device::HidUsageAndPage::Page> {
+  static device::mojom::HidPage ToMojom(device::HidUsageAndPage::Page input);
+  static bool FromMojom(device::mojom::HidPage input,
+                        device::HidUsageAndPage::Page* output);
+};
+
+template <>
+struct StructTraits<device::mojom::HidUsageAndPageDataView,
+                    device::HidUsageAndPage> {
+  static uint16_t usage(const device::HidUsageAndPage& r) { return r.usage; }
+  static const device::HidUsageAndPage::Page& usage_page(
+      const device::HidUsageAndPage& r) {
+    return r.usage_page;
+  }
+  static bool Read(device::mojom::HidUsageAndPageDataView data,
+                   device::HidUsageAndPage* out);
+};
+
+template <>
+struct StructTraits<device::mojom::HidCollectionInfoDataView,
+                    device::HidCollectionInfo> {
+  static const device::HidUsageAndPage& usage(
+      const device::HidCollectionInfo& r) {
+    return r.usage;
+  }
+
+  static const std::set<int>& report_ids(const device::HidCollectionInfo& r) {
+    return r.report_ids;
+  }
+
+  static bool Read(device::mojom::HidCollectionInfoDataView data,
+                   device::HidCollectionInfo* out);
+};
+
+}  // namespace mojo
+
+#endif  // DEVICE_HID_PUBLIC_INTERFACES_HID_STRUCT_TRAITS_H_
diff --git a/device/hid/public/interfaces/typemaps.gni b/device/hid/public/interfaces/typemaps.gni
new file mode 100644
index 0000000..0a40a67
--- /dev/null
+++ b/device/hid/public/interfaces/typemaps.gni
@@ -0,0 +1,5 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+typemaps = [ "//device/hid/public/interfaces/hid.typemap" ]
diff --git a/device/u2f/u2f_hid_device.cc b/device/u2f/u2f_hid_device.cc
index 4e625673..e418667 100644
--- a/device/u2f/u2f_hid_device.cc
+++ b/device/u2f/u2f_hid_device.cc
@@ -20,12 +20,11 @@
 static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests";
 }  // namespace switches
 
-U2fHidDevice::U2fHidDevice(scoped_refptr<HidDeviceInfo> device_info)
+U2fHidDevice::U2fHidDevice(device::mojom::HidDeviceInfoPtr device_info)
     : U2fDevice(),
       state_(State::INIT),
-      device_info_(device_info),
-      weak_factory_(this) {
-}
+      device_info_(std::move(device_info)),
+      weak_factory_(this) {}
 
 U2fHidDevice::~U2fHidDevice() {
   // Cleanup connection
@@ -85,7 +84,7 @@
 void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) {
   HidService* hid_service = DeviceClient::Get()->GetHidService();
 
-  hid_service->Connect(device_info_->device_guid(), callback);
+  hid_service->Connect(device_info_->guid, callback);
 }
 
 void U2fHidDevice::OnConnect(std::unique_ptr<U2fApduCommand> command,
@@ -332,7 +331,7 @@
 
 std::string U2fHidDevice::GetId() {
   std::ostringstream id("hid:", std::ios::ate);
-  id << device_info_->device_guid();
+  id << device_info_->guid;
   return id.str();
 }
 
diff --git a/device/u2f/u2f_hid_device.h b/device/u2f/u2f_hid_device.h
index 64bc0f2..f84030c 100644
--- a/device/u2f/u2f_hid_device.h
+++ b/device/u2f/u2f_hid_device.h
@@ -9,6 +9,7 @@
 
 #include "base/cancelable_callback.h"
 #include "device/hid/hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "u2f_device.h"
 
 namespace net {
@@ -19,11 +20,10 @@
 
 class U2fMessage;
 class HidConnection;
-class HidDeviceInfo;
 
 class U2fHidDevice : public U2fDevice {
  public:
-  U2fHidDevice(scoped_refptr<HidDeviceInfo>);
+  U2fHidDevice(device::mojom::HidDeviceInfoPtr);
   ~U2fHidDevice() final;
 
   // Send a U2f command to this device
@@ -96,7 +96,7 @@
   base::CancelableClosure timeout_callback_;
   std::list<std::pair<std::unique_ptr<U2fApduCommand>, DeviceCallback>>
       pending_transactions_;
-  scoped_refptr<HidDeviceInfo> device_info_;
+  device::mojom::HidDeviceInfoPtr device_info_;
   scoped_refptr<HidConnection> connection_;
   base::WeakPtrFactory<U2fHidDevice> weak_factory_;
 
diff --git a/device/u2f/u2f_hid_device_unittest.cc b/device/u2f/u2f_hid_device_unittest.cc
index fab33990..7d3d556 100644
--- a/device/u2f/u2f_hid_device_unittest.cc
+++ b/device/u2f/u2f_hid_device_unittest.cc
@@ -74,13 +74,13 @@
         run_loop_() {}
   ~U2fDeviceEnumerate() {}
 
-  void ReceivedCallback(
-      const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
+  void ReceivedCallback(std::vector<device::mojom::HidDeviceInfoPtr> devices) {
     std::list<std::unique_ptr<U2fHidDevice>> u2f_devices;
     filter_.SetUsagePage(0xf1d0);
-    for (auto device_info : devices) {
-      if (filter_.Matches(device_info))
-        u2f_devices.push_front(std::make_unique<U2fHidDevice>(device_info));
+    for (auto& device_info : devices) {
+      if (filter_.Matches(*device_info))
+        u2f_devices.push_front(
+            std::make_unique<U2fHidDevice>(std::move(device_info)));
     }
     devices_ = std::move(u2f_devices);
     closure_.Run();
@@ -220,9 +220,9 @@
   MockHidService* hid_service = client->hid_service();
   HidCollectionInfo c_info;
   c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
-  scoped_refptr<HidDeviceInfo> device0 =
-      new HidDeviceInfo(kTestDeviceId, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0);
+  scoped_refptr<HidDeviceInfo> device0 = new HidDeviceInfo(
+      kTestDeviceId, 0, 0, "Test Fido Device", "123FIDO",
+      device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0);
   hid_service->AddDevice(device0);
   hid_service->FirstEnumerationComplete();
   hid_service->GetDevices(callback.callback());
@@ -265,9 +265,9 @@
   MockHidService* hid_service = client->hid_service();
   HidCollectionInfo c_info;
   c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
-  scoped_refptr<HidDeviceInfo> device0 =
-      new HidDeviceInfo(kTestDeviceId, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0);
+  scoped_refptr<HidDeviceInfo> device0 = new HidDeviceInfo(
+      kTestDeviceId, 0, 0, "Test Fido Device", "123FIDO",
+      device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0);
   hid_service->AddDevice(device0);
   hid_service->FirstEnumerationComplete();
   hid_service->GetDevices(callback.callback());
diff --git a/device/u2f/u2f_request.cc b/device/u2f/u2f_request.cc
index efd470b..7fa5472 100644
--- a/device/u2f/u2f_request.cc
+++ b/device/u2f/u2f_request.cc
@@ -57,10 +57,11 @@
 
 void U2fRequest::OnEnumerate(
     HidService* hid_service,
-    const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
-  for (auto device_info : devices) {
-    if (filter_.Matches(device_info))
-      devices_.push_back(std::make_unique<U2fHidDevice>(device_info));
+    std::vector<device::mojom::HidDeviceInfoPtr> devices) {
+  for (auto& device_info : devices) {
+    if (filter_.Matches(*device_info))
+      devices_.push_back(
+          std::make_unique<U2fHidDevice>(std::move(device_info)));
   }
 
   hid_service_observer_.Add(hid_service);
@@ -69,21 +70,21 @@
   Transition();
 }
 
-void U2fRequest::OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) {
+void U2fRequest::OnDeviceAdded(device::mojom::HidDeviceInfoPtr device_info) {
   // Ignore non-U2F devices
-  if (!filter_.Matches(device_info))
+  if (!filter_.Matches(*device_info))
     return;
 
-  auto device = std::make_unique<U2fHidDevice>(device_info);
+  auto device = std::make_unique<U2fHidDevice>(std::move(device_info));
   AddDevice(std::move(device));
 }
 
-void U2fRequest::OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) {
+void U2fRequest::OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device_info) {
   // Ignore non-U2F devices
-  if (!filter_.Matches(device_info))
+  if (!filter_.Matches(*device_info))
     return;
 
-  auto device = std::make_unique<U2fHidDevice>(device_info);
+  auto device = std::make_unique<U2fHidDevice>(std::move(device_info));
 
   // Check if the active device was removed
   if (current_device_ && current_device_->GetId() == device->GetId()) {
diff --git a/device/u2f/u2f_request.h b/device/u2f/u2f_request.h
index 6ba2b09..49cec99 100644
--- a/device/u2f/u2f_request.h
+++ b/device/u2f/u2f_request.h
@@ -9,6 +9,7 @@
 #include "base/scoped_observer.h"
 #include "device/hid/hid_device_filter.h"
 #include "device/hid/hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "u2f_device.h"
 
 namespace device {
@@ -50,10 +51,10 @@
   void IterateDevice();
   void OnWaitComplete();
   void AddDevice(std::unique_ptr<U2fDevice> device);
-  void OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) override;
-  void OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) override;
+  void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device_info) override;
+  void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device_info) override;
   void OnEnumerate(HidService* hid_service,
-                   const std::vector<scoped_refptr<HidDeviceInfo>>& devices);
+                   std::vector<device::mojom::HidDeviceInfoPtr> devices);
 
   std::list<std::unique_ptr<U2fDevice>> devices_;
   std::list<std::unique_ptr<U2fDevice>> attempted_devices_;
diff --git a/device/u2f/u2f_request_unittest.cc b/device/u2f/u2f_request_unittest.cc
index dab7e92..03261a301 100644
--- a/device/u2f/u2f_request_unittest.cc
+++ b/device/u2f/u2f_request_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/test_io_thread.h"
 #include "device/base/mock_device_client.h"
 #include "device/hid/mock_hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/test/test_device_client.h"
 #include "device/test/usb_test_gadget.h"
 #include "device/u2f/u2f_hid_device.h"
@@ -87,9 +88,10 @@
   request.Enumerate();
 
   c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
-  scoped_refptr<HidDeviceInfo> u2f_device_0 = make_scoped_refptr(
-      new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  scoped_refptr<HidDeviceInfo> u2f_device_0 =
+      make_scoped_refptr(new HidDeviceInfo(
+          kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
+          device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0));
   hid_service->AddDevice(u2f_device_0);
 
   // Make sure the enumeration is finshed, so HidService is ready to send
@@ -98,16 +100,18 @@
   EXPECT_EQ(static_cast<size_t>(0), request.devices_.size());
 
   // Add one U2F device
-  scoped_refptr<HidDeviceInfo> u2f_device_1 = make_scoped_refptr(
-      new HidDeviceInfo(kTestDeviceId1, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  scoped_refptr<HidDeviceInfo> u2f_device_1 =
+      make_scoped_refptr(new HidDeviceInfo(
+          kTestDeviceId1, 0, 0, "Test Fido Device", "123FIDO",
+          device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0));
   hid_service->AddDevice(u2f_device_1);
   EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
 
   // Add one non-U2F device. Verify that it is not added to our device list.
-  scoped_refptr<HidDeviceInfo> other_device = make_scoped_refptr(
-      new HidDeviceInfo(kTestDeviceId2, 0, 0, "Other Device", "OtherDevice",
-                        kHIDBusTypeUSB, std::vector<uint8_t>()));
+  scoped_refptr<HidDeviceInfo> other_device =
+      make_scoped_refptr(new HidDeviceInfo(
+          kTestDeviceId2, 0, 0, "Other Device", "OtherDevice",
+          device::mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>()));
   hid_service->AddDevice(other_device);
   EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
 
@@ -126,14 +130,16 @@
   HidCollectionInfo c_info;
   // Add one U2F device and one non-U2f device
   c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
-  scoped_refptr<HidDeviceInfo> device0 = make_scoped_refptr(
-      new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0));
-  request.devices_.push_back(std::make_unique<U2fHidDevice>(device0));
-  scoped_refptr<HidDeviceInfo> device1 = make_scoped_refptr(
-      new HidDeviceInfo(kTestDeviceId1, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0));
-  request.devices_.push_back(std::make_unique<U2fHidDevice>(device1));
+  scoped_refptr<HidDeviceInfo> device0 = make_scoped_refptr(new HidDeviceInfo(
+      kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
+      device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0));
+  request.devices_.push_back(
+      std::make_unique<U2fHidDevice>(device0->device()->Clone()));
+  scoped_refptr<HidDeviceInfo> device1 = make_scoped_refptr(new HidDeviceInfo(
+      kTestDeviceId1, 0, 0, "Test Fido Device", "123FIDO",
+      device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0));
+  request.devices_.push_back(
+      std::make_unique<U2fHidDevice>(device1->device()->Clone()));
 
   // Move first device to current
   request.IterateDevice();
@@ -162,9 +168,10 @@
   // Add one U2F device
   HidCollectionInfo c_info;
   c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
-  scoped_refptr<HidDeviceInfo> u2f_device = make_scoped_refptr(
-      new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
-                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  scoped_refptr<HidDeviceInfo> u2f_device =
+      make_scoped_refptr(new HidDeviceInfo(
+          kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
+          device::mojom::HidBusType::kHIDBusTypeUSB, c_info, 64, 64, 0));
   hid_service->AddDevice(u2f_device);
   cb.WaitForCallback();
   EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc
index bfb10dd..b6dda62 100644
--- a/extensions/browser/api/device_permissions_manager.cc
+++ b/extensions/browser/api/device_permissions_manager.cc
@@ -17,8 +17,6 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "device/base/device_client.h"
-#include "device/hid/hid_device_info.h"
-#include "device/hid/hid_service.h"
 #include "device/usb/usb_device.h"
 #include "device/usb/usb_ids.h"
 #include "extensions/browser/api/hid/hid_device_manager.h"
@@ -33,7 +31,6 @@
 
 using content::BrowserContext;
 using content::BrowserThread;
-using device::HidDeviceInfo;
 using device::HidService;
 using device::UsbDevice;
 using device::UsbService;
@@ -279,14 +276,13 @@
 }
 
 DevicePermissionEntry::DevicePermissionEntry(
-    scoped_refptr<HidDeviceInfo> device)
-    : hid_device_(device),
+    const device::mojom::HidDeviceInfo& device)
+    : hid_device_guid_(device.guid),
       type_(Type::HID),
-      vendor_id_(device->vendor_id()),
-      product_id_(device->product_id()),
-      serial_number_(base::UTF8ToUTF16(device->serial_number())),
-      product_string_(base::UTF8ToUTF16(device->product_name())) {
-}
+      vendor_id_(device.vendor_id),
+      product_id_(device.product_id),
+      serial_number_(base::UTF8ToUTF16(device.serial_number)),
+      product_string_(base::UTF8ToUTF16(device.product_name)) {}
 
 DevicePermissionEntry::DevicePermissionEntry(
     Type type,
@@ -374,21 +370,20 @@
 }
 
 scoped_refptr<DevicePermissionEntry> DevicePermissions::FindHidDeviceEntry(
-    scoped_refptr<HidDeviceInfo> device) const {
-  const auto& ephemeral_device_entry =
-      ephemeral_hid_devices_.find(device.get());
+    const device::mojom::HidDeviceInfo& device) const {
+  const auto& ephemeral_device_entry = ephemeral_hid_devices_.find(device.guid);
   if (ephemeral_device_entry != ephemeral_hid_devices_.end()) {
     return ephemeral_device_entry->second;
   }
 
-  if (device->serial_number().empty()) {
+  if (device.serial_number.empty()) {
     return nullptr;
   }
 
-  base::string16 serial_number = base::UTF8ToUTF16(device->serial_number());
+  base::string16 serial_number = base::UTF8ToUTF16(device.serial_number);
   for (const auto& entry : entries_) {
-    if (entry->IsPersistent() && entry->vendor_id() == device->vendor_id() &&
-        entry->product_id() == device->product_id() &&
+    if (entry->IsPersistent() && entry->vendor_id() == device.vendor_id &&
+        entry->product_id() == device.product_id &&
         entry->serial_number() == serial_number) {
       return entry;
     }
@@ -563,7 +558,7 @@
 
 void DevicePermissionsManager::AllowHidDevice(
     const std::string& extension_id,
-    scoped_refptr<HidDeviceInfo> device) {
+    const device::mojom::HidDeviceInfo& device) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DevicePermissions* device_permissions = GetForExtension(extension_id);
 
@@ -582,12 +577,12 @@
     device_permissions->entries_.insert(device_entry);
     SaveDevicePermissionEntry(context_, extension_id, device_entry);
   } else if (!ContainsKey(device_permissions->ephemeral_hid_devices_,
-                          device.get())) {
+                          device.guid)) {
     // Non-persistent devices cannot be reliably identified when they are
     // reconnected so such devices are only remembered until disconnect.
     // Register an observer here so that this set doesn't grow undefinitely.
     device_permissions->entries_.insert(device_entry);
-    device_permissions->ephemeral_hid_devices_[device.get()] = device_entry;
+    device_permissions->ephemeral_hid_devices_[device.guid] = device_entry;
 
     // Make sure the HidDeviceManager is active. HidDeviceManager is
     // responsible for removing the permission entry for an ephemeral hid
@@ -621,7 +616,7 @@
   } else if (entry->type_ == DevicePermissionEntry::Type::USB) {
     device_permissions->ephemeral_usb_devices_.erase(entry->usb_device_.get());
   } else if (entry->type_ == DevicePermissionEntry::Type::HID) {
-    device_permissions->ephemeral_hid_devices_.erase(entry->hid_device_.get());
+    device_permissions->ephemeral_hid_devices_.erase(entry->hid_device_guid_);
   } else {
     NOTREACHED();
   }
@@ -676,15 +671,15 @@
   }
 }
 
-void DevicePermissionsManager::RemoveEntryForEphemeralHidDevice(
-    scoped_refptr<device::HidDeviceInfo> device) {
+void DevicePermissionsManager::RemoveEntryByHidDeviceGUID(
+    const std::string& guid) {
   DCHECK(thread_checker_.CalledOnValidThread());
   for (const auto& map_entry : extension_id_to_device_permissions_) {
     // An ephemeral device cannot be identified if it is reconnected and so
     // permission to access it is cleared on disconnect.
     DevicePermissions* device_permissions = map_entry.second;
     const auto& device_entry =
-        device_permissions->ephemeral_hid_devices_.find(device.get());
+        device_permissions->ephemeral_hid_devices_.find(guid);
     if (device_entry != device_permissions->ephemeral_hid_devices_.end()) {
       device_permissions->entries_.erase(device_entry->second);
       device_permissions->ephemeral_hid_devices_.erase(device_entry);
diff --git a/extensions/browser/api/device_permissions_manager.h b/extensions/browser/api/device_permissions_manager.h
index 4d5dce4..72e68128 100644
--- a/extensions/browser/api/device_permissions_manager.h
+++ b/extensions/browser/api/device_permissions_manager.h
@@ -21,6 +21,7 @@
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "device/hid/hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/usb/usb_service.h"
 
 namespace base {
@@ -44,7 +45,8 @@
   };
 
   DevicePermissionEntry(scoped_refptr<device::UsbDevice> device);
-  DevicePermissionEntry(scoped_refptr<device::HidDeviceInfo> device);
+
+  DevicePermissionEntry(const device::mojom::HidDeviceInfo& device);
   DevicePermissionEntry(Type type,
                         uint16_t vendor_id,
                         uint16_t product_id,
@@ -84,10 +86,9 @@
   // The USB device tracked by this entry. Will be nullptr if this entry was
   // restored from ExtensionPrefs or type_ is not Type::USB.
   scoped_refptr<device::UsbDevice> usb_device_;
-  // The HID device tracked by this entry. Will be nullptr if this entry was
-  // restored from ExtensionPrefs or type_ is not Type::HID.
-  scoped_refptr<device::HidDeviceInfo> hid_device_;
 
+  // The device guid of hid device tracked by this entry.
+  std::string hid_device_guid_;
   // The type of device this entry represents.
   Type type_;
   // The vendor ID of this device.
@@ -113,7 +114,7 @@
   scoped_refptr<DevicePermissionEntry> FindUsbDeviceEntry(
       scoped_refptr<device::UsbDevice> device) const;
   scoped_refptr<DevicePermissionEntry> FindHidDeviceEntry(
-      scoped_refptr<device::HidDeviceInfo> device) const;
+      const device::mojom::HidDeviceInfo& device) const;
 
   const std::set<scoped_refptr<DevicePermissionEntry>>& entries() const {
     return entries_;
@@ -129,7 +130,7 @@
   std::set<scoped_refptr<DevicePermissionEntry>> entries_;
   std::map<device::UsbDevice*, scoped_refptr<DevicePermissionEntry>>
       ephemeral_usb_devices_;
-  std::map<device::HidDeviceInfo*, scoped_refptr<DevicePermissionEntry>>
+  std::map<std::string, scoped_refptr<DevicePermissionEntry>>
       ephemeral_hid_devices_;
 
   DISALLOW_COPY_AND_ASSIGN(DevicePermissions);
@@ -137,8 +138,7 @@
 
 // Manages saved device permissions for all extensions.
 class DevicePermissionsManager : public KeyedService,
-                                 public device::UsbService::Observer,
-                                 public device::HidService::Observer {
+                                 public device::UsbService::Observer {
  public:
   static DevicePermissionsManager* Get(content::BrowserContext* context);
 
@@ -161,7 +161,7 @@
   void AllowUsbDevice(const std::string& extension_id,
                       scoped_refptr<device::UsbDevice> device);
   void AllowHidDevice(const std::string& extension_id,
-                      scoped_refptr<device::HidDeviceInfo> device);
+                      const device::mojom::HidDeviceInfo& device);
 
   // Updates the "last used" timestamp on the given device entry and writes it
   // out to ExtensionPrefs.
@@ -172,9 +172,9 @@
   void RemoveEntry(const std::string& extension_id,
                    scoped_refptr<DevicePermissionEntry> entry);
 
-  // Revokes permission for an ephemeral hid device.
-  void RemoveEntryForEphemeralHidDevice(
-      scoped_refptr<device::HidDeviceInfo> device);
+  // Revokes permission for an ephemeral hid device by its guid.
+
+  void RemoveEntryByHidDeviceGUID(const std::string& guid);
 
   // Revokes permission for the extension to access all allowed devices.
   void Clear(const std::string& extension_id);
diff --git a/extensions/browser/api/device_permissions_prompt.cc b/extensions/browser/api/device_permissions_prompt.cc
index 139492e..9b3cbe8 100644
--- a/extensions/browser/api/device_permissions_prompt.cc
+++ b/extensions/browser/api/device_permissions_prompt.cc
@@ -14,8 +14,8 @@
 #include "build/build_config.h"
 #include "device/base/device_client.h"
 #include "device/hid/hid_device_filter.h"
-#include "device/hid/hid_device_info.h"
 #include "device/hid/hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/usb/public/cpp/filter_utils.h"
 #include "device/usb/usb_device.h"
 #include "device/usb/usb_ids.h"
@@ -27,7 +27,6 @@
 #if defined(OS_CHROMEOS)
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/permission_broker_client.h"
-#include "device/hid/hid_device_info_linux.h"
 #endif  // defined(OS_CHROMEOS)
 
 using device::HidDeviceFilter;
@@ -40,8 +39,7 @@
 
 namespace {
 
-void NoopHidCallback(const std::vector<scoped_refptr<device::HidDeviceInfo>>&) {
-}
+void NoopHidCallback(std::vector<device::mojom::HidDeviceInfoPtr>) {}
 
 void NoopUsbCallback(const std::vector<scoped_refptr<device::UsbDevice>>&) {}
 
@@ -158,23 +156,23 @@
 
 class HidDeviceInfo : public DevicePermissionsPrompt::Prompt::DeviceInfo {
  public:
-  explicit HidDeviceInfo(scoped_refptr<device::HidDeviceInfo> device)
-      : device_(device) {
+  explicit HidDeviceInfo(device::mojom::HidDeviceInfoPtr device)
+      : device_(std::move(device)) {
     name_ = DevicePermissionsManager::GetPermissionMessage(
-        device->vendor_id(), device->product_id(),
+        device_->vendor_id, device_->product_id,
         base::string16(),  // HID devices include manufacturer in product name.
-        base::UTF8ToUTF16(device->product_name()),
+        base::UTF8ToUTF16(device_->product_name),
         base::string16(),  // Serial number is displayed separately.
         false);
-    serial_number_ = base::UTF8ToUTF16(device->serial_number());
+    serial_number_ = base::UTF8ToUTF16(device_->serial_number);
   }
 
   ~HidDeviceInfo() override {}
 
-  const scoped_refptr<device::HidDeviceInfo>& device() const { return device_; }
+  device::mojom::HidDeviceInfoPtr& device() { return device_; }
 
  private:
-  scoped_refptr<device::HidDeviceInfo> device_;
+  device::mojom::HidDeviceInfoPtr device_;
 };
 
 class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
@@ -211,36 +209,34 @@
   void Dismissed() override {
     DevicePermissionsManager* permissions_manager =
         DevicePermissionsManager::Get(browser_context());
-    std::vector<scoped_refptr<device::HidDeviceInfo>> devices;
+    std::vector<device::mojom::HidDeviceInfoPtr> devices;
     for (const auto& device : devices_) {
       if (device->granted()) {
-        const HidDeviceInfo* hid_device =
-            static_cast<const HidDeviceInfo*>(device.get());
-        devices.push_back(hid_device->device());
+        HidDeviceInfo* hid_device = static_cast<HidDeviceInfo*>(device.get());
         if (permissions_manager) {
+          DCHECK(hid_device->device());
           permissions_manager->AllowHidDevice(extension()->id(),
-                                              hid_device->device());
+                                              *(hid_device->device()));
         }
+        devices.push_back(std::move(hid_device->device()));
       }
     }
     DCHECK(multiple() || devices.size() <= 1);
-    callback_.Run(devices);
+    callback_.Run(std::move(devices));
     callback_.Reset();
   }
 
   // device::HidService::Observer implementation:
-  void OnDeviceAdded(scoped_refptr<device::HidDeviceInfo> device) override {
-    if (HasUnprotectedCollections(device) &&
-        (filters_.empty() || HidDeviceFilter::MatchesAny(device, filters_))) {
-      std::unique_ptr<DeviceInfo> device_info(new HidDeviceInfo(device));
+  void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) override {
+    if (HasUnprotectedCollections(*device) &&
+        (filters_.empty() || HidDeviceFilter::MatchesAny(*device, filters_))) {
+      auto device_info = base::MakeUnique<HidDeviceInfo>(std::move(device));
 #if defined(OS_CHROMEOS)
       chromeos::PermissionBrokerClient* client =
           chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
       DCHECK(client) << "Could not get permission broker client.";
-      device::HidDeviceInfoLinux* linux_device_info =
-          static_cast<device::HidDeviceInfoLinux*>(device.get());
       client->CheckPathAccess(
-          linux_device_info->device_node(),
+          device_info.get()->device()->device_node,
           base::Bind(&HidDevicePermissionsPrompt::AddCheckedDevice, this,
                      base::Passed(&device_info)));
 #else
@@ -249,11 +245,10 @@
     }
   }
 
-  void OnDeviceRemoved(scoped_refptr<device::HidDeviceInfo> device) override {
+  void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) override {
     for (auto it = devices_.begin(); it != devices_.end(); ++it) {
-      const HidDeviceInfo* entry =
-          static_cast<const HidDeviceInfo*>((*it).get());
-      if (entry->device() == device) {
+      HidDeviceInfo* entry = static_cast<HidDeviceInfo*>((*it).get());
+      if (entry->device()->guid == device->guid) {
         size_t index = it - devices_.begin();
         base::string16 device_name = (*it)->name();
         devices_.erase(it);
@@ -266,15 +261,15 @@
 
   void OnDevicesEnumerated(
       HidService* service,
-      const std::vector<scoped_refptr<device::HidDeviceInfo>>& devices) {
-    for (const auto& device : devices) {
-      OnDeviceAdded(device);
+      std::vector<device::mojom::HidDeviceInfoPtr> devices) {
+    for (auto& device : devices) {
+      OnDeviceAdded(std::move(device));
     }
     service_observer_.Add(service);
   }
 
-  bool HasUnprotectedCollections(scoped_refptr<device::HidDeviceInfo> device) {
-    for (const auto& collection : device->collections()) {
+  bool HasUnprotectedCollections(const device::mojom::HidDeviceInfo& device) {
+    for (const auto& collection : device.collections) {
       if (!collection.usage.IsProtected()) {
         return true;
       }
diff --git a/extensions/browser/api/device_permissions_prompt.h b/extensions/browser/api/device_permissions_prompt.h
index d2502f5..0d47060 100644
--- a/extensions/browser/api/device_permissions_prompt.h
+++ b/extensions/browser/api/device_permissions_prompt.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "device/usb/public/interfaces/device_manager.mojom.h"
 
 namespace content {
@@ -24,7 +25,6 @@
 
 namespace device {
 class HidDeviceFilter;
-class HidDeviceInfo;
 class UsbDevice;
 }
 
@@ -38,8 +38,8 @@
  public:
   using UsbDevicesCallback = base::Callback<void(
       const std::vector<scoped_refptr<device::UsbDevice>>&)>;
-  using HidDevicesCallback = base::Callback<void(
-      const std::vector<scoped_refptr<device::HidDeviceInfo>>&)>;
+  using HidDevicesCallback =
+      base::Callback<void(std::vector<device::mojom::HidDeviceInfoPtr>)>;
 
   // Context information available to the UI implementation.
   class Prompt : public base::RefCounted<Prompt> {
diff --git a/extensions/browser/api/hid/hid_api.cc b/extensions/browser/api/hid/hid_api.cc
index 1c283b68..3f64ca56 100644
--- a/extensions/browser/api/hid/hid_api.cc
+++ b/extensions/browser/api/hid/hid_api.cc
@@ -15,7 +15,6 @@
 #include "device/base/device_client.h"
 #include "device/hid/hid_connection.h"
 #include "device/hid/hid_device_filter.h"
-#include "device/hid/hid_device_info.h"
 #include "device/hid/hid_service.h"
 #include "extensions/browser/api/api_resource_manager.h"
 #include "extensions/browser/api/device_permissions_prompt.h"
@@ -27,7 +26,6 @@
 
 using device::HidConnection;
 using device::HidDeviceFilter;
-using device::HidDeviceInfo;
 using device::HidService;
 
 namespace {
@@ -144,10 +142,11 @@
 }
 
 void HidGetUserSelectedDevicesFunction::OnDevicesChosen(
-    const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
+    std::vector<device::mojom::HidDeviceInfoPtr> devices) {
   HidDeviceManager* device_manager = HidDeviceManager::Get(browser_context());
   CHECK(device_manager);
-  Respond(OneArgument(device_manager->GetApiDevicesFromList(devices)));
+  Respond(
+      OneArgument(device_manager->GetApiDevicesFromList(std::move(devices))));
 }
 
 HidConnectFunction::HidConnectFunction() : connection_manager_(nullptr) {
@@ -167,13 +166,13 @@
       ApiResourceManager<HidConnectionResource>::Get(browser_context());
   CHECK(connection_manager_);
 
-  scoped_refptr<HidDeviceInfo> device_info =
+  const device::mojom::HidDeviceInfo* device_info =
       device_manager->GetDeviceInfo(parameters->device_id);
   if (!device_info) {
     return RespondNow(Error(kErrorInvalidDeviceId));
   }
 
-  if (!device_manager->HasPermission(extension(), device_info, true)) {
+  if (!device_manager->HasPermission(extension(), *device_info, true)) {
     return RespondNow(Error(kErrorPermissionDenied));
   }
 
@@ -181,7 +180,7 @@
   CHECK(hid_service);
 
   hid_service->Connect(
-      device_info->device_guid(),
+      device_info->guid,
       base::Bind(&HidConnectFunction::OnConnectComplete, this));
   return RespondLater();
 }
diff --git a/extensions/browser/api/hid/hid_api.h b/extensions/browser/api/hid/hid_api.h
index 1d00566..8b53659 100644
--- a/extensions/browser/api/hid/hid_api.h
+++ b/extensions/browser/api/hid/hid_api.h
@@ -12,6 +12,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "extensions/browser/api/api_resource_manager.h"
 #include "extensions/browser/api/hid/hid_connection_resource.h"
 #include "extensions/browser/api/hid/hid_device_manager.h"
@@ -20,7 +21,6 @@
 
 namespace device {
 class HidConnection;
-class HidDeviceInfo;
 }  // namespace device
 
 namespace net {
@@ -61,8 +61,7 @@
   // ExtensionFunction:
   ResponseAction Run() override;
 
-  void OnDevicesChosen(
-      const std::vector<scoped_refptr<device::HidDeviceInfo>>& devices);
+  void OnDevicesChosen(std::vector<device::mojom::HidDeviceInfoPtr> devices);
 
   std::unique_ptr<DevicePermissionsPrompt> prompt_;
 
diff --git a/extensions/browser/api/hid/hid_apitest.cc b/extensions/browser/api/hid/hid_apitest.cc
index b9360fe..2067ccaf 100644
--- a/extensions/browser/api/hid/hid_apitest.cc
+++ b/extensions/browser/api/hid/hid_apitest.cc
@@ -17,6 +17,7 @@
 #include "device/hid/hid_device_info.h"
 #include "device/hid/hid_usage_and_page.h"
 #include "device/hid/mock_hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "extensions/browser/api/device_permissions_prompt.h"
 #include "extensions/shell/browser/shell_extensions_api_client.h"
 #include "extensions/shell/test/shell_apitest.h"
@@ -225,7 +226,7 @@
     }
     device_client_->hid_service()->AddDevice(new HidDeviceInfo(
         platform_device_id, vendor_id, product_id, "Test Device", serial_number,
-        device::kHIDBusTypeUSB, report_descriptor));
+        device::mojom::HidBusType::kHIDBusTypeUSB, report_descriptor));
   }
 
  protected:
diff --git a/extensions/browser/api/hid/hid_device_manager.cc b/extensions/browser/api/hid/hid_device_manager.cc
index bb584cbd0..6dd60dc 100644
--- a/extensions/browser/api/hid/hid_device_manager.cc
+++ b/extensions/browser/api/hid/hid_device_manager.cc
@@ -25,7 +25,6 @@
 namespace hid = extensions::api::hid;
 
 using device::HidDeviceFilter;
-using device::HidDeviceInfo;
 using device::HidService;
 
 namespace extensions {
@@ -33,16 +32,16 @@
 namespace {
 
 void PopulateHidDeviceInfo(hid::HidDeviceInfo* output,
-                           scoped_refptr<const HidDeviceInfo> input) {
-  output->vendor_id = input->vendor_id();
-  output->product_id = input->product_id();
-  output->product_name = input->product_name();
-  output->serial_number = input->serial_number();
-  output->max_input_report_size = input->max_input_report_size();
-  output->max_output_report_size = input->max_output_report_size();
-  output->max_feature_report_size = input->max_feature_report_size();
+                           const device::mojom::HidDeviceInfo& input) {
+  output->vendor_id = input.vendor_id;
+  output->product_id = input.product_id;
+  output->product_name = input.product_name;
+  output->serial_number = input.serial_number;
+  output->max_input_report_size = input.max_input_report_size;
+  output->max_output_report_size = input.max_output_report_size;
+  output->max_feature_report_size = input.max_feature_report_size;
 
-  for (const device::HidCollectionInfo& collection : input->collections()) {
+  for (const device::HidCollectionInfo& collection : input.collections) {
     // Don't expose sensitive data.
     if (collection.usage.IsProtected()) {
       continue;
@@ -59,7 +58,7 @@
     output->collections.push_back(std::move(api_collection));
   }
 
-  const std::vector<uint8_t>& report_descriptor = input->report_descriptor();
+  const std::vector<uint8_t>& report_descriptor = input.report_descriptor;
   if (report_descriptor.size() > 0) {
     output->report_descriptor.assign(report_descriptor.begin(),
                                      report_descriptor.end());
@@ -67,7 +66,7 @@
 }
 
 bool WillDispatchDeviceEvent(base::WeakPtr<HidDeviceManager> device_manager,
-                             scoped_refptr<device::HidDeviceInfo> device_info,
+                             const device::mojom::HidDeviceInfo& device_info,
                              content::BrowserContext* context,
                              const Extension* extension,
                              Event* event,
@@ -135,38 +134,38 @@
 }
 
 std::unique_ptr<base::ListValue> HidDeviceManager::GetApiDevicesFromList(
-    const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
+    std::vector<device::mojom::HidDeviceInfoPtr> devices) {
   DCHECK(thread_checker_.CalledOnValidThread());
   std::unique_ptr<base::ListValue> device_list(new base::ListValue());
   for (const auto& device : devices) {
-    const auto device_entry = resource_ids_.find(device->device_guid());
+    const auto device_entry = resource_ids_.find(device->guid);
     DCHECK(device_entry != resource_ids_.end());
 
     hid::HidDeviceInfo device_info;
     device_info.device_id = device_entry->second;
-    PopulateHidDeviceInfo(&device_info, device);
+    PopulateHidDeviceInfo(&device_info, *device);
     device_list->Append(device_info.ToValue());
   }
   return device_list;
 }
 
-scoped_refptr<HidDeviceInfo> HidDeviceManager::GetDeviceInfo(int resource_id) {
+const device::mojom::HidDeviceInfo* HidDeviceManager::GetDeviceInfo(
+    int resource_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  HidService* hid_service = device::DeviceClient::Get()->GetHidService();
-  DCHECK(hid_service);
 
-  ResourceIdToDeviceIdMap::const_iterator device_iter =
-      device_ids_.find(resource_id);
-  if (device_iter == device_ids_.end()) {
+  ResourceIdToDeviceInfoMap::const_iterator device_iter =
+      devices_.find(resource_id);
+  if (device_iter == devices_.end()) {
     return nullptr;
   }
 
-  return hid_service->GetDeviceInfo(device_iter->second);
+  return device_iter->second.get();
 }
 
-bool HidDeviceManager::HasPermission(const Extension* extension,
-                                     scoped_refptr<HidDeviceInfo> device_info,
-                                     bool update_last_used) {
+bool HidDeviceManager::HasPermission(
+    const Extension* extension,
+    const device::mojom::HidDeviceInfo& device_info,
+    bool update_last_used) {
   DevicePermissionsManager* permissions_manager =
       DevicePermissionsManager::Get(browser_context_);
   CHECK(permissions_manager);
@@ -184,7 +183,7 @@
 
   std::unique_ptr<UsbDevicePermission::CheckParam> usb_param =
       UsbDevicePermission::CheckParam::ForHidDevice(
-          extension, device_info->vendor_id(), device_info->product_id());
+          extension, device_info.vendor_id, device_info.product_id);
   if (extension->permissions_data()->CheckAPIPermissionWithParam(
           APIPermission::kUsbDevice, usb_param.get())) {
     return true;
@@ -211,55 +210,53 @@
 void HidDeviceManager::OnListenerAdded(const EventListenerInfo& details) {
   LazyInitialize();
 }
-
-void HidDeviceManager::OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) {
+void HidDeviceManager::OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_LT(next_resource_id_, std::numeric_limits<int>::max());
   int new_id = next_resource_id_++;
-  DCHECK(!base::ContainsKey(resource_ids_, device_info->device_guid()));
-  resource_ids_[device_info->device_guid()] = new_id;
-  device_ids_[new_id] = device_info->device_guid();
+  DCHECK(!base::ContainsKey(resource_ids_, device->guid));
+  resource_ids_[device->guid] = new_id;
+  devices_[new_id] = std::move(device);
 
   // Don't generate events during the initial enumeration.
   if (enumeration_ready_ && event_router_) {
     api::hid::HidDeviceInfo api_device_info;
     api_device_info.device_id = new_id;
-    PopulateHidDeviceInfo(&api_device_info, device_info);
+
+    PopulateHidDeviceInfo(&api_device_info, *devices_[new_id]);
 
     if (api_device_info.collections.size() > 0) {
       std::unique_ptr<base::ListValue> args(
           hid::OnDeviceAdded::Create(api_device_info));
       DispatchEvent(events::HID_ON_DEVICE_ADDED, hid::OnDeviceAdded::kEventName,
-                    std::move(args), device_info);
+                    std::move(args), *devices_[new_id]);
     }
   }
 }
 
-void HidDeviceManager::OnDeviceRemoved(
-    scoped_refptr<HidDeviceInfo> device_info) {
+void HidDeviceManager::OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  const auto& resource_entry = resource_ids_.find(device_info->device_guid());
+  const auto& resource_entry = resource_ids_.find(device->guid);
   DCHECK(resource_entry != resource_ids_.end());
   int resource_id = resource_entry->second;
-  const auto& device_entry = device_ids_.find(resource_id);
-  DCHECK(device_entry != device_ids_.end());
+  const auto& device_entry = devices_.find(resource_id);
+  DCHECK(device_entry != devices_.end());
   resource_ids_.erase(resource_entry);
-  device_ids_.erase(device_entry);
+  devices_.erase(device_entry);
 
   if (event_router_) {
     DCHECK(enumeration_ready_);
     std::unique_ptr<base::ListValue> args(
         hid::OnDeviceRemoved::Create(resource_id));
     DispatchEvent(events::HID_ON_DEVICE_REMOVED,
-                  hid::OnDeviceRemoved::kEventName, std::move(args),
-                  device_info);
+                  hid::OnDeviceRemoved::kEventName, std::move(args), *device);
   }
 
   // Remove permission entry for ephemeral hid device.
   DevicePermissionsManager* permissions_manager =
       DevicePermissionsManager::Get(browser_context_);
   DCHECK(permissions_manager);
-  permissions_manager->RemoveEntryForEphemeralHidDevice(device_info);
+  permissions_manager->RemoveEntryByHidDeviceGUID(device->guid);
 }
 
 void HidDeviceManager::LazyInitialize() {
@@ -280,32 +277,23 @@
 std::unique_ptr<base::ListValue> HidDeviceManager::CreateApiDeviceList(
     const Extension* extension,
     const std::vector<HidDeviceFilter>& filters) {
-  HidService* hid_service = device::DeviceClient::Get()->GetHidService();
-  DCHECK(hid_service);
-
   std::unique_ptr<base::ListValue> api_devices(new base::ListValue());
-  for (const ResourceIdToDeviceIdMap::value_type& map_entry : device_ids_) {
+  for (const ResourceIdToDeviceInfoMap::value_type& map_entry : devices_) {
     int resource_id = map_entry.first;
-    const std::string& device_guid = map_entry.second;
-
-    scoped_refptr<HidDeviceInfo> device_info =
-        hid_service->GetDeviceInfo(device_guid);
-    if (!device_info) {
-      continue;
-    }
+    auto& device_info = map_entry.second;
 
     if (!filters.empty() &&
-        !HidDeviceFilter::MatchesAny(device_info, filters)) {
+        !HidDeviceFilter::MatchesAny(*device_info, filters)) {
       continue;
     }
 
-    if (!HasPermission(extension, device_info, false)) {
+    if (!HasPermission(extension, *device_info, false)) {
       continue;
     }
 
     hid::HidDeviceInfo api_device_info;
     api_device_info.device_id = resource_id;
-    PopulateHidDeviceInfo(&api_device_info, device_info);
+    PopulateHidDeviceInfo(&api_device_info, *device_info);
 
     // Expose devices with which user can communicate.
     if (api_device_info.collections.size() > 0) {
@@ -318,11 +306,11 @@
 
 void HidDeviceManager::OnEnumerationComplete(
     HidService* hid_service,
-    const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
+    std::vector<device::mojom::HidDeviceInfoPtr> devices) {
   DCHECK(resource_ids_.empty());
-  DCHECK(device_ids_.empty());
-  for (const scoped_refptr<HidDeviceInfo>& device_info : devices) {
-    OnDeviceAdded(device_info);
+  DCHECK(devices_.empty());
+  for (auto& device_info : devices) {
+    OnDeviceAdded(std::move(device_info));
   }
   enumeration_ready_ = true;
 
@@ -340,11 +328,14 @@
     events::HistogramValue histogram_value,
     const std::string& event_name,
     std::unique_ptr<base::ListValue> event_args,
-    scoped_refptr<HidDeviceInfo> device_info) {
+    const device::mojom::HidDeviceInfo& device_info) {
   std::unique_ptr<Event> event(
       new Event(histogram_value, event_name, std::move(event_args)));
-  event->will_dispatch_callback = base::Bind(
-      &WillDispatchDeviceEvent, weak_factory_.GetWeakPtr(), device_info);
+  // The |event->will_dispatch_callback| will be called synchronously, it is
+  // safe to pass |device_info| by reference.
+  event->will_dispatch_callback =
+      base::Bind(&WillDispatchDeviceEvent, weak_factory_.GetWeakPtr(),
+                 base::ConstRef(device_info));
   event_router_->BroadcastEvent(std::move(event));
 }
 
diff --git a/extensions/browser/api/hid/hid_device_manager.h b/extensions/browser/api/hid/hid_device_manager.h
index 5197a4b..a311381 100644
--- a/extensions/browser/api/hid/hid_device_manager.h
+++ b/extensions/browser/api/hid/hid_device_manager.h
@@ -14,6 +14,7 @@
 #include "base/scoped_observer.h"
 #include "base/threading/thread_checker.h"
 #include "device/hid/hid_service.h"
+#include "device/hid/public/interfaces/hid.mojom.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_event_histogram_value.h"
@@ -21,7 +22,6 @@
 
 namespace device {
 class HidDeviceFilter;
-class HidDeviceInfo;
 }
 
 namespace extensions {
@@ -56,17 +56,17 @@
                      const std::vector<device::HidDeviceFilter>& filters,
                      const GetApiDevicesCallback& callback);
 
-  // Converts a list of HidDeviceInfo objects into a value that can be returned
-  // through the API.
+  // Converts a list of device::mojom::HidDeviceInfo objects into a value that
+  // can be returned through the API.
   std::unique_ptr<base::ListValue> GetApiDevicesFromList(
-      const std::vector<scoped_refptr<device::HidDeviceInfo>>& devices);
+      std::vector<device::mojom::HidDeviceInfoPtr> devices);
 
-  scoped_refptr<device::HidDeviceInfo> GetDeviceInfo(int resource_id);
+  const device::mojom::HidDeviceInfo* GetDeviceInfo(int resource_id);
 
   // Checks if |extension| has permission to open |device_info|. Set
   // |update_last_used| to update the timestamp in the DevicePermissionsManager.
   bool HasPermission(const Extension* extension,
-                     scoped_refptr<device::HidDeviceInfo> device_info,
+                     const device::mojom::HidDeviceInfo& device_info,
                      bool update_last_used);
 
   // Wait to perform an initial enumeration and register a HidService::Observer
@@ -77,7 +77,8 @@
  private:
   friend class BrowserContextKeyedAPIFactory<HidDeviceManager>;
 
-  typedef std::map<int, std::string> ResourceIdToDeviceIdMap;
+  typedef std::map<int, device::mojom::HidDeviceInfoPtr>
+      ResourceIdToDeviceInfoMap;
   typedef std::map<std::string, int> DeviceIdToResourceIdMap;
 
   struct GetApiDevicesParams;
@@ -94,9 +95,8 @@
   void OnListenerAdded(const EventListenerInfo& details) override;
 
   // HidService::Observer:
-  void OnDeviceAdded(scoped_refptr<device::HidDeviceInfo> device_info) override;
-  void OnDeviceRemoved(
-      scoped_refptr<device::HidDeviceInfo> device_info) override;
+  void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) override;
+  void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) override;
 
   // Builds a list of device info objects representing the currently enumerated
   // devices, taking into account the permissions held by the given extension
@@ -106,12 +106,12 @@
       const std::vector<device::HidDeviceFilter>& filters);
   void OnEnumerationComplete(
       device::HidService* hid_service,
-      const std::vector<scoped_refptr<device::HidDeviceInfo>>& devices);
+      std::vector<device::mojom::HidDeviceInfoPtr> devices);
 
   void DispatchEvent(events::HistogramValue histogram_value,
                      const std::string& event_name,
                      std::unique_ptr<base::ListValue> event_args,
-                     scoped_refptr<device::HidDeviceInfo> device_info);
+                     const device::mojom::HidDeviceInfo& device_info);
 
   base::ThreadChecker thread_checker_;
   content::BrowserContext* browser_context_ = nullptr;
@@ -122,7 +122,7 @@
   bool enumeration_ready_ = false;
   std::vector<std::unique_ptr<GetApiDevicesParams>> pending_enumerations_;
   int next_resource_id_ = 0;
-  ResourceIdToDeviceIdMap device_ids_;
+  ResourceIdToDeviceInfoMap devices_;
   DeviceIdToResourceIdMap resource_ids_;
   base::WeakPtrFactory<HidDeviceManager> weak_factory_;
 
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index e90ac72..079c69d 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1576,6 +1576,9 @@
       <message name="IDS_IOS_CHOOSE_EMAIL_CLIENT_APP" desc="Title for action sheet to select an email client app when user taps on an URL that has a mailto: URL scheme. [Length: 50em]">
         Create email with:
       </message>
+      <message name="IDS_IOS_CHOOSE_EMAIL_ASK_TOGGLE" desc="Title for toggle switch to set whether to ask user which Mail client app to use every time a mailto:// URL is tapped. [Length: 50]">
+        Ask me which app to use every time
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm
index aa019f2..ad65daa4 100644
--- a/ios/web/navigation/crw_session_controller_unittest.mm
+++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -1267,7 +1267,7 @@
 }
 
 // Tests that |-backwardItems| returns all committed items if there is a
-// transient item. This can happen if an intersitial was loaded for SSL error.
+// transient item. This can happen if an interstitial was loaded for SSL error.
 // See crbug.com/691311.
 TEST_F(CRWSessionControllerTest,
        BackwardItemsShouldContainAllCommittedIfCurrentIsTransient) {
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
index 3f19264..e425f4c 100644
--- a/ios/web/navigation/navigation_manager_impl_unittest.mm
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -1964,7 +1964,7 @@
 }
 
 // Tests that all committed items are considered history if there is a transient
-// item. This can happen if an intersitial was loaded for SSL error.
+// item. This can happen if an interstitial was loaded for SSL error.
 // See crbug.com/691311.
 TEST_P(NavigationManagerTest,
        BackwardItemsShouldContainAllCommittedIfCurrentIsTransient) {
diff --git a/ios/web/public/test/earl_grey/web_view_matchers.h b/ios/web/public/test/earl_grey/web_view_matchers.h
index 583a25a..ae8700c 100644
--- a/ios/web/public/test/earl_grey/web_view_matchers.h
+++ b/ios/web/public/test/earl_grey/web_view_matchers.h
@@ -26,9 +26,6 @@
 id<GREYMatcher> WebViewContainingLoadedImage(std::string image_id,
                                              WebState* web_state);
 
-// Matcher for WKWebView containing an html element which matches |selector|.
-id<GREYMatcher> WebViewCssSelector(std::string selector, WebState* web_state);
-
 // Matcher for WKWebView's scroll view.
 id<GREYMatcher> WebViewScrollView(WebState* web_state);
 
diff --git a/ios/web/public/test/earl_grey/web_view_matchers.mm b/ios/web/public/test/earl_grey/web_view_matchers.mm
index 5c53385f..558b434f 100644
--- a/ios/web/public/test/earl_grey/web_view_matchers.mm
+++ b/ios/web/public/test/earl_grey/web_view_matchers.mm
@@ -58,8 +58,6 @@
 // Script that returns document.body as a string.
 char kGetDocumentBodyJavaScript[] =
     "document.body ? document.body.textContent : null";
-// Script that tests presence of css selector.
-char kTestCssSelectorJavaScriptTemplate[] = "!!document.querySelector(\"%s\");";
 
 // Fetches the image from |image_url|.
 UIImage* LoadImage(const GURL& image_url) {
@@ -195,33 +193,6 @@
   return WebViewContainingImage(image_id, web_state, IMAGE_STATE_LOADED);
 }
 
-id<GREYMatcher> WebViewCssSelector(std::string selector, WebState* web_state) {
-  MatchesBlock matches = ^BOOL(WKWebView*) {
-    std::string script = base::StringPrintf(kTestCssSelectorJavaScriptTemplate,
-                                            selector.c_str());
-    return WaitUntilConditionOrTimeout(testing::kWaitForUIElementTimeout, ^{
-      bool did_succeed = false;
-      std::unique_ptr<base::Value> value =
-          web::test::ExecuteJavaScript(web_state, script);
-      if (value) {
-        value->GetAsBoolean(&did_succeed);
-      }
-      return did_succeed;
-    });
-  };
-
-  DescribeToBlock describe = ^(id<GREYDescription> description) {
-    [description appendText:@"web view selector "];
-    [description appendText:base::SysUTF8ToNSString(selector)];
-  };
-
-  return grey_allOf(
-      WebViewInWebState(web_state),
-      [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches
-                                           descriptionBlock:describe],
-      nil);
-}
-
 id<GREYMatcher> WebViewScrollView(WebState* web_state) {
   MatchesBlock matches = ^BOOL(UIView* view) {
     return [view isKindOfClass:[UIScrollView class]] &&
diff --git a/ios/web/public/test/web_view_content_test_util.h b/ios/web/public/test/web_view_content_test_util.h
index 160abbaf..1fc0a72 100644
--- a/ios/web/public/test/web_view_content_test_util.h
+++ b/ios/web/public/test/web_view_content_test_util.h
@@ -32,6 +32,12 @@
 bool WaitForWebViewContainingImage(std::string image_id,
                                    web::WebState* web_state,
                                    ImageStateElement image_state);
+
+// Returns true if there is a web view for |web_state| that contains the CSS
+// selector |css_selector|.
+bool IsWebViewContainingCssSelector(web::WebState* web_state,
+                                    const std::string& css_selector);
+
 }  // namespace test
 }  // namespace web
 
diff --git a/ios/web/public/test/web_view_content_test_util.mm b/ios/web/public/test/web_view_content_test_util.mm
index 3a6863d..4c1db3b 100644
--- a/ios/web/public/test/web_view_content_test_util.mm
+++ b/ios/web/public/test/web_view_content_test_util.mm
@@ -9,6 +9,7 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
 #import "ios/testing/wait_util.h"
 #import "ios/web/public/test/web_view_interaction_test_util.h"
 #import "net/base/mac/url_conversions.h"
@@ -147,5 +148,22 @@
   });
 }
 
+bool IsWebViewContainingCssSelector(web::WebState* web_state,
+                                    const std::string& css_selector) {
+  // Script that tests presence of css selector.
+  char testCssSelectorJavaScriptTemplate[] =
+      "!!document.querySelector(\"%s\");";
+  std::string script = base::StringPrintf(testCssSelectorJavaScriptTemplate,
+                                          css_selector.c_str());
+
+  bool did_succeed = false;
+  std::unique_ptr<base::Value> value =
+      web::test::ExecuteJavaScript(web_state, script);
+  if (value) {
+    value->GetAsBoolean(&did_succeed);
+  }
+  return did_succeed;
+}
+
 }  // namespace test
 }  // namespace web
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey.h b/ios/web/shell/test/earl_grey/shell_earl_grey.h
index a1f7a4b..1b49272 100644
--- a/ios/web/shell/test/earl_grey/shell_earl_grey.h
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey.h
@@ -23,6 +23,15 @@
 // within a timeout, a GREYAssert is induced.
 + (void)waitForWebViewContainingText:(const std::string)text;
 
+// Waits for the current web view to contain a css selector matching |selector|.
+// If the condition is not met within a timeout, a GREYAssert is induced.
++ (void)waitForWebViewContainingCSSSelector:(std::string)selector;
+
+// Waits for the current web view to not contain a css selector matching
+// |selector|. If the condition is not met within a timeout, a GREYAssert is
+// induced.
++ (void)waitForWebViewNotContainingCSSSelector:(std::string)selector;
+
 @end
 
 #endif  // IOS_WEB_SHELL_TEST_EARL_GREY_SHELL_EARL_GREY_H_
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey.mm b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
index 4a08d771..a4427ef 100644
--- a/ios/web/shell/test/earl_grey/shell_earl_grey.mm
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
@@ -9,6 +9,7 @@
 #import "ios/testing/wait_util.h"
 #import "ios/web/public/test/earl_grey/js_test_util.h"
 #import "ios/web/public/test/web_view_content_test_util.h"
+#import "ios/web/public/test/web_view_interaction_test_util.h"
 #include "ios/web/shell/test/app/navigation_test_util.h"
 #import "ios/web/shell/test/app/web_shell_test_util.h"
 
@@ -49,4 +50,28 @@
              @"Failed waiting for web view containing %s", text.c_str());
 }
 
++ (void)waitForWebViewContainingCSSSelector:(std::string)selector {
+  GREYCondition* condition = [GREYCondition
+      conditionWithName:@"Wait for web view containing text"
+                  block:^BOOL {
+                    return web::test::IsWebViewContainingCssSelector(
+                        web::shell_test_util::GetCurrentWebState(), selector);
+                  }];
+  GREYAssert([condition waitWithTimeout:testing::kWaitForUIElementTimeout],
+             @"Failed waiting for web view containing css selector: %s",
+             selector.c_str());
+}
+
++ (void)waitForWebViewNotContainingCSSSelector:(std::string)selector {
+  GREYCondition* condition = [GREYCondition
+      conditionWithName:@"Wait for web view not containing text"
+                  block:^BOOL {
+                    return !web::test::IsWebViewContainingCssSelector(
+                        web::shell_test_util::GetCurrentWebState(), selector);
+                  }];
+  GREYAssert([condition waitWithTimeout:testing::kWaitForUIElementTimeout],
+             @"Failed waiting for web view not containing css selector: %s",
+             selector.c_str());
+}
+
 @end
diff --git a/ios/web/shell/test/earl_grey/shell_matchers.h b/ios/web/shell/test/earl_grey/shell_matchers.h
index 396eafc..bd2a3d67 100644
--- a/ios/web/shell/test/earl_grey/shell_matchers.h
+++ b/ios/web/shell/test/earl_grey/shell_matchers.h
@@ -11,9 +11,6 @@
 
 namespace web {
 
-// Matcher for WKWebView containing an html element which matches |selector|.
-id<GREYMatcher> WebViewCssSelector(const std::string& selector);
-
 // Matcher for the WKWebView.
 id<GREYMatcher> WebView();
 
diff --git a/ios/web/shell/test/earl_grey/shell_matchers.mm b/ios/web/shell/test/earl_grey/shell_matchers.mm
index 1e71a85..85561a3 100644
--- a/ios/web/shell/test/earl_grey/shell_matchers.mm
+++ b/ios/web/shell/test/earl_grey/shell_matchers.mm
@@ -19,11 +19,6 @@
 
 namespace web {
 
-id<GREYMatcher> WebViewCssSelector(const std::string& selector) {
-  WebState* web_state = shell_test_util::GetCurrentWebState();
-  return WebViewCssSelector(std::move(selector), web_state);
-}
-
 id<GREYMatcher> WebView() {
   return WebViewInWebState(shell_test_util::GetCurrentWebState());
 }
diff --git a/ios/web/shell/test/plugin_placeholder_egtest.mm b/ios/web/shell/test/plugin_placeholder_egtest.mm
index 7e39fa57..4145aa9 100644
--- a/ios/web/shell/test/plugin_placeholder_egtest.mm
+++ b/ios/web/shell/test/plugin_placeholder_egtest.mm
@@ -20,8 +20,6 @@
 #error "This file requires ARC support."
 #endif
 
-using web::WebViewCssSelector;
-
 namespace {
 
 // Loads a web page with given content.
@@ -58,8 +56,7 @@
   // Verify that placeholder image is not displayed.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
   [ShellEarlGrey waitForWebViewContainingText:kFallbackText];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img")]
-      assertWithMatcher:grey_nil()];
+  [ShellEarlGrey waitForWebViewNotContainingCSSSelector:"img"];
 }
 
 // Tests placeholder for a large <applet> with no fallback.
@@ -76,8 +73,7 @@
 
   // Verify that plugin object is replaced with placeholder image.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img[src*='data']")]
-      assertWithMatcher:grey_notNil()];
+  [ShellEarlGrey waitForWebViewContainingCSSSelector:"img[src*='data']"];
 }
 
 // Tests placeholder for a large <object> with an embed fallback.
@@ -98,8 +94,7 @@
 
   // Verify that plugin object is replaced with placeholder image.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img[src*='data']")]
-      assertWithMatcher:grey_notNil()];
+  [ShellEarlGrey waitForWebViewContainingCSSSelector:"img[src*='data']"];
 }
 
 // Tests that a large <object> with text fallback is untouched.
@@ -121,8 +116,7 @@
   // Verify that placeholder image is not displayed.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
   [ShellEarlGrey waitForWebViewContainingText:kFallbackText];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img")]
-      assertWithMatcher:grey_nil()];
+  [ShellEarlGrey waitForWebViewNotContainingCSSSelector:"img"];
 }
 
 // Tests placeholder for a large <object> with no fallback.
@@ -140,8 +134,7 @@
 
   // Verify that plugin object is replaced with placeholder image.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img[src*='data']")]
-      assertWithMatcher:grey_notNil()];
+  [ShellEarlGrey waitForWebViewContainingCSSSelector:"img[src*='data']"];
 }
 
 // Tests that a large png <object> is untouched.
@@ -158,8 +151,7 @@
 
   // Verify that placeholder image is not displayed.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img")]
-      assertWithMatcher:grey_nil()];
+  [ShellEarlGrey waitForWebViewNotContainingCSSSelector:"img"];
 }
 
 // Test that non-major plugins (e.g., top/side ads) don't get placeholders.
@@ -195,8 +187,7 @@
 
   // Verify that placeholder image is not displayed.
   [ShellEarlGrey waitForWebViewContainingText:kPageDescription];
-  [[EarlGrey selectElementWithMatcher:WebViewCssSelector("img")]
-      assertWithMatcher:grey_nil()];
+  [ShellEarlGrey waitForWebViewNotContainingCSSSelector:"img"];
 }
 
 @end
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 16a6878..dd636b5 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -646,15 +646,14 @@
     bool dispatch = false;
     bool send_done = false;
     bool should_pump_messages = false;
-    bool registered = registry->RegisterEvent(
-        context->GetSendDoneEvent(), base::Bind(&OnEventReady, &send_done));
-    DCHECK(registered);
+    base::Closure on_send_done_callback = base::Bind(&OnEventReady, &send_done);
+    registry->RegisterEvent(context->GetSendDoneEvent(), on_send_done_callback);
 
+    base::Closure on_pump_messages_callback;
     if (pump_messages_event) {
-      registered = registry->RegisterEvent(
-          pump_messages_event,
-          base::Bind(&OnEventReady, &should_pump_messages));
-      DCHECK(registered);
+      on_pump_messages_callback =
+          base::Bind(&OnEventReady, &should_pump_messages);
+      registry->RegisterEvent(pump_messages_event, on_pump_messages_callback);
     }
 
     const bool* stop_flags[] = { &dispatch, &send_done, &should_pump_messages };
@@ -662,9 +661,10 @@
     registry->Wait(stop_flags, 3);
     context->received_sync_msgs()->UnblockDispatch();
 
-    registry->UnregisterEvent(context->GetSendDoneEvent());
+    registry->UnregisterEvent(context->GetSendDoneEvent(),
+                              on_send_done_callback);
     if (pump_messages_event)
-      registry->UnregisterEvent(pump_messages_event);
+      registry->UnregisterEvent(pump_messages_event, on_pump_messages_callback);
 
     if (dispatch) {
       // We're waiting for a reply, but we received a blocking synchronous call.
diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc
index 15ffdbc..86d4ce1 100644
--- a/ipc/ipc_sync_message_filter.cc
+++ b/ipc/ipc_sync_message_filter.cc
@@ -73,9 +73,10 @@
   bool shutdown = false;
   scoped_refptr<mojo::SyncHandleRegistry> registry =
       mojo::SyncHandleRegistry::current();
-  registry->RegisterEvent(shutdown_event_,
-                          base::Bind(&OnEventReady, &shutdown));
-  registry->RegisterEvent(&done_event, base::Bind(&OnEventReady, &done));
+  auto on_shutdown_callback = base::Bind(&OnEventReady, &shutdown);
+  auto on_done_callback = base::Bind(&OnEventReady, &done);
+  registry->RegisterEvent(shutdown_event_, on_shutdown_callback);
+  registry->RegisterEvent(&done_event, on_done_callback);
 
   const bool* stop_flags[] = { &done, &shutdown };
   registry->Wait(stop_flags, 2);
@@ -84,8 +85,8 @@
                           "SyncMessageFilter::Send", &done_event);
   }
 
-  registry->UnregisterEvent(shutdown_event_);
-  registry->UnregisterEvent(&done_event);
+  registry->UnregisterEvent(shutdown_event_, on_shutdown_callback);
+  registry->UnregisterEvent(&done_event, on_done_callback);
 
   {
     base::AutoLock auto_lock(lock_);
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 49fc34f..d1ee33b 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -160,21 +160,6 @@
     "filters/vp8_parser_unittest.cc",
     "filters/vp9_parser_unittest.cc",
     "filters/vp9_raw_bits_reader_unittest.cc",
-    "formats/ac3/ac3_util_unittest.cc",
-    "formats/common/offset_byte_queue_unittest.cc",
-    "formats/webm/cluster_builder.cc",
-    "formats/webm/cluster_builder.h",
-    "formats/webm/opus_packet_builder.cc",
-    "formats/webm/opus_packet_builder.h",
-    "formats/webm/tracks_builder.cc",
-    "formats/webm/tracks_builder.h",
-    "formats/webm/webm_cluster_parser_unittest.cc",
-    "formats/webm/webm_content_encodings_client_unittest.cc",
-    "formats/webm/webm_crypto_helpers_unittest.cc",
-    "formats/webm/webm_parser_unittest.cc",
-    "formats/webm/webm_stream_parser_unittest.cc",
-    "formats/webm/webm_tracks_parser_unittest.cc",
-    "formats/webm/webm_webvtt_parser_unittest.cc",
     "muxers/webm_muxer_unittest.cc",
   ]
 
@@ -256,32 +241,7 @@
   }
 
   if (proprietary_codecs) {
-    sources += [
-      "filters/h264_to_annex_b_bitstream_converter_unittest.cc",
-      "formats/common/stream_parser_test_base.cc",
-      "formats/common/stream_parser_test_base.h",
-      "formats/mp4/aac_unittest.cc",
-      "formats/mp4/avc_unittest.cc",
-      "formats/mp4/box_reader_unittest.cc",
-      "formats/mp4/es_descriptor_unittest.cc",
-      "formats/mp4/mp4_stream_parser_unittest.cc",
-      "formats/mp4/sample_to_group_iterator_unittest.cc",
-      "formats/mp4/track_run_iterator_unittest.cc",
-      "formats/mpeg/adts_stream_parser_unittest.cc",
-      "formats/mpeg/mpeg1_audio_stream_parser_unittest.cc",
-    ]
-    if (enable_mse_mpeg2ts_stream_parser) {
-      sources += [
-        "formats/mp2t/es_adapter_video_unittest.cc",
-        "formats/mp2t/es_parser_adts_unittest.cc",
-        "formats/mp2t/es_parser_h264_unittest.cc",
-        "formats/mp2t/es_parser_mpeg1audio_unittest.cc",
-        "formats/mp2t/es_parser_test_base.cc",
-        "formats/mp2t/es_parser_test_base.h",
-        "formats/mp2t/mp2t_stream_parser_unittest.cc",
-        "formats/mp2t/timestamp_unroller_unittest.cc",
-      ]
-    }
+    sources += [ "filters/h264_to_annex_b_bitstream_converter_unittest.cc" ]
     if (media_use_ffmpeg) {
       sources += [
         "filters/ffmpeg_aac_bitstream_converter_unittest.cc",
@@ -291,9 +251,6 @@
     if (enable_hls_sample_aes) {
       deps += [ "//third_party/boringssl" ]
     }
-    if (enable_dolby_vision_demuxing) {
-      sources += [ "formats/mp4/dolby_vision_unittest.cc" ]
-    }
   }
 
   if (is_mac || is_ios) {
@@ -317,6 +274,7 @@
     "//media/audio:test_support",
     "//media/base:test_support",
     "//media/base/android:test_support",
+    "//media/formats:test_support",
     "//media/video:test_support",
   ]
 }
@@ -330,6 +288,7 @@
     "//media/base:unit_tests",
     "//media/cdm:unit_tests",
     "//media/device_monitors:unit_tests",
+    "//media/formats:unit_tests",
     "//media/gpu:unit_tests",
     "//media/mojo:unit_tests",
     "//media/renderers:unit_tests",
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 224264f..762f5ac8 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -364,6 +364,7 @@
     "//media:test_support",
     "//media/audio:test_support",
     "//media/base/android:test_support",
+    "//media/formats:test_support",
     "//media/video:test_support",
   ]
   testonly = true
diff --git a/media/base/android/android_cdm_factory.cc b/media/base/android/android_cdm_factory.cc
index 539ddada..1cd8030 100644
--- a/media/base/android/android_cdm_factory.cc
+++ b/media/base/android/android_cdm_factory.cc
@@ -15,7 +15,7 @@
 #include "media/base/media_switches.h"
 #include "media/cdm/aes_decryptor.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace media {
 namespace {
@@ -46,7 +46,7 @@
 
 void AndroidCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
@@ -56,7 +56,7 @@
   // Bound |cdm_created_cb| so we always fire it asynchronously.
   CdmCreatedCB bound_cdm_created_cb = BindToCurrentLoop(cdm_created_cb);
 
-  if (!security_origin.is_valid()) {
+  if (security_origin.unique()) {
     bound_cdm_created_cb.Run(nullptr, "Invalid origin.");
     return;
   }
diff --git a/media/base/android/android_cdm_factory.h b/media/base/android/android_cdm_factory.h
index 5285efcf..d28300f 100644
--- a/media/base/android/android_cdm_factory.h
+++ b/media/base/android/android_cdm_factory.h
@@ -23,7 +23,7 @@
 
   // CdmFactory implementation.
   void Create(const std::string& key_system,
-              const GURL& security_origin,
+              const url::Origin& security_origin,
               const CdmConfig& cdm_config,
               const SessionMessageCB& session_message_cb,
               const SessionClosedCB& session_closed_cb,
diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc
index 661050a..85f7c55 100644
--- a/media/base/android/media_drm_bridge.cc
+++ b/media/base/android/media_drm_bridge.cc
@@ -363,7 +363,7 @@
 // static
 void MediaDrmBridge::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     SecurityLevel security_level,
     const CreateFetcherCB& create_fetcher_cb,
     const CreateStorageCB& create_storage_cb,
@@ -395,7 +395,7 @@
       session_closed_cb, session_keys_change_cb, session_expiration_update_cb);
 
   if (IsPersistentLicenseTypeSupported(key_system) &&
-      !security_origin.is_empty() && !create_storage_cb.is_null()) {
+      !security_origin.unique() && !create_storage_cb.is_null()) {
     raw_storage->Initialize(
         create_storage_cb, base::BindOnce(&OnStorageInitialized,
                                           std::move(create_media_drm_bridge_cb),
diff --git a/media/base/android/media_drm_bridge.h b/media/base/android/media_drm_bridge.h
index d12f3628..1869c986 100644
--- a/media/base/android/media_drm_bridge.h
+++ b/media/base/android/media_drm_bridge.h
@@ -29,7 +29,7 @@
 #include "media/base/player_tracker.h"
 #include "media/base/provision_fetcher.h"
 #include "media/cdm/player_tracker_impl.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -95,7 +95,7 @@
   // if |security_level| is SECURITY_LEVEL_DEFAULT.
   static void Create(
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       SecurityLevel security_level,
       const CreateFetcherCB& create_fetcher_cb,
       const CreateStorageCB& create_storage_cb,
diff --git a/media/base/cdm_factory.h b/media/base/cdm_factory.h
index f7a72fa..36056a4 100644
--- a/media/base/cdm_factory.h
+++ b/media/base/cdm_factory.h
@@ -11,7 +11,9 @@
 #include "media/base/content_decryption_module.h"
 #include "media/base/media_export.h"
 
-class GURL;
+namespace url {
+class Origin;
+}
 
 namespace media {
 
@@ -32,7 +34,7 @@
   // asynchronously.
   virtual void Create(
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       const CdmConfig& cdm_config,
       const SessionMessageCB& session_message_cb,
       const SessionClosedCB& session_closed_cb,
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index 4c5d5f8..1eb8659 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -258,8 +258,8 @@
 
 void MockCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
-    const CdmConfig& cdm_config,
+    const url::Origin& /* security_origin */,
+    const CdmConfig& /* cdm_config */,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
     const SessionKeysChangeCB& session_keys_change_cb,
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 2264be19..c11da37e 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -547,7 +547,7 @@
   // created CDM is passed to |cdm_created_cb|, a copy is kept (and available
   // using Cdm()). If |key_system| is empty, no CDM will be created.
   void Create(const std::string& key_system,
-              const GURL& security_origin,
+              const url::Origin& security_origin,
               const CdmConfig& cdm_config,
               const SessionMessageCB& session_message_cb,
               const SessionClosedCB& session_closed_cb,
diff --git a/media/blink/cdm_session_adapter.cc b/media/blink/cdm_session_adapter.cc
index 3837820..a025df3 100644
--- a/media/blink/cdm_session_adapter.cc
+++ b/media/blink/cdm_session_adapter.cc
@@ -20,7 +20,7 @@
 #include "media/base/key_systems.h"
 #include "media/blink/webcontentdecryptionmodule_impl.h"
 #include "media/blink/webcontentdecryptionmodulesession_impl.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -38,7 +38,7 @@
 void CdmSessionAdapter::CreateCdm(
     CdmFactory* cdm_factory,
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     std::unique_ptr<blink::WebContentDecryptionModuleResult> result) {
   TRACE_EVENT_ASYNC_BEGIN0("media", "CdmSessionAdapter::CreateCdm",
diff --git a/media/blink/cdm_session_adapter.h b/media/blink/cdm_session_adapter.h
index 1c7aefa..8d4da1b 100644
--- a/media/blink/cdm_session_adapter.h
+++ b/media/blink/cdm_session_adapter.h
@@ -19,7 +19,9 @@
 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h"
 
-class GURL;
+namespace url {
+class Origin;
+}
 
 namespace media {
 
@@ -40,7 +42,7 @@
   void CreateCdm(
       CdmFactory* cdm_factory,
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       const CdmConfig& cdm_config,
       std::unique_ptr<blink::WebContentDecryptionModuleResult> result);
 
diff --git a/media/blink/webcontentdecryptionmodule_impl.cc b/media/blink/webcontentdecryptionmodule_impl.cc
index d2c56c3a..ed7a1ca 100644
--- a/media/blink/webcontentdecryptionmodule_impl.cc
+++ b/media/blink/webcontentdecryptionmodule_impl.cc
@@ -20,7 +20,6 @@
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/platform/WebString.h"
-#include "url/gurl.h"
 #include "url/origin.h"
 
 namespace media {
@@ -101,15 +100,13 @@
     return;
   }
 
-  GURL security_origin_as_gurl(url::Origin(security_origin).GetURL());
-
   // CdmSessionAdapter::CreateCdm() will keep a reference to |adapter|. Then
   // if WebContentDecryptionModuleImpl is successfully created (returned in
   // |result|), it will keep a reference to |adapter|. Otherwise, |adapter| will
   // be destructed.
   scoped_refptr<CdmSessionAdapter> adapter(new CdmSessionAdapter());
-  adapter->CreateCdm(cdm_factory, key_system_ascii, security_origin_as_gurl,
-                     cdm_config, std::move(result));
+  adapter->CreateCdm(cdm_factory, key_system_ascii, security_origin, cdm_config,
+                     std::move(result));
 }
 
 WebContentDecryptionModuleImpl::WebContentDecryptionModuleImpl(
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index a02e7dff..fcda8cd 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1347,6 +1347,7 @@
     renderer_factory_selector_->SetUseMediaPlayer(true);
 
     pipeline_controller_.Stop();
+    SetMemoryReportingState(false);
 
     main_task_runner_->PostTask(
         FROM_HERE, base::Bind(&WebMediaPlayerImpl::StartPipeline, AsWeakPtr()));
@@ -2409,7 +2410,7 @@
   // It's not critical if some cases where memory usage can change are missed,
   // since media memory changes are usually gradual.
   result.is_memory_reporting_enabled =
-      can_play && !result.is_suspended && (!paused_ || seeking_);
+      !has_error && can_play && !result.is_suspended && (!paused_ || seeking_);
 
   return result;
 }
@@ -2421,8 +2422,12 @@
   // thread.  Before that, however, ~WebMediaPlayerImpl() posts a task to the
   // media thread and waits for it to finish.  Hence, the GetMemoryUsage() task
   // posted here must finish earlier.
-
-  if (demuxer_) {
+  //
+  // The exception to the above is when OnError() has been called. If we're in
+  // the error state we've already shut down the pipeline and can't rely on it
+  // to cycle the media thread before we destroy |demuxer_|. In this case skip
+  // collection of the demuxer memory stats.
+  if (demuxer_ && !IsNetworkStateError(network_state_)) {
     base::PostTaskAndReplyWithResult(
         media_task_runner_.get(), FROM_HERE,
         base::Bind(&Demuxer::GetMemoryUsage, base::Unretained(demuxer_.get())),
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index f9fcfac..db7b42803 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -318,6 +318,10 @@
           TestAudioConfig::Normal();
   }
 
+  void SetError(PipelineStatus status = PIPELINE_ERROR_DECODE) {
+    wmpi_->OnError(status);
+  }
+
   void OnMetadata(PipelineMetadata metadata) { wmpi_->OnMetadata(metadata); }
 
   void OnVideoNaturalSizeChange(const gfx::Size& size) {
@@ -527,6 +531,24 @@
   EXPECT_FALSE(state.is_memory_reporting_enabled);
 }
 
+// Ensure memory reporting is not running after an error.
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_PlayingError) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::kReadyStateHaveFutureData);
+  SetPaused(false);
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
+  EXPECT_FALSE(state.is_idle);
+  EXPECT_FALSE(state.is_suspended);
+  EXPECT_TRUE(state.is_memory_reporting_enabled);
+  SetError();
+  state = ComputePlayState();
+  EXPECT_TRUE(state.is_idle);
+  EXPECT_FALSE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
+}
+
 TEST_F(WebMediaPlayerImplTest, ComputePlayState_Playing) {
   InitializeWebMediaPlayerImpl();
   SetMetadata(true, true);
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index 7fdeb8f..61dd1a285 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -270,7 +270,7 @@
 }
 
 AesDecryptor::AesDecryptor(
-    const GURL& /* security_origin */,
+    const url::Origin& /* security_origin */,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
     const SessionKeysChangeCB& session_keys_change_cb,
diff --git a/media/cdm/aes_decryptor.h b/media/cdm/aes_decryptor.h
index ddab596f..e9b23a86 100644
--- a/media/cdm/aes_decryptor.h
+++ b/media/cdm/aes_decryptor.h
@@ -23,12 +23,14 @@
 #include "media/base/decryptor.h"
 #include "media/base/media_export.h"
 
-class GURL;
-
 namespace crypto {
 class SymmetricKey;
 }
 
+namespace url {
+class Origin;
+}
+
 namespace media {
 
 // Decrypts an AES encrypted buffer into an unencrypted buffer. The AES
@@ -37,7 +39,7 @@
                                   public CdmContext,
                                   public Decryptor {
  public:
-  AesDecryptor(const GURL& security_origin,
+  AesDecryptor(const url::Origin& security_origin,
                const SessionMessageCB& session_message_cb,
                const SessionClosedCB& session_closed_cb,
                const SessionKeysChangeCB& session_keys_change_cb,
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc
index c4a72d96..67f3cc4 100644
--- a/media/cdm/aes_decryptor_unittest.cc
+++ b/media/cdm/aes_decryptor_unittest.cc
@@ -31,7 +31,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest-param-test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "media/cdm/api/content_decryption_module.h"
@@ -260,7 +260,7 @@
   void SetUp() override {
     if (GetParam() == TestType::kAesDecryptor) {
       OnCdmCreated(
-          new AesDecryptor(GURL::EmptyGURL(),
+          new AesDecryptor(url::Origin(),
                            base::Bind(&MockCdmClient::OnSessionMessage,
                                       base::Unretained(&cdm_client_)),
                            base::Bind(&MockCdmClient::OnSessionClosed,
diff --git a/media/cdm/cdm_adapter_factory.cc b/media/cdm/cdm_adapter_factory.cc
index 7b7ef7e..166519ae 100644
--- a/media/cdm/cdm_adapter_factory.cc
+++ b/media/cdm/cdm_adapter_factory.cc
@@ -8,6 +8,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/cdm_factory.h"
 #include "media/cdm/cdm_adapter.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -21,7 +22,7 @@
 
 void CdmAdapterFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
@@ -30,7 +31,7 @@
     const CdmCreatedCB& cdm_created_cb) {
   DVLOG(1) << __FUNCTION__ << ": key_system=" << key_system;
 
-  if (!security_origin.is_valid()) {
+  if (security_origin.unique()) {
     LOG(ERROR) << "Invalid Origin: " << security_origin;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(cdm_created_cb, nullptr, "Invalid origin."));
diff --git a/media/cdm/cdm_adapter_factory.h b/media/cdm/cdm_adapter_factory.h
index cc0d498a0..3cc5390 100644
--- a/media/cdm/cdm_adapter_factory.h
+++ b/media/cdm/cdm_adapter_factory.h
@@ -19,7 +19,7 @@
 
   // CdmFactory implementation.
   void Create(const std::string& key_system,
-              const GURL& security_origin,
+              const url::Origin& security_origin,
               const CdmConfig& cdm_config,
               const SessionMessageCB& session_message_cb,
               const SessionClosedCB& session_closed_cb,
diff --git a/media/cdm/default_cdm_factory.cc b/media/cdm/default_cdm_factory.cc
index 7a150df4..a735906c 100644
--- a/media/cdm/default_cdm_factory.cc
+++ b/media/cdm/default_cdm_factory.cc
@@ -14,7 +14,7 @@
 #include "media/base/key_systems.h"
 #include "media/base/media_switches.h"
 #include "media/cdm/aes_decryptor.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -36,14 +36,14 @@
 
 void DefaultCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
     const SessionKeysChangeCB& session_keys_change_cb,
     const SessionExpirationUpdateCB& session_expiration_update_cb,
     const CdmCreatedCB& cdm_created_cb) {
-  if (!security_origin.is_valid()) {
+  if (security_origin.unique()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(cdm_created_cb, nullptr, "Invalid origin."));
     return;
diff --git a/media/cdm/default_cdm_factory.h b/media/cdm/default_cdm_factory.h
index 3274373..234e194 100644
--- a/media/cdm/default_cdm_factory.h
+++ b/media/cdm/default_cdm_factory.h
@@ -20,7 +20,7 @@
 
   // CdmFactory implementation.
   void Create(const std::string& key_system,
-              const GURL& security_origin,
+              const url::Origin& security_origin,
               const CdmConfig& cdm_config,
               const SessionMessageCB& session_message_cb,
               const SessionClosedCB& session_closed_cb,
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
index 0b73a4b..165636f0 100644
--- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
+++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
@@ -24,7 +24,7 @@
 #include "media/cdm/json_web_key.h"
 #include "media/cdm/ppapi/cdm_file_io_test.h"
 #include "media/cdm/ppapi/external_clear_key/cdm_video_decoder.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
 const int64_t kNoTimestamp = INT64_MIN;
@@ -271,8 +271,7 @@
     return nullptr;
 
   // TODO(jrummell): Obtain the proper origin for this instance.
-  GURL empty_origin;
-  return new media::ClearKeyCdm(host, key_system_string, empty_origin);
+  return new media::ClearKeyCdm(host, key_system_string, url::Origin());
 }
 
 const char* GetCdmVersion() {
@@ -337,7 +336,7 @@
 
 ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host,
                          const std::string& key_system,
-                         const GURL& origin)
+                         const url::Origin& origin)
     : cdm_(new ClearKeyPersistentSessionCdm(
           origin,
           host,
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.h b/media/cdm/ppapi/external_clear_key/clear_key_cdm.h
index 4059d68..af17128 100644
--- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.h
+++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.h
@@ -26,7 +26,9 @@
 #define CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER
 #endif
 
-class GURL;
+namespace url {
+class Origin;
+}
 
 namespace media {
 class CdmVideoDecoder;
@@ -37,7 +39,9 @@
 // Clear key implementation of the cdm::ContentDecryptionModule interface.
 class ClearKeyCdm : public ClearKeyCdmInterface {
  public:
-  ClearKeyCdm(Host* host, const std::string& key_system, const GURL& origin);
+  ClearKeyCdm(Host* host,
+              const std::string& key_system,
+              const url::Origin& origin);
   ~ClearKeyCdm() override;
 
   // ClearKeyCdmInterface implementation.
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.cc b/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.cc
index c10b61b..c42b202 100644
--- a/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.cc
+++ b/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "media/base/cdm_promise.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -87,7 +88,7 @@
 }  // namespace
 
 ClearKeyPersistentSessionCdm::ClearKeyPersistentSessionCdm(
-    const GURL& origin,
+    const url::Origin& origin,
     ClearKeyCdmHost* host,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.h b/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.h
index 5fef3d3..fd0a8d8f 100644
--- a/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.h
+++ b/media/cdm/ppapi/external_clear_key/clear_key_persistent_session_cdm.h
@@ -20,7 +20,9 @@
 #include "media/cdm/cdm_file_adapter.h"
 #include "media/cdm/ppapi/external_clear_key/clear_key_cdm_common.h"
 
-class GURL;
+namespace url {
+class Origin;
+}
 
 namespace media {
 
@@ -30,7 +32,7 @@
 class ClearKeyPersistentSessionCdm : public ContentDecryptionModule {
  public:
   ClearKeyPersistentSessionCdm(
-      const GURL& origin,
+      const url::Origin& origin,
       ClearKeyCdmHost* host,
       const SessionMessageCB& session_message_cb,
       const SessionClosedCB& session_closed_cb,
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index 532dc9a..5569108 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -149,3 +149,91 @@
     ]
   }
 }
+
+static_library("test_support") {
+  testonly = true
+  visibility = [ "//media:test_support" ]
+
+  sources = [
+    "webm/cluster_builder.cc",
+    "webm/cluster_builder.h",
+    "webm/opus_packet_builder.cc",
+    "webm/opus_packet_builder.h",
+    "webm/tracks_builder.cc",
+    "webm/tracks_builder.h",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//media/base:test_support",
+  ]
+
+  if (proprietary_codecs) {
+    sources += [
+      "common/stream_parser_test_base.cc",
+      "common/stream_parser_test_base.h",
+    ]
+
+    deps += [ "//testing/gtest" ]
+
+    if (enable_mse_mpeg2ts_stream_parser) {
+      sources += [
+        "mp2t/es_parser_test_base.cc",
+        "mp2t/es_parser_test_base.h",
+      ]
+    }
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "ac3/ac3_util_unittest.cc",
+    "common/offset_byte_queue_unittest.cc",
+    "webm/webm_cluster_parser_unittest.cc",
+    "webm/webm_content_encodings_client_unittest.cc",
+    "webm/webm_crypto_helpers_unittest.cc",
+    "webm/webm_parser_unittest.cc",
+    "webm/webm_stream_parser_unittest.cc",
+    "webm/webm_tracks_parser_unittest.cc",
+    "webm/webm_webvtt_parser_unittest.cc",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//media:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  if (proprietary_codecs) {
+    sources += [
+      "mp4/aac_unittest.cc",
+      "mp4/avc_unittest.cc",
+      "mp4/box_reader_unittest.cc",
+      "mp4/es_descriptor_unittest.cc",
+      "mp4/mp4_stream_parser_unittest.cc",
+      "mp4/sample_to_group_iterator_unittest.cc",
+      "mp4/track_run_iterator_unittest.cc",
+      "mpeg/adts_stream_parser_unittest.cc",
+      "mpeg/mpeg1_audio_stream_parser_unittest.cc",
+    ]
+
+    deps += [ "//crypto" ]
+
+    if (enable_mse_mpeg2ts_stream_parser) {
+      sources += [
+        "mp2t/es_adapter_video_unittest.cc",
+        "mp2t/es_parser_adts_unittest.cc",
+        "mp2t/es_parser_h264_unittest.cc",
+        "mp2t/es_parser_mpeg1audio_unittest.cc",
+        "mp2t/mp2t_stream_parser_unittest.cc",
+        "mp2t/timestamp_unroller_unittest.cc",
+      ]
+    }
+
+    if (enable_dolby_vision_demuxing) {
+      sources += [ "mp4/dolby_vision_unittest.cc" ]
+    }
+  }
+}
diff --git a/media/gpu/rendering_helper.cc b/media/gpu/rendering_helper.cc
index 31c9a6b..9ee290a 100644
--- a/media/gpu/rendering_helper.cc
+++ b/media/gpu/rendering_helper.cc
@@ -27,33 +27,22 @@
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/init/gl_factory.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
 #endif
 
-#if defined(USE_X11)
-#include "ui/gfx/x/x11_types.h"  // nogncheck
-#endif
-
-#if defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
-#include "ui/gl/gl_surface_glx.h"
-#define GL_VARIANT_GLX 1
-#else
-#include "ui/gl/gl_surface_egl.h"
-#define GL_VARIANT_EGL 1
-#endif
-
-#if defined(USE_OZONE)
 #if defined(OS_CHROMEOS)
 #include "ui/display/manager/chromeos/display_configurator.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/native_display_delegate.h"
-#endif  // defined(OS_CHROMEOS)
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
-#endif  // defined(USE_OZONE)
+#endif  // defined(OS_CHROMEOS)
 
 // Helper for Shader creation.
 static void CreateShader(GLuint program,
@@ -84,7 +73,7 @@
 
 }  // namespace
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
 
 class DisplayConfiguratorObserver
     : public display::DisplayConfigurator::Observer {
@@ -157,7 +146,7 @@
   DISALLOW_COPY_AND_ASSIGN(StubOzoneDelegate);
 };
 
-#endif  // defined(USE_OZONE)
+#endif  // defined(OS_CHROMEOS)
 
 RenderingHelperParams::RenderingHelperParams()
     : rendering_fps(0), warm_up_iterations(0), render_as_thumbnails(false) {}
@@ -191,14 +180,9 @@
 // static
 void RenderingHelper::InitializeOneOff(base::WaitableEvent* done) {
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-#if GL_VARIANT_GLX
-  cmd_line->AppendSwitchASCII(switches::kUseGL,
-                              gl::kGLImplementationDesktopName);
-#else
   cmd_line->AppendSwitchASCII(switches::kUseGL, gl::kGLImplementationEGLName);
-#endif
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
@@ -220,34 +204,7 @@
 }
 
 void RenderingHelper::Setup() {
-#if defined(USE_X11)
-  Display* display = gfx::GetXDisplay();
-  Screen* screen = DefaultScreenOfDisplay(display);
-
-  CHECK(display);
-
-  XSetWindowAttributes window_attributes;
-  memset(&window_attributes, 0, sizeof(window_attributes));
-  window_attributes.background_pixel =
-      BlackPixel(display, DefaultScreen(display));
-  window_attributes.override_redirect = true;
-  int depth = DefaultDepth(display, DefaultScreen(display));
-
-  window_ = XCreateWindow(display,
-                          DefaultRootWindow(display),
-                          0, 0,
-                          XWidthOfScreen(screen),
-                          XHeightOfScreen(screen),
-                          0 /* border width */,
-                          depth,
-                          CopyFromParent /* class */,
-                          CopyFromParent /* visual */,
-                          (CWBackPixel | CWOverrideRedirect),
-                          &window_attributes);
-  XStoreName(display, window_, "VideoDecodeAcceleratorTest");
-  XSelectInput(display, window_, ExposureMask);
-  XMapWindow(display, window_);
-#elif defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   base::MessageLoop::ScopedNestableTaskAllower nest_loop(
       base::MessageLoop::current());
   base::RunLoop wait_window_resize;
@@ -258,13 +215,13 @@
   // Ignore the vsync provider by default. On ChromeOS this will be set
   // accordingly based on the display configuration.
   ignore_vsync_ = true;
-#if defined(OS_CHROMEOS)
+
   // We hold onto the main loop here to wait for the DisplayController
   // to give us the size of the display so we can create a window of
   // the same size.
   base::RunLoop wait_display_setup;
   DisplayConfiguratorObserver display_setup_observer(&wait_display_setup);
-  display_configurator_.reset(new display::DisplayConfigurator());
+  display_configurator_ = std::make_unique<display::DisplayConfigurator>();
   display_configurator_->SetDelegateForTesting(0);
   display_configurator_->AddObserver(&display_setup_observer);
   display_configurator_->Init(
@@ -274,12 +231,13 @@
   wait_display_setup.Run();
   display_configurator_->RemoveObserver(&display_setup_observer);
 
-  gfx::Size framebuffer_size = display_configurator_->framebuffer_size();
-  if (!framebuffer_size.IsEmpty()) {
-    window_size = framebuffer_size;
+  auto& cached_displays = display_configurator_->cached_displays();
+  if (!cached_displays.empty()) {
+    DCHECK(cached_displays[0]->current_mode());
+    window_size = cached_displays[0]->current_mode()->size();
     ignore_vsync_ = false;
   }
-#endif
+
   if (ignore_vsync_)
     DVLOG(1) << "Ignoring vsync provider";
 
@@ -300,19 +258,11 @@
 }
 
 void RenderingHelper::TearDown() {
-#if defined(USE_X11)
-  // Destroy resources acquired in Initialize, in reverse-acquisition order.
-  if (window_) {
-    CHECK(XUnmapWindow(gfx::GetXDisplay(), window_));
-    CHECK(XDestroyWindow(gfx::GetXDisplay(), window_));
-  }
-#elif defined(USE_OZONE)
-  platform_window_delegate_.reset();
 #if defined(OS_CHROMEOS)
+  platform_window_delegate_.reset();
   display_configurator_->PrepareForExit();
   display_configurator_.reset();
 #endif
-#endif
   window_ = gfx::kNullAcceleratedWidget;
 }
 
@@ -345,10 +295,10 @@
   task_runner_ = base::ThreadTaskRunnerHandle::Get();
 
   gl_surface_ = gl::init::CreateViewGLSurface(window_);
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   gl_surface_->Resize(platform_window_delegate_->GetSize(), 1.f,
                       gl::GLSurface::ColorSpace::UNSPECIFIED, true);
-#endif  // defined(USE_OZONE)
+#endif  // defined(OS_CHROMEOS)
   screen_size_ = gl_surface_->GetSize();
 
   gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
@@ -431,7 +381,7 @@
                   gl_Position = in_pos;
                 });
 
-#if GL_VARIANT_EGL && !defined(OS_WIN)
+#if !defined(OS_WIN)
   static const char kFragmentShader[] =
       "#extension GL_OES_EGL_image_external : enable\n"
       "precision mediump float;\n"
@@ -494,7 +444,7 @@
 
   if (!frame_duration_.is_zero()) {
     int warm_up_iterations = params.warm_up_iterations;
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
     // On Ozone the VSyncProvider can't provide a vsync interval until
     // we render at least a frame, so we warm up with at least one
     // frame.
@@ -749,11 +699,11 @@
   }
 
   int tex_flip = !gl_surface_->FlipsVertically();
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   // Ozone surfaceless renders flipped from normal GL, so there's no need to
   // do an extra flip.
   tex_flip = 0;
-#endif  // defined(USE_OZONE)
+#endif  // defined(OS_CHROMEOS)
   glUniform1i(glGetUniformLocation(program_, "tex_flip"), tex_flip);
 
   // Frames that will be returned to the client (via the no_longer_needed_cb)
diff --git a/media/gpu/rendering_helper.h b/media/gpu/rendering_helper.h
index 54dcbed..40a3263 100644
--- a/media/gpu/rendering_helper.h
+++ b/media/gpu/rendering_helper.h
@@ -190,14 +190,11 @@
   scoped_refptr<gl::GLContext> gl_context_;
   scoped_refptr<gl::GLSurface> gl_surface_;
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   class StubOzoneDelegate;
   std::unique_ptr<StubOzoneDelegate> platform_window_delegate_;
-
-#if defined(OS_CHROMEOS)
   std::unique_ptr<display::DisplayConfigurator> display_configurator_;
 #endif
-#endif
 
   bool ignore_vsync_;
 
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index 2545016d..fce044c 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -78,12 +78,12 @@
 #include "media/gpu/vaapi_wrapper.h"
 #endif  // BUILDFLAG(USE_VAAPI)
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
 #include "ui/gfx/native_pixmap.h"
 #include "ui/ozone/public/ozone_gpu_test_helper.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
-#endif  // defined(USE_OZONE)
+#endif  // defined(OS_CHROMEOS)
 
 namespace media {
 
@@ -269,7 +269,7 @@
         FROM_HERE, base::Bind(&RenderingHelper::InitializeOneOff, &done));
     done.Wait();
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
     gpu_helper_.reset(new ui::OzoneGpuTestHelper());
     // Need to initialize after the rendering side since the rendering side
     // initializes the "GPU" parts of Ozone.
@@ -289,7 +289,7 @@
   }
 
   void TearDown() override {
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
     gpu_helper_.reset();
 #endif
     rendering_thread_.Stop();
@@ -301,7 +301,7 @@
 
  private:
   base::Thread rendering_thread_;
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   std::unique_ptr<ui::OzoneGpuTestHelper> gpu_helper_;
 #endif
 
@@ -336,7 +336,7 @@
 
   uint32_t texture_id_;
   base::Closure no_longer_needed_cb_;
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   scoped_refptr<gfx::NativePixmap> pixmap_;
 #endif
 };
@@ -352,7 +352,7 @@
   return make_scoped_refptr(new TextureRef(texture_id, no_longer_needed_cb));
 }
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
 gfx::BufferFormat VideoPixelFormatToGfxBufferFormat(
     VideoPixelFormat pixel_format) {
   switch (pixel_format) {
@@ -376,7 +376,7 @@
     VideoPixelFormat pixel_format,
     const gfx::Size& size) {
   scoped_refptr<TextureRef> texture_ref;
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   texture_ref = TextureRef::Create(texture_id, no_longer_needed_cb);
   LOG_ASSERT(texture_ref);
 
@@ -395,7 +395,7 @@
 
 gfx::GpuMemoryBufferHandle TextureRef::ExportGpuMemoryBufferHandle() const {
   gfx::GpuMemoryBufferHandle handle;
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
   CHECK(pixmap_);
   int duped_fd = HANDLE_EINTR(dup(pixmap_->GetDmaBufFd(0)));
   LOG_ASSERT(duped_fd != -1) << "Failed duplicating dmabuf fd";
@@ -1827,7 +1827,7 @@
   VDATestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {}
 
   int Run() {
-#if defined(OS_WIN) || defined(USE_OZONE)
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
     // For windows the decoding thread initializes the media foundation decoder
     // which uses COM. We need the thread to be a UI thread.
     // On Ozone, the backend initializes the event system using a UI
@@ -1836,14 +1836,14 @@
         base::test::ScopedTaskEnvironment::MainThreadType::UI);
 #else
     base::test::ScopedTaskEnvironment scoped_task_environment;
-#endif  // OS_WIN || USE_OZONE
+#endif  // OS_WIN || OS_CHROMEOS
 
     media::g_env =
         reinterpret_cast<media::VideoDecodeAcceleratorTestEnvironment*>(
             testing::AddGlobalTestEnvironment(
                 new media::VideoDecodeAcceleratorTestEnvironment()));
 
-#if defined(USE_OZONE)
+#if defined(OS_CHROMEOS)
     ui::OzonePlatform::InitializeForUI();
 #endif
 
diff --git a/media/mojo/clients/mojo_cdm.cc b/media/mojo/clients/mojo_cdm.cc
index 8e7f8ca..74a06437 100644
--- a/media/mojo/clients/mojo_cdm.cc
+++ b/media/mojo/clients/mojo_cdm.cc
@@ -21,14 +21,14 @@
 #include "media/mojo/interfaces/decryptor.mojom.h"
 #include "services/service_manager/public/cpp/connect.h"
 #include "services/service_manager/public/interfaces/interface_provider.mojom.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace media {
 
 // static
 void MojoCdm::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     mojom::ContentDecryptionModulePtr remote_cdm,
     const SessionMessageCB& session_message_cb,
@@ -96,7 +96,7 @@
 // error handler can't be invoked and callbacks won't be dispatched.
 
 void MojoCdm::InitializeCdm(const std::string& key_system,
-                            const GURL& security_origin,
+                            const url::Origin& security_origin,
                             const CdmConfig& cdm_config,
                             std::unique_ptr<CdmInitializedPromise> promise) {
   DVLOG(1) << __func__ << ": " << key_system;
@@ -116,8 +116,11 @@
 
   pending_init_promise_ = std::move(promise);
 
+  // TODO(jrummell): Pass |security_origin| as a url.mojom.Origin.
+  // http://crbug.com/639438.
   remote_cdm_->Initialize(
-      key_system, security_origin.spec(), mojom::CdmConfig::From(cdm_config),
+      key_system, security_origin.Serialize(),
+      mojom::CdmConfig::From(cdm_config),
       base::Bind(&MojoCdm::OnCdmInitialized, base::Unretained(this)));
 }
 
diff --git a/media/mojo/clients/mojo_cdm.h b/media/mojo/clients/mojo_cdm.h
index 3c93313..d239c22 100644
--- a/media/mojo/clients/mojo_cdm.h
+++ b/media/mojo/clients/mojo_cdm.h
@@ -27,6 +27,10 @@
 class SingleThreadTaskRunner;
 }
 
+namespace url {
+class Origin;
+}
+
 namespace media {
 
 class MojoDecryptor;
@@ -42,7 +46,7 @@
 
   static void Create(
       const std::string& key_system,
-      const GURL& security_origin,
+      const url::Origin& security_origin,
       const CdmConfig& cdm_config,
       mojom::ContentDecryptionModulePtr remote_cdm,
       const SessionMessageCB& session_message_cb,
@@ -88,7 +92,7 @@
   ~MojoCdm() final;
 
   void InitializeCdm(const std::string& key_system,
-                     const GURL& security_origin,
+                     const url::Origin& security_origin,
                      const CdmConfig& cdm_config,
                      std::unique_ptr<CdmInitializedPromise> promise);
 
diff --git a/media/mojo/clients/mojo_cdm_factory.cc b/media/mojo/clients/mojo_cdm_factory.cc
index 91d140d..2b9f400a 100644
--- a/media/mojo/clients/mojo_cdm_factory.cc
+++ b/media/mojo/clients/mojo_cdm_factory.cc
@@ -15,6 +15,7 @@
 #include "media/mojo/features.h"
 #include "media/mojo/interfaces/interface_factory.mojom.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -28,7 +29,7 @@
 
 void MojoCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
@@ -37,7 +38,7 @@
     const CdmCreatedCB& cdm_created_cb) {
   DVLOG(2) << __func__ << ": " << key_system;
 
-  if (!security_origin.is_valid()) {
+  if (security_origin.unique()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(cdm_created_cb, nullptr, "Invalid origin."));
     return;
diff --git a/media/mojo/clients/mojo_cdm_factory.h b/media/mojo/clients/mojo_cdm_factory.h
index d53987f0..a7abe21 100644
--- a/media/mojo/clients/mojo_cdm_factory.h
+++ b/media/mojo/clients/mojo_cdm_factory.h
@@ -21,7 +21,7 @@
 
   // CdmFactory implementation.
   void Create(const std::string& key_system,
-              const GURL& security_origin,
+              const url::Origin& security_origin,
               const CdmConfig& cdm_config,
               const SessionMessageCB& session_message_cb,
               const SessionClosedCB& session_closed_cb,
diff --git a/media/mojo/clients/mojo_cdm_unittest.cc b/media/mojo/clients/mojo_cdm_unittest.cc
index d70f8a7f..ed448378 100644
--- a/media/mojo/clients/mojo_cdm_unittest.cc
+++ b/media/mojo/clients/mojo_cdm_unittest.cc
@@ -21,6 +21,8 @@
 #include "media/mojo/services/mojo_cdm_service_context.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 using ::testing::_;
 using ::testing::DoAll;
@@ -106,8 +108,8 @@
       }
     }
 
-    MojoCdm::Create(key_system, GURL(kTestSecurityOrigin), CdmConfig(),
-                    std::move(remote_cdm),
+    MojoCdm::Create(key_system, url::Origin(GURL(kTestSecurityOrigin)),
+                    CdmConfig(), std::move(remote_cdm),
                     base::Bind(&MockCdmClient::OnSessionMessage,
                                base::Unretained(&cdm_client_)),
                     base::Bind(&MockCdmClient::OnSessionClosed,
diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc
index 9487445..af835c5 100644
--- a/media/mojo/services/mojo_cdm_service.cc
+++ b/media/mojo/services/mojo_cdm_service.cc
@@ -20,6 +20,7 @@
 #include "media/mojo/common/media_type_converters.h"
 #include "media/mojo/services/mojo_cdm_service_context.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -67,7 +68,8 @@
 
   auto weak_this = weak_factory_.GetWeakPtr();
   cdm_factory_->Create(
-      key_system, GURL(security_origin), cdm_config.To<CdmConfig>(),
+      key_system, url::Origin(GURL(security_origin)),
+      cdm_config.To<CdmConfig>(),
       base::Bind(&MojoCdmService::OnSessionMessage, weak_this),
       base::Bind(&MojoCdmService::OnSessionClosed, weak_this),
       base::Bind(&MojoCdmService::OnSessionKeysChange, weak_this),
diff --git a/media/remoting/BUILD.gn b/media/remoting/BUILD.gn
index eb835ff..b98c682 100644
--- a/media/remoting/BUILD.gn
+++ b/media/remoting/BUILD.gn
@@ -63,6 +63,7 @@
     "//media/mojo/interfaces:remoting",
     "//mojo/public/cpp/bindings",
     "//ui/gfx",
+    "//url",
   ]
 
   if (enable_media_remoting_rpc) {
diff --git a/media/remoting/remoting_cdm_factory.cc b/media/remoting/remoting_cdm_factory.cc
index 635036a..7f37539 100644
--- a/media/remoting/remoting_cdm_factory.cc
+++ b/media/remoting/remoting_cdm_factory.cc
@@ -9,6 +9,7 @@
 #include "base/single_thread_task_runner.h"
 #include "media/base/cdm_config.h"
 #include "media/remoting/remoting_cdm.h"
+#include "url/origin.h"
 
 namespace media {
 namespace remoting {
@@ -48,7 +49,7 @@
 // TODO(xjz): Replace the callbacks with an interface. http://crbug.com/657940.
 void RemotingCdmFactory::Create(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
@@ -77,7 +78,7 @@
 
 void RemotingCdmFactory::CreateCdm(
     const std::string& key_system,
-    const GURL& security_origin,
+    const url::Origin& security_origin,
     const CdmConfig& cdm_config,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
diff --git a/media/remoting/remoting_cdm_factory.h b/media/remoting/remoting_cdm_factory.h
index eaba632..6c04344 100644
--- a/media/remoting/remoting_cdm_factory.h
+++ b/media/remoting/remoting_cdm_factory.h
@@ -27,7 +27,7 @@
   ~RemotingCdmFactory() override;
 
   void Create(const std::string& key_system,
-              const GURL& security_origin,
+              const url::Origin& security_origin,
               const CdmConfig& cdm_config,
               const SessionMessageCB& session_message_cb,
               const SessionClosedCB& session_closed_cb,
@@ -38,7 +38,7 @@
  private:
   std::unique_ptr<RemotingCdmController> CreateRemotingCdmController();
   void CreateCdm(const std::string& key_system,
-                 const GURL& security_origin,
+                 const url::Origin& security_origin,
                  const CdmConfig& cdm_config,
                  const SessionMessageCB& session_message_cb,
                  const SessionClosedCB& session_closed_cb,
diff --git a/media/test/fake_encrypted_media.cc b/media/test/fake_encrypted_media.cc
index fabccc6..52968ca1 100644
--- a/media/test/fake_encrypted_media.cc
+++ b/media/test/fake_encrypted_media.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "media/base/cdm_key_information.h"
 #include "media/cdm/aes_decryptor.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -23,7 +24,7 @@
 
 FakeEncryptedMedia::FakeEncryptedMedia(AppBase* app)
     : decryptor_(new AesDecryptor(
-          GURL::EmptyGURL(),
+          url::Origin(),
           base::Bind(&FakeEncryptedMedia::OnSessionMessage,
                      base::Unretained(this)),
           base::Bind(&FakeEncryptedMedia::OnSessionClosed,
diff --git a/mojo/public/cpp/bindings/lib/sync_event_watcher.cc b/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
index e64e604..19a41eb5 100644
--- a/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
+++ b/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
@@ -18,7 +18,7 @@
 SyncEventWatcher::~SyncEventWatcher() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (registered_)
-    registry_->UnregisterEvent(event_);
+    registry_->UnregisterEvent(event_, callback_);
   destroyed_->data = true;
 }
 
@@ -51,15 +51,17 @@
 
 void SyncEventWatcher::IncrementRegisterCount() {
   register_request_count_++;
-  if (!registered_)
-    registered_ = registry_->RegisterEvent(event_, callback_);
+  if (!registered_) {
+    registry_->RegisterEvent(event_, callback_);
+    registered_ = true;
+  }
 }
 
 void SyncEventWatcher::DecrementRegisterCount() {
   DCHECK_GT(register_request_count_, 0u);
   register_request_count_--;
   if (register_request_count_ == 0 && registered_) {
-    registry_->UnregisterEvent(event_);
+    registry_->UnregisterEvent(event_, callback_);
     registered_ = false;
   }
 }
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
index 8ac451c7..a7a5a6f 100644
--- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -4,6 +4,8 @@
 
 #include "mojo/public/cpp/bindings/sync_handle_registry.h"
 
+#include <algorithm>
+
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
@@ -62,23 +64,52 @@
   handles_.erase(handle);
 }
 
-bool SyncHandleRegistry::RegisterEvent(base::WaitableEvent* event,
+void SyncHandleRegistry::RegisterEvent(base::WaitableEvent* event,
                                        const base::Closure& callback) {
-  auto result = events_.insert({event, callback});
-  DCHECK(result.second);
-  MojoResult rv = wait_set_.AddEvent(event);
-  if (rv == MOJO_RESULT_OK)
-    return true;
-  DCHECK_EQ(MOJO_RESULT_ALREADY_EXISTS, rv);
-  return false;
+  auto it = events_.find(event);
+  if (it == events_.end()) {
+    auto result = events_.emplace(event, EventCallbackList{});
+    it = result.first;
+  }
+
+  auto& callbacks = it->second.container();
+  if (callbacks.empty()) {
+    // AddEvent() must succeed since we only attempt it when there are
+    // previously no callbacks registered for this event.
+    MojoResult rv = wait_set_.AddEvent(event);
+    DCHECK_EQ(MOJO_RESULT_OK, rv);
+  }
+
+  callbacks.push_back(callback);
 }
 
-void SyncHandleRegistry::UnregisterEvent(base::WaitableEvent* event) {
+void SyncHandleRegistry::UnregisterEvent(base::WaitableEvent* event,
+                                         const base::Closure& callback) {
   auto it = events_.find(event);
-  DCHECK(it != events_.end());
-  events_.erase(it);
-  MojoResult rv = wait_set_.RemoveEvent(event);
-  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  if (it == events_.end())
+    return;
+
+  auto& callbacks = it->second.container();
+  if (is_dispatching_event_callbacks_) {
+    // Not safe to remove any elements from |callbacks| here since an outer
+    // stack frame is currently iterating over it in Wait().
+    for (auto& cb : callbacks) {
+      if (cb.Equals(callback))
+        cb.Reset();
+    }
+    remove_invalid_event_callbacks_after_dispatch_ = true;
+  } else {
+    callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
+                                   [&callback](const base::Closure& cb) {
+                                     return cb.Equals(callback);
+                                   }),
+                    callbacks.end());
+    if (callbacks.empty()) {
+      events_.erase(it);
+      MojoResult rv = wait_set_.RemoveEvent(event);
+      DCHECK_EQ(MOJO_RESULT_OK, rv);
+    }
+  }
 }
 
 bool SyncHandleRegistry::Wait(const bool* should_stop[], size_t count) {
@@ -109,7 +140,29 @@
     if (ready_event) {
       const auto iter = events_.find(ready_event);
       DCHECK(iter != events_.end());
-      iter->second.Run();
+      bool was_dispatching_event_callbacks = is_dispatching_event_callbacks_;
+      is_dispatching_event_callbacks_ = true;
+
+      // NOTE: It's possible for the container to be extended by any of these
+      // callbacks if they call RegisterEvent, so we are careful to iterate by
+      // index. Also note that conversely, elements cannot be *removed* from the
+      // container, by any of these callbacks, so it is safe to assume the size
+      // only stays the same or increases, with no elements changing position.
+      auto& callbacks = iter->second.container();
+      for (size_t i = 0; i < callbacks.size(); ++i) {
+        auto& callback = callbacks[i];
+        if (callback)
+          callback.Run();
+      }
+
+      is_dispatching_event_callbacks_ = was_dispatching_event_callbacks;
+      if (!was_dispatching_event_callbacks &&
+          remove_invalid_event_callbacks_after_dispatch_) {
+        // If we've had events unregistered within any callback dispatch, now is
+        // a good time to prune them from the map.
+        RemoveInvalidEventCallbacks();
+        remove_invalid_event_callbacks_after_dispatch_ = false;
+      }
     }
   };
 
@@ -120,4 +173,22 @@
 
 SyncHandleRegistry::~SyncHandleRegistry() = default;
 
+void SyncHandleRegistry::RemoveInvalidEventCallbacks() {
+  for (auto it = events_.begin(); it != events_.end();) {
+    auto& callbacks = it->second.container();
+    callbacks.erase(
+        std::remove_if(callbacks.begin(), callbacks.end(),
+                       [](const base::Closure& callback) { return !callback; }),
+        callbacks.end());
+    if (callbacks.empty()) {
+      MojoResult rv = wait_set_.RemoveEvent(it->first);
+      DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+      events_.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+}
+
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/sync_handle_registry.h b/mojo/public/cpp/bindings/sync_handle_registry.h
index de766151..cd535aa 100644
--- a/mojo/public/cpp/bindings/sync_handle_registry.h
+++ b/mojo/public/cpp/bindings/sync_handle_registry.h
@@ -6,9 +6,9 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_SYNC_HANDLE_REGISTRY_H_
 
 #include <map>
-#include <unordered_map>
 
 #include "base/callback.h"
+#include "base/containers/stack_container.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
@@ -30,6 +30,10 @@
   static scoped_refptr<SyncHandleRegistry> current();
 
   using HandleCallback = base::Callback<void(MojoResult)>;
+
+  // Registers a |Handle| to be watched for |handle_signals|. If any such
+  // signals are satisfied during a Wait(), the Wait() is woken up and
+  // |callback| is run.
   bool RegisterHandle(const Handle& handle,
                       MojoHandleSignals handle_signals,
                       const HandleCallback& callback);
@@ -38,11 +42,13 @@
 
   // Registers a |base::WaitableEvent| which can be used to wake up
   // Wait() before any handle signals. |event| is not owned, and if it signals
-  // during Wait(), |callback| is invoked. Returns |true| if registered
-  // successfully or |false| if |event| was already registered.
-  bool RegisterEvent(base::WaitableEvent* event, const base::Closure& callback);
+  // during Wait(), |callback| is invoked.  Note that |event| may be registered
+  // multiple times with different callbacks.
+  void RegisterEvent(base::WaitableEvent* event, const base::Closure& callback);
 
-  void UnregisterEvent(base::WaitableEvent* event);
+  // Unregisters a specific |event|+|callback| pair.
+  void UnregisterEvent(base::WaitableEvent* event,
+                       const base::Closure& callback);
 
   // Waits on all the registered handles and events and runs callbacks
   // synchronously for any that become ready.
@@ -54,12 +60,26 @@
  private:
   friend class base::RefCounted<SyncHandleRegistry>;
 
+  using EventCallbackList = base::StackVector<base::Closure, 1>;
+  using EventMap = std::map<base::WaitableEvent*, EventCallbackList>;
+
   SyncHandleRegistry();
   ~SyncHandleRegistry();
 
+  void RemoveInvalidEventCallbacks();
+
   WaitSet wait_set_;
   std::map<Handle, HandleCallback> handles_;
-  std::map<base::WaitableEvent*, base::Closure> events_;
+  EventMap events_;
+
+  // |true| iff this registry is currently dispatching event callbacks in
+  // Wait(). Used to allow for safe event registration/unregistration from event
+  // callbacks.
+  bool is_dispatching_event_callbacks_ = false;
+
+  // Indicates if one or more event callbacks was unregistered during the most
+  // recent event callback dispatch.
+  bool remove_invalid_event_callbacks_after_dispatch_ = false;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 0632a62..6fe236d 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -19,6 +19,7 @@
   "//content/public/common/typemaps.gni",
   "//device/bluetooth/public/interfaces/typemaps.gni",
   "//device/gamepad/public/interfaces/typemaps.gni",
+  "//device/hid/public/interfaces/typemaps.gni",
   "//extensions/common/typemaps.gni",
   "//gpu/ipc/common/typemaps.gni",
   "//media/capture/mojo/typemaps.gni",
diff --git a/net/quic/chromium/crypto/proof_source_chromium.cc b/net/quic/chromium/crypto/proof_source_chromium.cc
index 4f5608a..1ce25217 100644
--- a/net/quic/chromium/crypto/proof_source_chromium.cc
+++ b/net/quic/chromium/crypto/proof_source_chromium.cc
@@ -153,4 +153,45 @@
   callback->Run(ok, chain, out_proof, nullptr /* details */);
 }
 
+QuicReferenceCountedPointer<ProofSource::Chain>
+ProofSourceChromium::GetCertChain(const QuicSocketAddress& server_address,
+                                  const std::string& hostname) {
+  return chain_;
+}
+
+void ProofSourceChromium::ComputeTlsSignature(
+    const QuicSocketAddress& server_address,
+    const std::string& hostname,
+    uint16_t signature_algorithm,
+    QuicStringPiece in,
+    std::unique_ptr<SignatureCallback> callback) {
+  crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  bssl::ScopedEVP_MD_CTX sign_context;
+  EVP_PKEY_CTX* pkey_ctx;
+
+  size_t siglen;
+  string sig;
+  if (!EVP_DigestSignInit(sign_context.get(), &pkey_ctx, EVP_sha256(), nullptr,
+                          private_key_->key()) ||
+      !EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) ||
+      !EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) ||
+      !EVP_DigestSignUpdate(sign_context.get(),
+                            reinterpret_cast<const uint8_t*>(in.data()),
+                            in.size()) ||
+      !EVP_DigestSignFinal(sign_context.get(), nullptr, &siglen)) {
+    callback->Run(false, sig);
+    return;
+  }
+  sig.resize(siglen);
+  if (!EVP_DigestSignFinal(
+          sign_context.get(),
+          reinterpret_cast<uint8_t*>(const_cast<char*>(sig.data())), &siglen)) {
+    callback->Run(false, sig);
+    return;
+  }
+  sig.resize(siglen);
+
+  callback->Run(true, sig);
+}
+
 }  // namespace net
diff --git a/net/quic/chromium/crypto/proof_source_chromium.h b/net/quic/chromium/crypto/proof_source_chromium.h
index 00f6d3b..896ea5c 100644
--- a/net/quic/chromium/crypto/proof_source_chromium.h
+++ b/net/quic/chromium/crypto/proof_source_chromium.h
@@ -22,6 +22,19 @@
 // TODO(rtenneti): implement details of this class.
 class NET_EXPORT_PRIVATE ProofSourceChromium : public ProofSource {
  public:
+  // TODO(merge): Remove this class.
+  class SignatureCallback {
+   public:
+    SignatureCallback() {}
+    virtual ~SignatureCallback() = default;
+
+    virtual void Run(bool ok, std::string signature) = 0;
+
+   private:
+    SignatureCallback(const SignatureCallback&) = delete;
+    SignatureCallback& operator=(const SignatureCallback&) = delete;
+  };
+
   ProofSourceChromium();
   ~ProofSourceChromium() override;
 
@@ -41,6 +54,18 @@
                 const QuicTagVector& connection_options,
                 std::unique_ptr<Callback> callback) override;
 
+  // TODO(merge): Change this from 'virtual' to 'override'.
+  virtual QuicReferenceCountedPointer<Chain> GetCertChain(
+      const QuicSocketAddress& server_address,
+      const std::string& hostname);
+
+  // TODO(merge): Change this from 'virtual' to 'override'.
+  virtual void ComputeTlsSignature(const QuicSocketAddress& server_address,
+                                   const std::string& hostname,
+                                   uint16_t signature_algorithm,
+                                   QuicStringPiece in,
+                                   std::unique_ptr<SignatureCallback> callback);
+
  private:
   bool GetProofInner(const QuicSocketAddress& server_ip,
                      const std::string& hostname,
diff --git a/net/quic/chromium/crypto/proof_test_chromium.cc b/net/quic/chromium/crypto/proof_test_chromium.cc
index f6bfd15..1d8bbed2 100644
--- a/net/quic/chromium/crypto/proof_test_chromium.cc
+++ b/net/quic/chromium/crypto/proof_test_chromium.cc
@@ -11,12 +11,15 @@
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/x509_certificate.h"
+// TODO(merge): Remove the following include.
+#include "net/quic/chromium/crypto/proof_source_chromium.h"
 #include "net/quic/core/crypto/proof_source.h"
 #include "net/quic/core/crypto/proof_verifier.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
 
 using std::string;
 
@@ -189,6 +192,84 @@
                   first_chlo_hash, wrong_certs, corrupt_signature, false);
 }
 
+namespace {
+// TODO(merge): Change ProofSourceChromium::SignatureCallback to
+// ProofSource::SignatureCallback.
+class TestingSignatureCallback : public ProofSourceChromium::SignatureCallback {
+ public:
+  TestingSignatureCallback(bool* ok_out, std::string* signature_out)
+      : ok_out_(ok_out), signature_out_(signature_out) {}
+
+  void Run(bool ok, std::string signature) override {
+    *ok_out_ = ok;
+    *signature_out_ = std::move(signature);
+  }
+
+ private:
+  bool* ok_out_;
+  std::string* signature_out_;
+};
+
+}  // namespace
+
+TEST_P(ProofTest, TlsSignature) {
+  // TODO(merge): Change 'proof_source' to 'source' and remove the following
+  // static_cast.
+  std::unique_ptr<ProofSource> proof_source(
+      crypto_test_utils::ProofSourceForTesting());
+  std::unique_ptr<ProofSourceChromium> source(
+      static_cast<ProofSourceChromium*>(proof_source.release()));
+
+  QuicSocketAddress server_address;
+  const string hostname = "test.example.com";
+
+  QuicReferenceCountedPointer<ProofSource::Chain> chain =
+      source->GetCertChain(server_address, hostname);
+  ASSERT_GT(chain->certs.size(), 0ul);
+
+  // Generate a value to be signed similar to the example in TLS 1.3 section
+  // 4.4.3. The value to be signed starts with octed 0x20 repeated 64 times,
+  // followed by the context string, followed by a single 0 byte, followed by
+  // the transcript hash. Since there's no TLS stack here, we're using 32 bytes
+  // of 01 as the transcript hash.
+  string to_be_signed(64, ' ');
+  to_be_signed.append("TLS 1.3, server CertificateVerify");
+  to_be_signed.append(1, '\0');
+  to_be_signed.append(32, 1);
+
+  string sig;
+  bool success;
+  std::unique_ptr<TestingSignatureCallback> callback =
+      QuicMakeUnique<TestingSignatureCallback>(&success, &sig);
+  source->ComputeTlsSignature(server_address, hostname, SSL_SIGN_RSA_PSS_SHA256,
+                              to_be_signed, std::move(callback));
+  EXPECT_TRUE(success);
+
+  // Verify that the signature from ComputeTlsSignature can be verified with the
+  // leaf cert from GetCertChain.
+  const uint8_t* data;
+  const uint8_t* orig_data;
+  orig_data = data = reinterpret_cast<const uint8_t*>(chain->certs[0].data());
+  bssl::UniquePtr<X509> leaf(d2i_X509(nullptr, &data, chain->certs[0].size()));
+  ASSERT_NE(leaf.get(), nullptr);
+  EXPECT_EQ(data - orig_data, static_cast<ptrdiff_t>(chain->certs[0].size()));
+  bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(leaf.get()));
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  EVP_PKEY_CTX* ctx;
+  ASSERT_EQ(EVP_DigestVerifyInit(md_ctx.get(), &ctx, EVP_sha256(), nullptr,
+                                 pkey.get()),
+            1);
+  ASSERT_EQ(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING), 1);
+  ASSERT_EQ(EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1), 1);
+  ASSERT_EQ(EVP_DigestVerifyUpdate(md_ctx.get(), to_be_signed.data(),
+                                   to_be_signed.size()),
+            1);
+  EXPECT_EQ(EVP_DigestVerifyFinal(md_ctx.get(),
+                                  reinterpret_cast<const uint8_t*>(sig.data()),
+                                  sig.size()),
+            1);
+}
+
 TEST_P(ProofTest, UseAfterFree) {
   std::unique_ptr<ProofSource> source(
       crypto_test_utils::ProofSourceForTesting());
diff --git a/ppapi/proxy/interface_proxy.h b/ppapi/proxy/interface_proxy.h
index 6cfd7d76..ab62f1b 100644
--- a/ppapi/proxy/interface_proxy.h
+++ b/ppapi/proxy/interface_proxy.h
@@ -23,20 +23,20 @@
   // is transferred to the caller.
   typedef InterfaceProxy* (*Factory)(Dispatcher* dispatcher);
 
-  virtual ~InterfaceProxy();
+  ~InterfaceProxy() override;
 
   Dispatcher* dispatcher() const { return dispatcher_; }
 
   // IPC::Sender implementation.
-  virtual bool Send(IPC::Message* msg);
+  bool Send(IPC::Message* msg) override;
 
   // Sub-classes must implement IPC::Listener which contains this:
-  //virtual bool OnMessageReceived(const IPC::Message& msg);
+  // virtual bool OnMessageReceived(const Message& message) = 0;
 
  protected:
   // Creates the given interface associated with the given dispatcher. The
   // dispatcher manages our lifetime.
-  InterfaceProxy(Dispatcher* dispatcher);
+  explicit InterfaceProxy(Dispatcher* dispatcher);
 
  private:
   Dispatcher* dispatcher_;
diff --git a/ppapi/proxy/ppb_audio_proxy.h b/ppapi/proxy/ppb_audio_proxy.h
index a738104a..b4d3091 100644
--- a/ppapi/proxy/ppb_audio_proxy.h
+++ b/ppapi/proxy/ppb_audio_proxy.h
@@ -32,8 +32,8 @@
 
 class PPB_Audio_Proxy : public InterfaceProxy {
  public:
-  PPB_Audio_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_Audio_Proxy();
+  explicit PPB_Audio_Proxy(Dispatcher* dispatcher);
+  ~PPB_Audio_Proxy() override;
 
   // Creates an Audio object in the plugin process.
   static PP_Resource CreateProxyResource(
@@ -43,7 +43,7 @@
       void* user_data);
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   static const ApiID kApiID = API_ID_PPB_AUDIO;
 
diff --git a/ppapi/proxy/ppb_broker_proxy.h b/ppapi/proxy/ppb_broker_proxy.h
index 7baf3a2..ec29de43 100644
--- a/ppapi/proxy/ppb_broker_proxy.h
+++ b/ppapi/proxy/ppb_broker_proxy.h
@@ -23,12 +23,12 @@
 class PPB_Broker_Proxy : public InterfaceProxy {
  public:
   explicit PPB_Broker_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_Broker_Proxy();
+  ~PPB_Broker_Proxy() override;
 
   static PP_Resource CreateProxyResource(PP_Instance instance);
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   static const ApiID kApiID = API_ID_PPB_BROKER;
 
diff --git a/ppapi/proxy/ppb_buffer_proxy.h b/ppapi/proxy/ppb_buffer_proxy.h
index b3ba248..b36750a2 100644
--- a/ppapi/proxy/ppb_buffer_proxy.h
+++ b/ppapi/proxy/ppb_buffer_proxy.h
@@ -52,7 +52,7 @@
 class PPB_Buffer_Proxy : public InterfaceProxy {
  public:
   explicit PPB_Buffer_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_Buffer_Proxy();
+  ~PPB_Buffer_Proxy() override;
 
   static PP_Resource CreateProxyResource(PP_Instance instance,
                                          uint32_t size);
@@ -61,7 +61,7 @@
                                       uint32_t size);
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   static const ApiID kApiID = API_ID_PPB_BUFFER;
 
diff --git a/ppapi/proxy/ppb_core_proxy.h b/ppapi/proxy/ppb_core_proxy.h
index a564519..aea10964 100644
--- a/ppapi/proxy/ppb_core_proxy.h
+++ b/ppapi/proxy/ppb_core_proxy.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 PPAPI_PPB_CORE_PROXY_H_
-#define PPAPI_PPB_CORE_PROXY_H_
+#ifndef PPAPI_PROXY_PPB_CORE_PROXY_H_
+#define PPAPI_PROXY_PPB_CORE_PROXY_H_
 
 #include "ppapi/c/pp_completion_callback.h"
 #include "ppapi/c/pp_resource.h"
@@ -17,13 +17,13 @@
 
 class PPB_Core_Proxy : public InterfaceProxy {
  public:
-  PPB_Core_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_Core_Proxy();
+  explicit PPB_Core_Proxy(Dispatcher* dispatcher);
+  ~PPB_Core_Proxy() override;
 
   static const PPB_Core* GetPPB_Core_Interface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   static const ApiID kApiID = API_ID_PPB_CORE;
 
@@ -41,4 +41,4 @@
 }  // namespace proxy
 }  // namespace ppapi
 
-#endif  // PPAPI_PPB_CORE_PROXY_H_
+#endif  // PPAPI_PROXY_PPB_CORE_PROXY_H_
diff --git a/ppapi/proxy/ppb_message_loop_proxy.h b/ppapi/proxy/ppb_message_loop_proxy.h
index f78c3772..9c15539 100644
--- a/ppapi/proxy/ppb_message_loop_proxy.h
+++ b/ppapi/proxy/ppb_message_loop_proxy.h
@@ -121,7 +121,7 @@
 class PPB_MessageLoop_Proxy : public InterfaceProxy {
  public:
   explicit PPB_MessageLoop_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_MessageLoop_Proxy();
+  ~PPB_MessageLoop_Proxy() override;
 
   static const PPB_MessageLoop_1_0* GetInterface();
 
diff --git a/ppapi/proxy/ppb_testing_proxy.h b/ppapi/proxy/ppb_testing_proxy.h
index c4b149ac..8340e79 100644
--- a/ppapi/proxy/ppb_testing_proxy.h
+++ b/ppapi/proxy/ppb_testing_proxy.h
@@ -24,13 +24,13 @@
 
 class PPB_Testing_Proxy : public InterfaceProxy {
  public:
-  PPB_Testing_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_Testing_Proxy();
+  explicit PPB_Testing_Proxy(Dispatcher* dispatcher);
+  ~PPB_Testing_Proxy() override;
 
   static const PPB_Testing_Private* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppb_var_deprecated_proxy.h b/ppapi/proxy/ppb_var_deprecated_proxy.h
index 508b492..a281018 100644
--- a/ppapi/proxy/ppb_var_deprecated_proxy.h
+++ b/ppapi/proxy/ppb_var_deprecated_proxy.h
@@ -28,12 +28,12 @@
 class PPB_Var_Deprecated_Proxy : public InterfaceProxy {
  public:
   explicit PPB_Var_Deprecated_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_Var_Deprecated_Proxy();
+  ~PPB_Var_Deprecated_Proxy() override;
 
   static const PPB_Var_Deprecated* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppb_video_decoder_proxy.h b/ppapi/proxy/ppb_video_decoder_proxy.h
index 0b2a6f69..a74a874 100644
--- a/ppapi/proxy/ppb_video_decoder_proxy.h
+++ b/ppapi/proxy/ppb_video_decoder_proxy.h
@@ -20,8 +20,8 @@
 
 class PPB_VideoDecoder_Proxy : public InterfaceProxy {
  public:
-  PPB_VideoDecoder_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_VideoDecoder_Proxy();
+  explicit PPB_VideoDecoder_Proxy(Dispatcher* dispatcher);
+  ~PPB_VideoDecoder_Proxy() override;
 
   // Creates a VideoDecoder object in the plugin process.
   static PP_Resource CreateProxyResource(
@@ -30,7 +30,7 @@
       PP_VideoDecoder_Profile profile);
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   static const ApiID kApiID = API_ID_PPB_VIDEO_DECODER_DEV;
 
diff --git a/ppapi/proxy/ppp_class_proxy.h b/ppapi/proxy/ppp_class_proxy.h
index c70b564..8ee1189a 100644
--- a/ppapi/proxy/ppp_class_proxy.h
+++ b/ppapi/proxy/ppp_class_proxy.h
@@ -30,7 +30,7 @@
   // PPP_Class isn't a normal interface that you can query for, so this
   // constructor doesn't take an interface pointer.
   explicit PPP_Class_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Class_Proxy();
+  ~PPP_Class_Proxy() override;
 
   // Factory function used for registration (normal code can just use the
   // constructor).
@@ -50,7 +50,7 @@
                               int64_t* ppp_class_data);
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // IPC message handlers.
diff --git a/ppapi/proxy/ppp_content_decryptor_private_proxy.h b/ppapi/proxy/ppp_content_decryptor_private_proxy.h
index 5987452..7a35292 100644
--- a/ppapi/proxy/ppp_content_decryptor_private_proxy.h
+++ b/ppapi/proxy/ppp_content_decryptor_private_proxy.h
@@ -25,13 +25,13 @@
 class PPP_ContentDecryptor_Private_Proxy : public InterfaceProxy {
  public:
   explicit PPP_ContentDecryptor_Private_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_ContentDecryptor_Private_Proxy();
+  ~PPP_ContentDecryptor_Private_Proxy() override;
 
   static const PPP_ContentDecryptor_Private* GetProxyInterface();
 
  private:
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   // Message handlers.
   void OnMsgInitialize(PP_Instance instance,
diff --git a/ppapi/proxy/ppp_find_proxy.h b/ppapi/proxy/ppp_find_proxy.h
index 8e07d7e..4e0b783 100644
--- a/ppapi/proxy/ppp_find_proxy.h
+++ b/ppapi/proxy/ppp_find_proxy.h
@@ -18,12 +18,12 @@
 class PPP_Find_Proxy : public InterfaceProxy {
  public:
   explicit PPP_Find_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Find_Proxy();
+  ~PPP_Find_Proxy() override;
 
   static const PPP_Find_Private* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppp_graphics_3d_proxy.h b/ppapi/proxy/ppp_graphics_3d_proxy.h
index 6897e6b..1b6e91a 100644
--- a/ppapi/proxy/ppp_graphics_3d_proxy.h
+++ b/ppapi/proxy/ppp_graphics_3d_proxy.h
@@ -16,13 +16,13 @@
 
 class PPP_Graphics3D_Proxy : public InterfaceProxy {
  public:
-  PPP_Graphics3D_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Graphics3D_Proxy();
+  explicit PPP_Graphics3D_Proxy(Dispatcher* dispatcher);
+  ~PPP_Graphics3D_Proxy() override;
 
   static const PPP_Graphics3D* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppp_input_event_proxy.h b/ppapi/proxy/ppp_input_event_proxy.h
index 01479da7..753714e 100644
--- a/ppapi/proxy/ppp_input_event_proxy.h
+++ b/ppapi/proxy/ppp_input_event_proxy.h
@@ -18,13 +18,13 @@
 
 class PPP_InputEvent_Proxy : public InterfaceProxy {
  public:
-  PPP_InputEvent_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_InputEvent_Proxy();
+  explicit PPP_InputEvent_Proxy(Dispatcher* dispatcher);
+  ~PPP_InputEvent_Proxy() override;
 
   static const PPP_InputEvent* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppp_instance_private_proxy.h b/ppapi/proxy/ppp_instance_private_proxy.h
index f430203..f4ed0b8 100644
--- a/ppapi/proxy/ppp_instance_private_proxy.h
+++ b/ppapi/proxy/ppp_instance_private_proxy.h
@@ -20,14 +20,14 @@
 
 class PPP_Instance_Private_Proxy : public InterfaceProxy {
  public:
-  PPP_Instance_Private_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Instance_Private_Proxy();
+  explicit PPP_Instance_Private_Proxy(Dispatcher* dispatcher);
+  ~PPP_Instance_Private_Proxy() override;
 
   static const PPP_Instance_Private* GetProxyInterface();
 
  private:
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
   // Message handlers.
   void OnMsgGetInstanceObject(PP_Instance instance,
diff --git a/ppapi/proxy/ppp_instance_proxy.h b/ppapi/proxy/ppp_instance_proxy.h
index 7eda2bd..c8a4b464 100644
--- a/ppapi/proxy/ppp_instance_proxy.h
+++ b/ppapi/proxy/ppp_instance_proxy.h
@@ -26,7 +26,7 @@
 class PPP_Instance_Proxy : public InterfaceProxy {
  public:
   explicit PPP_Instance_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Instance_Proxy();
+  ~PPP_Instance_Proxy() override;
 
   static const PPP_Instance* GetInstanceInterface();
 
@@ -35,7 +35,7 @@
   }
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppp_pdf_proxy.h b/ppapi/proxy/ppp_pdf_proxy.h
index 94ddd1d..ff373fdf7 100644
--- a/ppapi/proxy/ppp_pdf_proxy.h
+++ b/ppapi/proxy/ppp_pdf_proxy.h
@@ -15,13 +15,13 @@
 
 class PPP_Pdf_Proxy : public InterfaceProxy {
  public:
-  PPP_Pdf_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Pdf_Proxy();
+  explicit PPP_Pdf_Proxy(Dispatcher* dispatcher);
+  ~PPP_Pdf_Proxy() override;
 
   static const PPP_Pdf* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppp_printing_proxy.h b/ppapi/proxy/ppp_printing_proxy.h
index 7c4025d..6f212bbb 100644
--- a/ppapi/proxy/ppp_printing_proxy.h
+++ b/ppapi/proxy/ppp_printing_proxy.h
@@ -24,13 +24,13 @@
 
 class PPP_Printing_Proxy : public InterfaceProxy {
  public:
-  PPP_Printing_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_Printing_Proxy();
+  explicit PPP_Printing_Proxy(Dispatcher* dispatcher);
+  ~PPP_Printing_Proxy() override;
 
   static const PPP_Printing_Dev* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/ppapi/proxy/ppp_video_decoder_proxy.h b/ppapi/proxy/ppp_video_decoder_proxy.h
index c4b23ef..2429ce6 100644
--- a/ppapi/proxy/ppp_video_decoder_proxy.h
+++ b/ppapi/proxy/ppp_video_decoder_proxy.h
@@ -21,13 +21,13 @@
 
 class PPP_VideoDecoder_Proxy : public InterfaceProxy {
  public:
-  PPP_VideoDecoder_Proxy(Dispatcher* dispatcher);
-  virtual ~PPP_VideoDecoder_Proxy();
+  explicit PPP_VideoDecoder_Proxy(Dispatcher* dispatcher);
+  ~PPP_VideoDecoder_Proxy() override;
 
   static const PPP_VideoDecoder_Dev* GetProxyInterface();
 
   // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
+  bool OnMessageReceived(const IPC::Message& msg) override;
 
  private:
   // Message handlers.
diff --git a/remoting/host/input_injector_win.cc b/remoting/host/input_injector_win.cc
index b7d31bb..93dc558 100644
--- a/remoting/host/input_injector_win.cc
+++ b/remoting/host/input_injector_win.cc
@@ -415,15 +415,8 @@
     input[1].ki.wVk = VK_CAPITAL;
     SendInput(arraysize(input), input, sizeof(INPUT));
   }
-
-  bool client_numlock_state =
-      (states & protocol::KeyEvent::LOCK_STATES_NUMLOCK) != 0;
-  bool host_numlock_state = (GetKeyState(VK_NUMLOCK) & 1) != 0;
-  if (client_numlock_state != host_numlock_state) {
-    input[0].ki.wVk = VK_NUMLOCK;
-    input[1].ki.wVk = VK_NUMLOCK;
-    SendInput(arraysize(input), input, sizeof(INPUT));
-  }
+  // TODO(jamiewalch): Reinstate NumLock synchronization when the protocol
+  //     supports the client reporting it as "unknown".
 }
 
 }  // namespace
diff --git a/remoting/host/input_injector_x11.cc b/remoting/host/input_injector_x11.cc
index 71b501b5..509b4531 100644
--- a/remoting/host/input_injector_x11.cc
+++ b/remoting/host/input_injector_x11.cc
@@ -371,17 +371,14 @@
 }
 
 void InputInjectorX11::Core::SetLockStates(uint32_t states) {
+  // TODO(jamiewalch): Reinstate NumLock synchronization when the protocol
+  //     supports the client reporting it as "unknown".
   unsigned int caps_lock_mask = XkbKeysymToModifiers(display_, XK_Caps_Lock);
-  unsigned int num_lock_mask = XkbKeysymToModifiers(display_, XK_Num_Lock);
   unsigned int lock_values = 0;
   if (states & protocol::KeyEvent::LOCK_STATES_CAPSLOCK) {
     lock_values |= caps_lock_mask;
   }
-  if (states & protocol::KeyEvent::LOCK_STATES_NUMLOCK) {
-    lock_values |= num_lock_mask;
-  }
-  XkbLockModifiers(display_, XkbUseCoreKbd, caps_lock_mask | num_lock_mask,
-                   lock_values);
+  XkbLockModifiers(display_, XkbUseCoreKbd, caps_lock_mask, lock_values);
 }
 
 void InputInjectorX11::Core::InjectScrollWheelClicks(int button, int count) {
diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py
index f026d68f..8302e290 100755
--- a/remoting/host/linux/linux_me2me_host.py
+++ b/remoting/host/linux/linux_me2me_host.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python2
 # 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.
@@ -11,6 +11,12 @@
 
 from __future__ import print_function
 
+import sys
+if sys.version_info[0] != 2 or sys.version_info[1] < 7:
+  print("This script requires Python version 2.7")
+  sys.exit(1)
+
+import argparse
 import atexit
 import errno
 import fcntl
@@ -19,7 +25,6 @@
 import hashlib
 import json
 import logging
-import optparse
 import os
 import pipes
 import platform
@@ -30,7 +35,6 @@
 import signal
 import socket
 import subprocess
-import sys
 import tempfile
 import threading
 import time
@@ -791,9 +795,40 @@
       self.host_proc.stdin.close()
 
 
-def get_daemon_proc():
-  """Checks if there is already an instance of this script running, and returns
-  a psutil.Process instance for it.
+def parse_config_arg(args):
+  """Parses only the --config option from a given command-line.
+
+  Returns:
+    A two-tuple. The first element is the value of the --config option (or None
+    if it is not specified), and the second is a list containing the remaining
+    arguments
+  """
+
+  # By default, argparse will exit the program on error. We would like it not to
+  # do that.
+  class ArgumentParserError(Exception):
+    pass
+  class ThrowingArgumentParser(argparse.ArgumentParser):
+    def error(self, message):
+      raise ArgumentParserError(message)
+
+  parser = ThrowingArgumentParser()
+  parser.add_argument("--config", nargs='?', action="store")
+
+  try:
+    result = parser.parse_known_args(args)
+    return (result[0].config, result[1])
+  except ArgumentParserError:
+    return (None, list(args))
+
+
+def get_daemon_proc(config_file):
+  """Checks if there is already an instance of this script running against
+  |config_file|, and returns a psutil.Process instance for it.
+
+  If a process is found without --config in the command line, get_daemon_proc
+  will fall back to the old behavior of checking whether the script path matches
+  the current script. This is to facilitate upgrades from previous versions.
 
   Returns:
     A Process instance for the existing daemon process, or None if the daemon
@@ -826,8 +861,18 @@
       cmdline = psget(process.cmdline)
       if len(cmdline) < 2:
         continue
-      if cmdline[0] == sys.executable and cmdline[1] == sys.argv[0]:
-        return process
+      if (os.path.basename(cmdline[0]).startswith('python')
+          and os.path.basename(cmdline[1]) == os.path.basename(sys.argv[0])):
+        process_config = parse_config_arg(cmdline[2:])[0]
+        if process_config is None:
+          # Fall back to old behavior if there is no --config argument
+          # TODO(rkjnsn): Consider removing this fallback once sufficient time
+          # has passed.
+          if cmdline[1] == sys.argv[0]:
+            return process
+        elif process_config == config_file:
+          return process
+
     except (psutil.NoSuchProcess, psutil.AccessDenied):
       continue
 
@@ -1323,57 +1368,63 @@
   EPILOG = """This script is not intended for use by end-users.  To configure
 Chrome Remote Desktop, please install the app from the Chrome
 Web Store: https://chrome.google.com/remotedesktop"""
-  parser = optparse.OptionParser(
-      usage="Usage: %prog [options] [ -- [ X server options ] ]",
+  parser = argparse.ArgumentParser(
+      usage="Usage: %(prog)s [options] [ -- [ X server options ] ]",
       epilog=EPILOG)
-  parser.add_option("-s", "--size", dest="size", action="append",
-                    help="Dimensions of virtual desktop. This can be specified "
-                    "multiple times to make multiple screen resolutions "
-                    "available (if the X server supports this).")
-  parser.add_option("-f", "--foreground", dest="foreground", default=False,
-                    action="store_true",
-                    help="Don't run as a background daemon.")
-  parser.add_option("", "--start", dest="start", default=False,
-                    action="store_true",
-                    help="Start the host.")
-  parser.add_option("-k", "--stop", dest="stop", default=False,
-                    action="store_true",
-                    help="Stop the daemon currently running.")
-  parser.add_option("", "--get-status", dest="get_status", default=False,
-                    action="store_true",
-                    help="Prints host status")
-  parser.add_option("", "--check-running", dest="check_running", default=False,
-                    action="store_true",
-                    help="Return 0 if the daemon is running, or 1 otherwise.")
-  parser.add_option("", "--config", dest="config", action="store",
-                    help="Use the specified configuration file.")
-  parser.add_option("", "--reload", dest="reload", default=False,
-                    action="store_true",
-                    help="Signal currently running host to reload the config.")
-  parser.add_option("", "--add-user", dest="add_user", default=False,
-                    action="store_true",
-                    help="Add current user to the chrome-remote-desktop group.")
-  parser.add_option("", "--add-user-as-root", dest="add_user_as_root",
-                    action="store", metavar="USER",
-                    help="Adds the specified user to the chrome-remote-desktop "
-                    "group (must be run as root).")
+  parser.add_argument("-s", "--size", dest="size", action="append",
+                      help="Dimensions of virtual desktop. This can be "
+                      "specified multiple times to make multiple screen "
+                      "resolutions available (if the X server supports this).")
+  parser.add_argument("-f", "--foreground", dest="foreground", default=False,
+                      action="store_true",
+                      help="Don't run as a background daemon.")
+  parser.add_argument("--start", dest="start", default=False,
+                      action="store_true",
+                      help="Start the host.")
+  parser.add_argument("-k", "--stop", dest="stop", default=False,
+                      action="store_true",
+                      help="Stop the daemon currently running.")
+  parser.add_argument("--get-status", dest="get_status", default=False,
+                      action="store_true",
+                      help="Prints host status")
+  parser.add_argument("--check-running", dest="check_running",
+                      default=False, action="store_true",
+                      help="Return 0 if the daemon is running, or 1 otherwise.")
+  parser.add_argument("--config", dest="config", action="store",
+                      help="Use the specified configuration file.")
+  parser.add_argument("--reload", dest="reload", default=False,
+                      action="store_true",
+                      help="Signal currently running host to reload the "
+                      "config.")
+  parser.add_argument("--add-user", dest="add_user", default=False,
+                      action="store_true",
+                      help="Add current user to the chrome-remote-desktop "
+                      "group.")
+  parser.add_argument("--add-user-as-root", dest="add_user_as_root",
+                      action="store", metavar="USER",
+                      help="Adds the specified user to the "
+                      "chrome-remote-desktop group (must be run as root).")
   # The script is being run as a child process under the user-session binary.
   # Don't daemonize and use the inherited environment.
-  parser.add_option("", "--child-process", dest="child_process", default=False,
-                    action="store_true",
-                    help=optparse.SUPPRESS_HELP)
-  parser.add_option("", "--watch-resolution", dest="watch_resolution",
-                    type="int", nargs=2, default=False, action="store",
-                    help=optparse.SUPPRESS_HELP)
-  (options, args) = parser.parse_args()
+  parser.add_argument("--child-process", dest="child_process", default=False,
+                      action="store_true",
+                      help=argparse.SUPPRESS)
+  parser.add_argument("--watch-resolution", dest="watch_resolution",
+                      type=int, nargs=2, default=False, action="store",
+                      help=argparse.SUPPRESS)
+  parser.add_argument(dest="args", nargs="*", help=argparse.SUPPRESS)
+  options = parser.parse_args()
 
-  # Determine the filename of the host configuration and PID files.
-  if not options.config:
-    options.config = os.path.join(CONFIG_DIR, "host#%s.json" % g_host_hash)
+  # Determine the filename of the host configuration.
+  if options.config:
+    config_file = options.config
+  else:
+    config_file = os.path.join(CONFIG_DIR, "host#%s.json" % g_host_hash)
+  config_file = os.path.realpath(config_file)
 
   # Check for a modal command-line option (start, stop, etc.)
   if options.get_status:
-    proc = get_daemon_proc()
+    proc = get_daemon_proc(config_file)
     if proc is not None:
       print("STARTED")
     elif is_supported_platform():
@@ -1385,11 +1436,11 @@
   # TODO(sergeyu): Remove --check-running once NPAPI plugin and NM host are
   # updated to always use get-status flag instead.
   if options.check_running:
-    proc = get_daemon_proc()
+    proc = get_daemon_proc(config_file)
     return 1 if proc is None else 0
 
   if options.stop:
-    proc = get_daemon_proc()
+    proc = get_daemon_proc(config_file)
     if proc is None:
       print("The daemon is not currently running")
     else:
@@ -1403,7 +1454,7 @@
     return 0
 
   if options.reload:
-    proc = get_daemon_proc()
+    proc = get_daemon_proc(config_file)
     if proc is None:
       return 1
     proc.send_signal(signal.SIGHUP)
@@ -1466,6 +1517,21 @@
     print(EPILOG, file=sys.stderr)
     return 1
 
+  # Determine whether a desktop is already active for the specified host
+  # configuration.
+  proc = get_daemon_proc(config_file)
+  if proc is not None:
+    # Debian policy requires that services should "start" cleanly and return 0
+    # if they are already running.
+    print("Service already running.")
+    return 0
+
+  if config_file != options.config:
+    # Canonicalize config flag so get_daemon_proc can find us.
+    exec_args = [sys.argv[0], "--config=" + config_file]
+    exec_args += parse_config_arg(sys.argv[1:])[1]
+    os.execvp(SCRIPT_PATH, exec_args)
+
   # If a RANDR-supporting Xvfb is not available, limit the default size to
   # something more sensible.
   if USE_XORG_ENV_VAR not in os.environ and locate_xvfb_randr():
@@ -1502,7 +1568,7 @@
   atexit.register(cleanup)
 
   # Load the initial host configuration.
-  host_config = Config(options.config)
+  host_config = Config(config_file)
   try:
     host_config.load()
   except (IOError, ValueError) as e:
@@ -1522,15 +1588,6 @@
     logging.error("Failed to load host configuration.")
     return 1
 
-  # Determine whether a desktop is already active for the specified host
-  # host configuration.
-  proc = get_daemon_proc()
-  if proc is not None:
-    # Debian policy requires that services should "start" cleanly and return 0
-    # if they are already running.
-    print("Service already running.")
-    return 0
-
   # If we're running under user-session, try to open the messaging pipe.
   if options.child_process:
     # Log to existing messaging pipe if it exists.
@@ -1605,7 +1662,7 @@
           relaunch_times.append(x_server_inhibitor.earliest_relaunch_time)
         else:
           logging.info("Launching X server and X session.")
-          desktop.launch_session(options.child_process, args)
+          desktop.launch_session(options.child_process, options.args)
           x_server_inhibitor.record_started(MINIMUM_PROCESS_LIFETIME,
                                             backoff_time)
           allow_relaunch_self = True
diff --git a/remoting/host/setup/daemon_controller_delegate_linux.cc b/remoting/host/setup/daemon_controller_delegate_linux.cc
index d2a9816..addc529 100644
--- a/remoting/host/setup/daemon_controller_delegate_linux.cc
+++ b/remoting/host/setup/daemon_controller_delegate_linux.cc
@@ -220,8 +220,8 @@
     return;
   }
 
-  std::vector<std::string> args;
-  args.push_back("--reload");
+  std::vector<std::string> args = {"--reload",
+                                   "--config=" + GetConfigPath().value()};
   DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
   if (RunHostScript(args))
     result = DaemonController::RESULT_OK;
@@ -231,8 +231,8 @@
 
 void DaemonControllerDelegateLinux::Stop(
     const DaemonController::CompletionCallback& done) {
-  std::vector<std::string> args;
-  args.push_back("--stop");
+  std::vector<std::string> args = {"--stop",
+                                   "--config=" + GetConfigPath().value()};
   DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
   if (RunHostScript(args))
     result = DaemonController::RESULT_OK;
diff --git a/services/device/generic_sensor/platform_sensor.cc b/services/device/generic_sensor/platform_sensor.cc
index 32e3708..42018d5 100644
--- a/services/device/generic_sensor/platform_sensor.cc
+++ b/services/device/generic_sensor/platform_sensor.cc
@@ -26,7 +26,7 @@
 
 PlatformSensor::~PlatformSensor() {
   if (provider_)
-    provider_->RemoveSensor(GetType());
+    provider_->RemoveSensor(GetType(), this);
 }
 
 mojom::SensorType PlatformSensor::GetType() const {
diff --git a/services/device/generic_sensor/platform_sensor_provider_base.cc b/services/device/generic_sensor/platform_sensor_provider_base.cc
index 51f63c3..f73b75146 100644
--- a/services/device/generic_sensor/platform_sensor_provider_base.cc
+++ b/services/device/generic_sensor/platform_sensor_provider_base.cc
@@ -74,9 +74,24 @@
   return shared_buffer_handle_.is_valid();
 }
 
-void PlatformSensorProviderBase::RemoveSensor(mojom::SensorType type) {
+void PlatformSensorProviderBase::RemoveSensor(mojom::SensorType type,
+                                              PlatformSensor* sensor) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(ContainsKey(sensor_map_, type));
+  auto it = sensor_map_.find(type);
+  if (it == sensor_map_.end()) {
+    // It is possible on PlatformSensorFusion creation failure since the
+    // PlatformSensorFusion object is not added to the |sensor_map_|, but
+    // its base class destructor PlatformSensor::~PlatformSensor() calls this
+    // RemoveSensor() function with the PlatformSensorFusion type.
+    return;
+  }
+
+  if (sensor != it->second) {
+    NOTREACHED()
+        << "not expecting to track more than one sensor of the same type";
+    return;
+  }
+
   sensor_map_.erase(type);
 
   if (sensor_map_.empty()) {
diff --git a/services/device/generic_sensor/platform_sensor_provider_base.h b/services/device/generic_sensor/platform_sensor_provider_base.h
index 77926f78..ac4e3c7f 100644
--- a/services/device/generic_sensor/platform_sensor_provider_base.h
+++ b/services/device/generic_sensor/platform_sensor_provider_base.h
@@ -66,7 +66,7 @@
   friend class PlatformSensor;  // To call RemoveSensor();
 
   bool CreateSharedBufferIfNeeded();
-  void RemoveSensor(mojom::SensorType type);
+  void RemoveSensor(mojom::SensorType type, PlatformSensor* sensor);
 
  private:
   using CallbackQueue = std::vector<CreateSensorCallback>;
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index 5e932f23..5fd4a6f 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -86,20 +86,20 @@
   mouse_button_down_ = false;
 }
 
-void EventDispatcher::SetMousePointerDisplayLocation(
-    const gfx::Point& display_location,
-    int64_t display_id) {
+std::unique_ptr<ui::Event> EventDispatcher::GenerateMouseMoveFor(
+    const gfx::Point& display_location) const {
   // Create a synthetic mouse event and dispatch it directly to ourselves so we
   // update internal caches and possibly send exit events in case the window
   // the cursor is over changes.
-  ui::PointerEvent move_event(
+  // TODO: This uses state here that may be out of sync at the time the event is
+  // actually processed. Fix.
+  std::unique_ptr<PointerEvent> event = base::MakeUnique<PointerEvent>(
       ui::ET_POINTER_MOVED, display_location, display_location,
       next_mouse_button_flags_, 0 /* changed_button_flags */,
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE,
                          ui::MouseEvent::kMousePointerId),
       base::TimeTicks::Now());
-  ProcessEvent(move_event, display_id,
-               EventDispatcher::AcceleratorMatchPhase::ANY);
+  return event;
 }
 
 ui::CursorData EventDispatcher::GetCurrentMouseCursor() const {
@@ -223,7 +223,7 @@
 }
 
 void EventDispatcher::UpdateNonClientAreaForCurrentWindow() {
-  if (mouse_cursor_source_window_) {
+  if (!mouse_button_down_ && mouse_cursor_source_window_) {
     event_targeter_->FindTargetForLocation(
         EventSource::MOUSE,
         {mouse_pointer_last_location_, mouse_pointer_display_id_},
@@ -285,7 +285,7 @@
 }
 
 bool EventDispatcher::IsProcessingEvent() const {
-  return event_targeter_->IsHitTestInFlight();
+  return waiting_on_event_targeter_;
 }
 
 void EventDispatcher::ProcessEvent(const ui::Event& event,
@@ -323,13 +323,22 @@
   }
 
   DCHECK(event.IsPointerEvent());
+  DCHECK(!waiting_on_event_targeter_);
   const EventSource event_source =
       event.IsMousePointerEvent() ? EventSource::MOUSE : EventSource::TOUCH;
-  event_targeter_->FindTargetForLocation(
-      event_source,
-      {event.AsPointerEvent()->root_location(), event_display_id_},
-      base::BindOnce(&EventDispatcher::ProcessPointerEventOnFoundTarget,
-                     base::Unretained(this), *event.AsPointerEvent()));
+  if (ShouldUseEventTargeter(*event.AsPointerEvent())) {
+    waiting_on_event_targeter_ = true;
+    event_targeter_->FindTargetForLocation(
+        event_source,
+        {event.AsPointerEvent()->root_location(), event_display_id_},
+        base::BindOnce(&EventDispatcher::ProcessPointerEventOnFoundTarget,
+                       base::Unretained(this), *event.AsPointerEvent()));
+  } else {
+    ProcessPointerEventOnFoundTargetImpl(
+        *event.AsPointerEvent(),
+        GetDisplayLocationFromEvent(*event.AsPointerEvent(), display_id),
+        nullptr);
+  }
 }
 
 ServerWindow* EventDispatcher::GetRootWindowContaining(
@@ -352,6 +361,15 @@
   return delegate_->GetWindowFromFrameSinkId(frame_sink_id);
 }
 
+DisplayLocation EventDispatcher::GetDisplayLocationFromEvent(
+    const ui::PointerEvent& event,
+    int64_t display_id) const {
+  DisplayLocation display_location{event.root_location(), display_id};
+  delegate_->GetRootWindowContaining(&display_location.location,
+                                     &display_location.display_id);
+  return display_location;
+}
+
 DeepestWindow EventDispatcher::AdjustTargetForModal(
     const DeepestWindow& target) const {
   const ServerWindow* modal_transient =
@@ -438,10 +456,37 @@
     delegate_->OnEventChangesCursorVisibility(event, false);
 }
 
+bool EventDispatcher::ShouldUseEventTargeter(const PointerEvent& event) const {
+  const int32_t pointer_id = event.pointer_details().id;
+  if (drag_controller_)
+    return true;
+
+  if (capture_window_)
+    return false;
+
+  auto iter = pointer_targets_.find(pointer_id);
+  if (iter == pointer_targets_.end() || !iter->second.is_pointer_down)
+    return true;
+
+  return (event.IsMousePointerEvent() && IsPointerGoingUp(event)) ||
+         event.type() == ET_POINTER_DOWN;
+}
+
 void EventDispatcher::ProcessPointerEventOnFoundTarget(
     const ui::PointerEvent& event,
     const DisplayLocation& display_location,
-    const DeepestWindow& found_target) {
+    const DeepestWindow& target) {
+  DCHECK(waiting_on_event_targeter_);
+  waiting_on_event_targeter_ = false;
+  ProcessPointerEventOnFoundTargetImpl(event, display_location, &target);
+}
+
+void EventDispatcher::ProcessPointerEventOnFoundTargetImpl(
+    const ui::PointerEvent& event,
+    const DisplayLocation& display_location,
+    const DeepestWindow* found_target) {
+  DCHECK(!waiting_on_event_targeter_);
+  // WARNING: |found_target| may be null!
   std::unique_ptr<ui::Event> cloned_event = ui::Event::Clone(event);
   if (display_location.display_id != event_display_id_) {
     event_display_id_ = display_location.display_id;
@@ -468,10 +513,13 @@
     }
   }
 
-  if (drag_controller_ && drag_controller_->DispatchPointerEvent(
-                              *cloned_event->AsPointerEvent(),
-                              AdjustTargetForModal(found_target).window)) {
-    return;
+  if (drag_controller_) {
+    DCHECK(found_target);
+    if (drag_controller_->DispatchPointerEvent(
+            *cloned_event->AsPointerEvent(),
+            AdjustTargetForModal(*found_target).window)) {
+      return;
+    }
   }
 
   if (capture_window_) {
@@ -481,20 +529,26 @@
     return;
   }
 
-  DeepestWindow updated_target = AdjustTargetForModal(found_target);
-  PointerTarget pointer_target;
-  pointer_target.is_mouse_event = is_mouse_event;
-  pointer_target.window = updated_target.window;
-  pointer_target.in_nonclient_area = updated_target.in_non_client_area;
-  pointer_target.is_pointer_down = event.type() == ui::ET_POINTER_DOWN;
+  std::unique_ptr<DeepestWindowAndTarget> result;
+  if (found_target) {
+    result = base::MakeUnique<DeepestWindowAndTarget>();
+    result->deepest_window = AdjustTargetForModal(*found_target);
+    result->pointer_target.is_mouse_event = is_mouse_event;
+    result->pointer_target.window = result->deepest_window.window;
+    result->pointer_target.in_nonclient_area =
+        result->deepest_window.in_non_client_area;
+    result->pointer_target.is_pointer_down =
+        event.type() == ui::ET_POINTER_DOWN;
+  }
 
   const int32_t pointer_id = event.pointer_details().id;
 
   if (!IsTrackingPointer(pointer_id) ||
       !pointer_targets_[pointer_id].is_pointer_down) {
+    DCHECK(result);
     const bool any_pointers_down = AreAnyPointersDown();
     UpdateTargetForPointer(pointer_id, *cloned_event->AsPointerEvent(),
-                           pointer_target);
+                           result->pointer_target);
     if (is_mouse_event)
       SetMouseCursorSourceWindow(pointer_targets_[pointer_id].window);
 
@@ -523,8 +577,10 @@
   // up event to the window that had implicit capture. We have to set this
   // before we perform dispatch because the Delegate is going to read this
   // information from us.
-  if (is_pointer_going_up && is_mouse_event)
-    UpdateCursorProvider(updated_target);
+  if (is_pointer_going_up && is_mouse_event) {
+    DCHECK(result);
+    UpdateCursorProvider(result->deepest_window);
+  }
 
   DispatchToPointerTarget(pointer_targets_[pointer_id],
                           *cloned_event->AsPointerEvent());
@@ -538,10 +594,13 @@
       delegate_->ReleaseNativeCapture();
   }
 
-  // Use |found_target| as |updated_target| has already been adjusted for the
-  // modal window.
-  if (event.type() == ET_POINTER_DOWN && found_target.window)
-    HandleClickOnBlockedWindow(found_target);
+  if (event.type() == ET_POINTER_DOWN) {
+    // Use |found_target| as |result| has already been adjusted for the
+    // modal window.
+    DCHECK(found_target);
+    if (found_target->window)
+      HandleClickOnBlockedWindow(*found_target);
+  }
 }
 
 void EventDispatcher::UpdateCursorRelatedProperties(
diff --git a/services/ui/ws/event_dispatcher.h b/services/ui/ws/event_dispatcher.h
index 50851f7..0e48fc7 100644
--- a/services/ui/ws/event_dispatcher.h
+++ b/services/ui/ws/event_dispatcher.h
@@ -73,8 +73,11 @@
   // any events to the delegate.
   void Reset();
 
-  void SetMousePointerDisplayLocation(const gfx::Point& display_location,
-                                      int64_t display_id);
+  // Generates a mouse move event corresponding to the mouse moving to a
+  // particular location.
+  std::unique_ptr<ui::Event> GenerateMouseMoveFor(
+      const gfx::Point& display_location) const;
+
   const gfx::Point& mouse_pointer_last_location() const {
     return mouse_pointer_last_location_;
   }
@@ -198,6 +201,14 @@
     bool is_pointer_down;
   };
 
+  struct DeepestWindowAndTarget {
+    PointerTarget pointer_target;
+    DeepestWindow deepest_window;
+  };
+
+  DisplayLocation GetDisplayLocationFromEvent(const ui::PointerEvent& event,
+                                              int64_t display_id) const;
+
   // EventTargeter returns the deepest window based on hit-test data. If the
   // target is blocked by a modal window this returns a different target,
   // otherwise the supplied target is returned.
@@ -227,6 +238,15 @@
     return pointer_targets_.count(pointer_id) > 0;
   }
 
+  // Returns true if EventTargeter needs to queried for the specified event.
+  bool ShouldUseEventTargeter(const PointerEvent& event) const;
+
+  // Callback from EventTargeter once the target has been found. Calls
+  // ProcessPointerEventOnFoundTargetImpl().
+  void ProcessPointerEventOnFoundTarget(const ui::PointerEvent& event,
+                                        const DisplayLocation& display_location,
+                                        const DeepestWindow& target);
+
   // EventDispatcher provides the following logic for pointer events:
   // . wheel events go to the current target of the associated pointer. If
   //   there is no target, they go to the deepest window.
@@ -236,9 +256,15 @@
   //   when no buttons on the mouse are down.
   // This also generates exit events as appropriate. For example, if the mouse
   // moves between one window to another an exit is generated on the first.
-  void ProcessPointerEventOnFoundTarget(const ui::PointerEvent& event,
-                                        const DisplayLocation& display_location,
-                                        const DeepestWindow& found_target);
+  //
+  // NOTE: |found_target| is null if ShouldUseEventTargeter() returned false.
+  // If ShouldUseEventTargeter() returned false it means this function should
+  // not need |found_target| and has enough information to process the event
+  // without a DeepestWindow.
+  void ProcessPointerEventOnFoundTargetImpl(
+      const ui::PointerEvent& event,
+      const DisplayLocation& display_location,
+      const DeepestWindow* found_target);
 
   // Called when processing a pointer event to updated cursor related
   // properties.
@@ -369,6 +395,9 @@
   // Keeps track of number of observe requests for each observed window.
   std::map<const ServerWindow*, uint8_t> observed_windows_;
 
+  // Set to true when querying EventTargeter for the target.
+  bool waiting_on_event_targeter_ = false;
+
 #if !defined(NDEBUG)
   std::unique_ptr<ui::Event> previous_event_;
   AcceleratorMatchPhase previous_accelerator_match_phase_ =
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index a107fd6..ec8c6034 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -306,6 +306,8 @@
   const ServerWindow* GetActiveSystemModalWindow() const;
 
  protected:
+  bool is_event_processing_async() const { return GetParam(); }
+
   // testing::TestWithParam<bool>:
   void SetUp() override;
 
@@ -340,8 +342,10 @@
     EventDispatcher* dispatcher,
     const gfx::Point& display_location,
     int64_t display_id) {
-  dispatcher->SetMousePointerDisplayLocation(display_location, display_id);
-  RunTasks();
+  std::unique_ptr<ui::Event> event =
+      dispatcher->GenerateMouseMoveFor(display_location);
+  DispatchEvent(dispatcher, *event, display_id,
+                EventDispatcher::AcceleratorMatchPhase::ANY);
 }
 
 void EventDispatcherTest::RunMouseEventTests(
@@ -422,8 +426,7 @@
 }
 
 void EventDispatcherTest::RunTasks() {
-  bool enable_async_event_targeting = GetParam();
-  if (!enable_async_event_targeting)
+  if (!is_event_processing_async())
     return;
 
   base::RunLoop runloop;
@@ -431,8 +434,7 @@
 }
 
 void EventDispatcherTest::SetUp() {
-  bool enable_async_event_targeting = GetParam();
-  if (enable_async_event_targeting) {
+  if (is_event_processing_async()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseAsyncEventTargeting);
   }
@@ -474,8 +476,7 @@
                      EventDispatcher::AcceleratorMatchPhase match_phase) {
     dispatcher->ProcessEvent(event, display_id, match_phase);
 
-    bool enable_async_event_targeting = GetParam();
-    if (!enable_async_event_targeting)
+    if (!is_event_processing_async())
       return;
 
     base::RunLoop runloop;
@@ -491,6 +492,8 @@
   }
 
  protected:
+  bool is_event_processing_async() const { return GetParam(); }
+
   // testing::TestWithParam<bool>:
   void SetUp() override;
 
@@ -514,8 +517,7 @@
 void EventDispatcherVizTargeterTest::SetUp() {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kUseVizHitTest);
-  bool enable_async_event_targeting = GetParam();
-  if (enable_async_event_targeting) {
+  if (is_event_processing_async()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseAsyncEventTargeting);
   }
@@ -2427,6 +2429,31 @@
   EXPECT_EQ(root_window(), event_dispatcher()->mouse_cursor_source_window());
 }
 
+TEST_P(EventDispatcherTest, DontQueryWhileMouseIsDown) {
+  // This test is easier to write with async event processing.
+  if (!is_event_processing_async())
+    return;
+
+  std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+  root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+  child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+  const ui::PointerEvent press_event(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
+      base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  DispatchEvent(event_dispatcher(), press_event, 0,
+                EventDispatcher::AcceleratorMatchPhase::ANY);
+  ASSERT_FALSE(event_dispatcher()->IsProcessingEvent());
+
+  const ui::PointerEvent move_event(ui::MouseEvent(
+      ui::ET_MOUSE_MOVED, gfx::Point(20, 25), gfx::Point(20, 25),
+      base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  event_dispatcher()->ProcessEvent(move_event, 0,
+                                   EventDispatcher::AcceleratorMatchPhase::ANY);
+  EXPECT_FALSE(event_dispatcher()->IsProcessingEvent());
+}
+
 TEST_P(EventDispatcherVizTargeterTest, ProcessEvent) {
   std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
 
diff --git a/services/ui/ws/event_targeter.cc b/services/ui/ws/event_targeter.cc
index e94346a..467bf33 100644
--- a/services/ui/ws/event_targeter.cc
+++ b/services/ui/ws/event_targeter.cc
@@ -15,19 +15,8 @@
 namespace ui {
 namespace ws {
 
-EventTargeter::HitTestRequest::HitTestRequest(
-    EventSource event_source,
-    const DisplayLocation& display_location,
-    HitTestCallback callback)
-    : event_source(event_source),
-      display_location(display_location),
-      callback(std::move(callback)) {}
-
-EventTargeter::HitTestRequest::~HitTestRequest() {}
-
 EventTargeter::EventTargeter(EventTargeterDelegate* event_targeter_delegate)
     : event_targeter_delegate_(event_targeter_delegate),
-      hit_test_in_flight_(false),
       weak_ptr_factory_(this) {}
 
 EventTargeter::~EventTargeter() {}
@@ -36,30 +25,10 @@
     EventSource event_source,
     const DisplayLocation& display_location,
     HitTestCallback callback) {
-  if (IsHitTestInFlight()) {
-    std::unique_ptr<HitTestRequest> hittest_request =
-        base::MakeUnique<HitTestRequest>(event_source, display_location,
-                                         std::move(callback));
-    hit_test_request_queue_.push(std::move(hittest_request));
-    return;
-  }
-
-  ProcessFindTarget(event_source, display_location, std::move(callback));
-}
-
-bool EventTargeter::IsHitTestInFlight() const {
-  return hit_test_in_flight_ || !hit_test_request_queue_.empty();
-}
-
-void EventTargeter::ProcessFindTarget(EventSource event_source,
-                                      const DisplayLocation& display_location,
-                                      HitTestCallback callback) {
   // TODO(riajiang): After the async ask-client part is implemented, the async
   // part should be moved to after sync viz-hit-test call.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kUseAsyncEventTargeting)) {
-    DCHECK(!hit_test_in_flight_);
-    hit_test_in_flight_ = true;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(&EventTargeter::FindTargetForLocationNow,
                                   weak_ptr_factory_.GetWeakPtr(), event_source,
@@ -106,22 +75,6 @@
     }
   }
   std::move(callback).Run(updated_display_location, deepest_window);
-  ProcessNextHitTestRequestFromQueue();
-}
-
-void EventTargeter::ProcessNextHitTestRequestFromQueue() {
-  hit_test_in_flight_ = false;
-  if (hit_test_request_queue_.empty()) {
-    event_targeter_delegate_->ProcessNextAvailableEvent();
-    return;
-  }
-
-  std::unique_ptr<HitTestRequest> hittest_request =
-      std::move(hit_test_request_queue_.front());
-  hit_test_request_queue_.pop();
-  ProcessFindTarget(hittest_request->event_source,
-                    hittest_request->display_location,
-                    std::move(hittest_request->callback));
 }
 
 }  // namespace ws
diff --git a/services/ui/ws/event_targeter.h b/services/ui/ws/event_targeter.h
index 93e8199..0a8ae3c 100644
--- a/services/ui/ws/event_targeter.h
+++ b/services/ui/ws/event_targeter.h
@@ -6,19 +6,22 @@
 #define SERVICES_UI_WS_EVENT_TARGETER_H_
 
 #include <stdint.h>
-#include <queue>
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "services/ui/ws/window_finder.h"
-#include "ui/display/types/display_constants.h"
 #include "ui/gfx/geometry/point.h"
 
 namespace ui {
 namespace ws {
+
 class EventTargeterDelegate;
 
+namespace test {
+class EventTargeterTestApi;
+}
+
 // Contains a location relative to a particular display.
 struct DisplayLocation {
   gfx::Point location;
@@ -40,39 +43,15 @@
                              const DisplayLocation& display_location,
                              HitTestCallback callback);
 
-  bool IsHitTestInFlight() const;
-
  private:
-  struct HitTestRequest {
-    HitTestRequest(EventSource event_source,
-                   const DisplayLocation& display_location,
-                   HitTestCallback hittest_callback);
-    ~HitTestRequest();
-
-    EventSource event_source;
-    DisplayLocation display_location;
-    HitTestCallback callback;
-  };
-
-  void ProcessFindTarget(EventSource event_source,
-                         const DisplayLocation& display_location,
-                         HitTestCallback callback);
+  friend class test::EventTargeterTestApi;
 
   void FindTargetForLocationNow(EventSource event_source,
                                 const DisplayLocation& display_location,
                                 HitTestCallback callback);
 
-  void ProcessNextHitTestRequestFromQueue();
-
   EventTargeterDelegate* event_targeter_delegate_;
 
-  // True if we are waiting for the result of a hit-test. False otherwise.
-  bool hit_test_in_flight_;
-
-  // Requests for a new location while waiting on an existing request are added
-  // here.
-  std::queue<std::unique_ptr<HitTestRequest>> hit_test_request_queue_;
-
   base::WeakPtrFactory<EventTargeter> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(EventTargeter);
diff --git a/services/ui/ws/test_utils.h b/services/ui/ws/test_utils.h
index 04dbe58..32035bb 100644
--- a/services/ui/ws/test_utils.h
+++ b/services/ui/ws/test_utils.h
@@ -23,6 +23,7 @@
 #include "services/ui/ws/display_binding.h"
 #include "services/ui/ws/drag_controller.h"
 #include "services/ui/ws/event_dispatcher.h"
+#include "services/ui/ws/event_targeter.h"
 #include "services/ui/ws/gpu_host.h"
 #include "services/ui/ws/platform_display.h"
 #include "services/ui/ws/platform_display_factory.h"
@@ -181,6 +182,7 @@
     return &ed_->modal_window_controller_;
   }
   ServerWindow* capture_window() { return ed_->capture_window_; }
+  EventTargeter* event_targeter() { return ed_->event_targeter_.get(); }
 
  private:
   EventDispatcher* ed_;
@@ -190,6 +192,24 @@
 
 // -----------------------------------------------------------------------------
 
+class EventTargeterTestApi {
+ public:
+  explicit EventTargeterTestApi(EventTargeter* event_targeter)
+      : event_targeter_(event_targeter) {}
+  ~EventTargeterTestApi() {}
+
+  bool HasPendingQueries() const {
+    return event_targeter_->weak_ptr_factory_.HasWeakPtrs();
+  }
+
+ private:
+  EventTargeter* event_targeter_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventTargeterTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
 class ModalWindowControllerTestApi {
  public:
   explicit ModalWindowControllerTestApi(ModalWindowController* mwc)
diff --git a/services/ui/ws/window_manager_state.cc b/services/ui/ws/window_manager_state.cc
index 393c9744..9afceb1 100644
--- a/services/ui/ws/window_manager_state.cc
+++ b/services/ui/ws/window_manager_state.cc
@@ -199,10 +199,11 @@
     return;
   }
 
-  event_dispatcher()->SetMousePointerDisplayLocation(display_pixels,
-                                                     display_id);
-  UpdateNativeCursorFromDispatcher();
   display->platform_display()->MoveCursorTo(display_pixels);
+
+  std::unique_ptr<ui::Event> event =
+      event_dispatcher_.GenerateMouseMoveFor(display_pixels);
+  ProcessEvent(*event, display_id);
 }
 
 void WindowManagerState::SetKeyEventsThatDontHideCursor(
@@ -309,8 +310,9 @@
                                   int64_t display_id) {
   SetAllRootWindowsVisible(true);
   event_dispatcher_.Reset();
-  event_dispatcher_.SetMousePointerDisplayLocation(mouse_location_on_display,
-                                                   display_id);
+  std::unique_ptr<ui::Event> event =
+      event_dispatcher_.GenerateMouseMoveFor(mouse_location_on_display);
+  ProcessEvent(*event, display_id);
 }
 
 void WindowManagerState::Deactivate() {
diff --git a/services/ui/ws/window_manager_state_unittest.cc b/services/ui/ws/window_manager_state_unittest.cc
index dc2fbbe60..9ee583d 100644
--- a/services/ui/ws/window_manager_state_unittest.cc
+++ b/services/ui/ws/window_manager_state_unittest.cc
@@ -729,8 +729,13 @@
       window_tree()->GetWindowByClientId(child_window_id);
   window_tree()->AddWindow(FirstRootId(window_tree()), child_window_id);
   // Setup steps already do hit-test for mouse cursor update so this should go
-  // to the queue in EventDispatcher.
-  EXPECT_TRUE(window_manager_state()->event_dispatcher()->IsProcessingEvent());
+  // to the queue in EventTargeter.
+  EventTargeterTestApi event_targeter_test_api(
+      EventDispatcherTestApi(window_manager_state()->event_dispatcher())
+          .event_targeter());
+  EXPECT_TRUE(event_targeter_test_api.HasPendingQueries());
+  // But no events have been generated, so IsProcessingEvent() should be false.
+  EXPECT_FALSE(window_manager_state()->event_dispatcher()->IsProcessingEvent());
   child_window->SetVisible(true);
   child_window->SetBounds(gfx::Rect(0, 0, 20, 20));
   child_window->parent()->SetCursor(ui::CursorData(ui::CursorType::kCopy));
@@ -743,12 +748,9 @@
   WindowManagerStateTestApi test_api(window_manager_state());
   EXPECT_TRUE(test_api.is_event_queue_empty());
   window_manager_state()->ProcessEvent(move, 0);
-  // There's no event dispatching in flight but there's hit-test in flight in
-  // EventDispatcher so we still put event processing request into the queue
-  // in WindowManagerState.
   EXPECT_FALSE(test_api.tree_awaiting_input_ack());
   EXPECT_TRUE(window_manager_state()->event_dispatcher()->IsProcessingEvent());
-  EXPECT_FALSE(test_api.is_event_queue_empty());
+  EXPECT_TRUE(test_api.is_event_queue_empty());
   task_runner_->RunUntilIdle();
   EXPECT_TRUE(test_api.is_event_queue_empty());
   // The event isn't over a valid target, which should trigger resetting the
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 65ae037..81c998d 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -220,6 +220,10 @@
 #define SK_SUPPORT_LEGACY_TILED_BITMAPS
 #endif
 
+#ifndef SK_JUMPER_LEGACY_X86_8BIT
+#define SK_JUMPER_LEGACY_X86_8BIT
+#endif
+
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 048c241..4a3d72e8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -877,7 +877,7 @@
               "device_type": "bullhead"
             }
           ],
-          "hard_timeout": 960,
+          "hard_timeout": 1200,
           "output_links": [
             {
               "link": [
@@ -6762,12 +6762,12 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "6",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
           ],
-          "hard_timeout": 1800,
+          "hard_timeout": 1200,
           "output_links": [
             {
               "link": [
@@ -6777,7 +6777,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 14
         },
         "test": "chrome_public_test_apk"
       },
@@ -6851,12 +6852,12 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "6",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
           ],
-          "hard_timeout": 1800,
+          "hard_timeout": 1200,
           "output_links": [
             {
               "link": [
@@ -6866,7 +6867,8 @@
               ],
               "name": "shard #${SHARD_INDEX} logcats"
             }
-          ]
+          ],
+          "shards": 14
         },
         "test": "chrome_public_test_apk"
       },
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index d6f6452..24573de 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -2061,130 +2061,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:22b1",
-              "id": "build155-b1",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:22b1",
-              "id": "build155-b1",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:22b1",
-              "id": "build154-b1",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:22b1",
-              "id": "build154-b1",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -6540,130 +6416,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:9874",
-              "id": "build219-b4",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:9874",
-              "id": "build219-b4",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:9874",
-              "id": "build218-b4",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:9874",
-              "id": "build218-b4",
-              "os": "Windows-10-10586",
-              "pool": "Chrome-perf-fyi"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index c3d0243..6936ff4 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -32118,130 +32118,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build150-m1",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build150-m1",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build152-m1",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build152-m1",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -36782,130 +36658,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0166",
-              "id": "build103-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0166",
-              "id": "build103-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0166",
-              "id": "build106-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0166",
-              "id": "build106-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -41487,130 +41239,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "id": "build159-m1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "id": "build159-m1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "id": "build162-m1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "id": "build162-m1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -46170,130 +45798,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1626",
-              "id": "build124-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1626",
-              "id": "build124-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1626",
-              "id": "build127-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1626",
-              "id": "build127-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -50876,130 +50380,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a26",
-              "id": "build25-b1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a26",
-              "id": "build25-b1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a26",
-              "id": "build28-b1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a26",
-              "id": "build28-b1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -55559,130 +54939,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "id": "build129-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "id": "build129-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "id": "build132-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "id": "build132-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -60264,130 +59520,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0d26",
-              "id": "build5-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0d26",
-              "id": "build5-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0d26",
-              "id": "build30-b4",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0d26",
-              "id": "build30-b4",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -64846,130 +63978,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1616",
-              "id": "build118-b1",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1616",
-              "id": "build118-b1",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1616",
-              "id": "build180-b4",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1616",
-              "id": "build180-b4",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -69303,130 +68311,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build133-m1",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build133-m1",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build136-m1",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build136-m1",
-              "os": "Windows-10-10240",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -73801,130 +72685,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "id": "build102-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "id": "build102-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "id": "build105-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "id": "build105-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -78300,130 +77060,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:041a",
-              "id": "build165-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:041a",
-              "id": "build165-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:041a",
-              "id": "build168-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:041a",
-              "id": "build168-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -82821,130 +81457,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:104a",
-              "id": "build93-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:104a",
-              "id": "build93-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:104a",
-              "id": "build96-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:104a",
-              "id": "build96-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -87299,130 +85811,6 @@
       },
       {
         "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build188-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build188-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build189-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build189-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
           "power.idle_platform",
           "-v",
           "--upload-results",
@@ -91755,130 +90143,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build139-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build139-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build142-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build142-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
@@ -96254,130 +94518,6 @@
         }
       },
       {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build144-m1",
-              "os": "Windows-2012ServerR2-SP0",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build144-m1",
-              "os": "Windows-2012ServerR2-SP0",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=release_x64"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build147-m1",
-              "os": "Windows-2012ServerR2-SP0",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
-        "args": [
-          "page_cycler_v2_site_isolation.basic_oopif",
-          "-v",
-          "--upload-results",
-          "--output-format=chartjson",
-          "--browser=reference",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "page_cycler_v2_site_isolation.basic_oopif.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build147-m1",
-              "os": "Windows-2012ServerR2-SP0",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 72000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 600,
-          "upload_test_results": false
-        }
-      },
-      {
         "args": [],
         "isolate_name": "performance_browser_tests",
         "name": "performance_browser_tests",
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 1110d88b6..47b4e99 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1001,6 +1001,10 @@
 
 crbug.com/637255 [ Win10 ] media/video-transformed.html [ Pass Failure ]
 
+crbug.com/760367 http/tests/devtools/console/console-tainted-globals.html [ NeedsManualRebaseline Timeout ]
+crbug.com/760367 inspector/runtime/runtime-callFunctionOn.html [ NeedsManualRebaseline Timeout ]
+crbug.com/760367 virtual/mojo-loading/http/tests/devtools/console/console-tainted-globals.html [ NeedsManualRebaseline Timeout ]
+
 # These tests pass but images do not match because tests are stricter than the spec.
 crbug.com/492664 external/wpt/css/css-writing-modes-3/text-combine-upright-value-all-001.html [ Failure ]
 crbug.com/492664 external/wpt/css/css-writing-modes-3/text-combine-upright-value-all-002.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-to-scroll-sibling-should-not-crash-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-to-scroll-sibling-should-not-crash-expected.html
new file mode 100644
index 0000000..d4d9515
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-to-scroll-sibling-should-not-crash-expected.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<div style="overflow:scroll; width:100px; height:100px;">
+  <div style="width:200px; height:200px; background: green;">
+  </div>
+</div>
+This test verifies an element can correctly escape clip to some scroll sibling's state.
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-to-scroll-sibling-should-not-crash.html b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-to-scroll-sibling-should-not-crash.html
new file mode 100644
index 0000000..3159e5e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-to-scroll-sibling-should-not-crash.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<div style="overflow:scroll; position:relative; width:100px; height:100px;">
+  <div id="elem" style="position:relative; width:200px; height:200px; overflow:hidden;">
+    <div style="will-change:opacity; overflow:hidden; width:10px; height:10px;">
+      <div style="position:absolute; width:250px; height:250px; background:green;"></div>
+    </div>
+  </div>
+</div>
+This test verifies an element can correctly escape clip to some scroll sibling's state.
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+// The crash we intended to test is sensitive to random hash ordering.
+// There is a 50% probability to crash for each trial.
+var elem = document.getElementById('elem');
+var count = 10;
+function toggle() {
+  if (elem.style.display == 'none')
+    elem.style.display = '';
+  else
+    elem.style.display = 'none';
+
+  if (--count)
+    runAfterLayoutAndPaint(toggle);
+  else if (window.testRunner)
+    testRunner.notifyDone();
+}
+if (window.testRunner)
+  testRunner.waitUntilDone();
+runAfterLayoutAndPaint(toggle);
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/resources/feature-policy-webvr.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/resources/feature-policy-webvr.html
new file mode 100644
index 0000000..64a152b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/resources/feature-policy-webvr.html
@@ -0,0 +1,9 @@
+<script>
+'use strict';
+
+Promise.resolve().then(() => navigator.getVRDisplays()).then(displays => {
+  window.parent.postMessage({ enabled: true }, '*');
+}, error => {
+  window.parent.postMessage({ enabled: false }, '*');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..567499c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy header vr "none"';
+
+    promise_test(() => {
+      return navigator.getVRDisplays().then(() => {
+        assert_unreached('expected promise to reject');
+      }, error => {
+      });
+    }, header + ' disallows the top-level document.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..d021af7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
new file mode 100644
index 0000000..da01dafd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+  <script>
+    'use strict';
+    var relative_path = '/feature-policy/resources/feature-policy-webvr.html';
+    var base_src = '/feature-policy/resources/redirect-on-load.html#';
+    var same_origin_src = base_src + relative_path;
+    var cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' +
+        relative_path;
+    var header = 'Feature-Policy allow="vr" attribute';
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default, 'vr');
+    }, header + ' allows same-origin relocation');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_unavailable_default, 'vr');
+    }, header + ' disallows cross-origin relocation');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html
new file mode 100644
index 0000000..d715f90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy allow="vr" attribute';
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default, 'vr');
+    }, header + ' allows same-origin iframe');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_available_default, 'vr');
+    }, header + ' allows cross-origin iframe');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..ee02566
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy header vr *';
+
+    promise_test(
+        () => navigator.getVRDisplays(),
+        header + ' allows the top-level document.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..e7427ee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr *
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..bd7e82f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy header vr "self"';
+
+    promise_test(
+        () => navigator.getVRDisplays(),
+        header + ' allows the top-level document.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..87d343d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr 'self'
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-tainted-globals.html b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-tainted-globals.html
index d212db4..a5f2289 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-tainted-globals.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-tainted-globals.html
@@ -157,7 +157,7 @@
       function sum() {
         return this.a + this.b;
       }
-      result = await TestRunner.RuntimeAgent.callFunctionOn(result.objectId, sum.toString());
+      result = await TestRunner.RuntimeAgent.callFunctionOn(sum.toString(), result.objectId);
 
       TestRunner.assertEquals(3, result.value);
       next();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-data-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-data-expected.txt
index c47f78e2..726f950c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-data-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-data-expected.txt
@@ -9,9 +9,9 @@
         (cache empty)
 Dumping CacheStorage tree:
     cache: testCache1 - http://127.0.0.1:8000
-        1
-        2
+        1, text/plain, 0
+        2, text/plain, 0
     cache: testCache2 - http://127.0.0.1:8000
-        1
-        2
+        1, text/plain, 0
+        2, text/plain, 0
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-deletion-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-deletion-expected.txt
index d23d751..9fbb577 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-deletion-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-deletion-expected.txt
@@ -9,11 +9,11 @@
         (cache empty)
 Dumping CacheStorage tree:
     cache: testCache1 - http://127.0.0.1:8000
-        1
-        2
+        1, text/plain, 0
+        2, text/plain, 0
     cache: testCache2 - http://127.0.0.1:8000
-        1
-        2
+        1, text/plain, 0
+        2, text/plain, 0
 Deleting CacheStorage cache testCache2
 Dumping CacheStorage tree:
     (empty)
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-entry-deletion-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-entry-deletion-expected.txt
index aea9ec6..ea56f742 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-entry-deletion-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-entry-deletion-expected.txt
@@ -4,15 +4,15 @@
     (empty)
 Dumping CacheStorage tree:
     cache: testCache1 - http://127.0.0.1:8000
-        1
-        2
+        1, text/plain, 0
+        2, text/plain, 0
     cache: testCache2 - http://127.0.0.1:8000
-        1
-        2
+        1, text/plain, 0
+        2, text/plain, 0
 Deleting CacheStorage entry http://fake.request2.com/2 in cache testCache2
 Dumping CacheStorage tree:
     cache: testCache1 - http://127.0.0.1:8000
-        1
+        1, text/plain, 0
     cache: testCache2 - http://127.0.0.1:8000
-        1
+        1, text/plain, 0
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-live-update-cache-content-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-live-update-cache-content-expected.txt
index ea42256..4740da4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-live-update-cache-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/cache-storage/cache-live-update-cache-content-expected.txt
@@ -8,7 +8,7 @@
 Added entry
 Dumping CacheStorage tree:
     cache: testCache1 - http://127.0.0.1:8000
-        1
+        1, text/plain, 0
 Deleted entry
 Dumping CacheStorage tree:
     cache: testCache1 - http://127.0.0.1:8000
diff --git a/third_party/WebKit/LayoutTests/inspector/runtime/runtime-callFunctionOn.html b/third_party/WebKit/LayoutTests/inspector/runtime/runtime-callFunctionOn.html
index 1f4229ea..17d10f2d 100644
--- a/third_party/WebKit/LayoutTests/inspector/runtime/runtime-callFunctionOn.html
+++ b/third_party/WebKit/LayoutTests/inspector/runtime/runtime-callFunctionOn.html
@@ -13,7 +13,7 @@
       function sum() {
         return this.a + this.b;
       }
-      var result = await TestRunner.RuntimeAgent.callFunctionOn(obj1.objectId, sum.toString());
+      var result = await TestRunner.RuntimeAgent.callFunctionOn(sum.toString(), obj1.objectId);
 
       TestRunner.addResult(result.value);
       next();
@@ -27,7 +27,7 @@
             undef;
       }
       var result =
-          await TestRunner.RuntimeAgent.callFunctionOn(obj1.objectId, format.toString(), [obj1, obj2, {value: 4}, {}]);
+          await TestRunner.RuntimeAgent.callFunctionOn(format.toString(), obj1.objectId, [obj1, obj2, {value: 4}, {}]);
 
       TestRunner.addResult(result.value);
       next();
diff --git a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
index a053005..19765f45 100644
--- a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
@@ -17,6 +17,7 @@
 }
 
 void ActiveSuggestionMarkerListImpl::Add(DocumentMarker* marker) {
+  DCHECK_EQ(DocumentMarker::kActiveSuggestion, marker->GetType());
   SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
                                                                      marker);
 }
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
index abcee75..130c10d 100644
--- a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
@@ -4,7 +4,7 @@
 
 #include "core/editing/markers/CompositionMarkerListImpl.h"
 
-#include "core/editing/markers/SortedDocumentMarkerListEditor.h"
+#include "core/editing/markers/UnsortedDocumentMarkerListEditor.h"
 
 namespace blink {
 
@@ -17,8 +17,8 @@
 }
 
 void CompositionMarkerListImpl::Add(DocumentMarker* marker) {
-  SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
-                                                                     marker);
+  DCHECK_EQ(DocumentMarker::kComposition, marker->GetType());
+  markers_.push_back(marker);
 }
 
 void CompositionMarkerListImpl::Clear() {
@@ -33,34 +33,34 @@
 DocumentMarker* CompositionMarkerListImpl::FirstMarkerIntersectingRange(
     unsigned start_offset,
     unsigned end_offset) const {
-  return SortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
+  return UnsortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 HeapVector<Member<DocumentMarker>>
 CompositionMarkerListImpl::MarkersIntersectingRange(unsigned start_offset,
                                                     unsigned end_offset) const {
-  return SortedDocumentMarkerListEditor::MarkersIntersectingRange(
+  return UnsortedDocumentMarkerListEditor::MarkersIntersectingRange(
       markers_, start_offset, end_offset);
 }
 
 bool CompositionMarkerListImpl::MoveMarkers(int length,
                                             DocumentMarkerList* dst_markers_) {
-  return SortedDocumentMarkerListEditor::MoveMarkers(&markers_, length,
-                                                     dst_markers_);
+  return UnsortedDocumentMarkerListEditor::MoveMarkers(&markers_, length,
+                                                       dst_markers_);
 }
 
 bool CompositionMarkerListImpl::RemoveMarkers(unsigned start_offset,
                                               int length) {
-  return SortedDocumentMarkerListEditor::RemoveMarkers(&markers_, start_offset,
-                                                       length);
+  return UnsortedDocumentMarkerListEditor::RemoveMarkers(&markers_,
+                                                         start_offset, length);
 }
 
 bool CompositionMarkerListImpl::ShiftMarkers(const String&,
                                              unsigned offset,
                                              unsigned old_length,
                                              unsigned new_length) {
-  return SortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(
+  return UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(
       &markers_, offset, old_length, new_length);
 }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h
index 2c057d4..4bf8851e 100644
--- a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h
+++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h
@@ -9,12 +9,10 @@
 
 namespace blink {
 
-// Implementation of DocumentMarkerList for Composition markers.
-// Composition markers are always inserted in order, aside from some potential
-// oddball cases (e.g. splitting the marker list into two nodes, then undoing
-// the split). This means we can keep the list in sorted order to do some
-// operations more efficiently, while still being able to do inserts in O(1)
-// time at the end of the list.
+// Implementation of DocumentMarkerList for Composition markers. Composition
+// markers can overlap (e.g. an IME might pass two markers on the same region of
+// text, one to underline it and one to add a background color), so we store
+// them unsorted.
 class CORE_EXPORT CompositionMarkerListImpl final : public DocumentMarkerList {
  public:
   CompositionMarkerListImpl() = default;
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImplTest.cpp b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImplTest.cpp
index 15f58c7..9ba9da4 100644
--- a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImplTest.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImplTest.cpp
@@ -23,20 +23,66 @@
   Persistent<CompositionMarkerListImpl> marker_list_;
 };
 
-// CompositionMarkerListImpl shouldn't merge markers with touching endpoints
-TEST_F(CompositionMarkerListImplTest, Add) {
-  EXPECT_EQ(0u, marker_list_->GetMarkers().size());
+namespace {
 
-  marker_list_->Add(CreateMarker(0, 1));
-  marker_list_->Add(CreateMarker(1, 2));
+bool compare_markers(const Member<DocumentMarker>& marker1,
+                     const Member<DocumentMarker>& marker2) {
+  if (marker1->StartOffset() != marker2->StartOffset())
+    return marker1->StartOffset() < marker2->StartOffset();
 
-  EXPECT_EQ(2u, marker_list_->GetMarkers().size());
+  return marker1->EndOffset() < marker2->EndOffset();
+}
 
-  EXPECT_EQ(0u, marker_list_->GetMarkers()[0]->StartOffset());
-  EXPECT_EQ(1u, marker_list_->GetMarkers()[0]->EndOffset());
+}  // namespace
 
-  EXPECT_EQ(1u, marker_list_->GetMarkers()[1]->StartOffset());
-  EXPECT_EQ(2u, marker_list_->GetMarkers()[1]->EndOffset());
+TEST_F(CompositionMarkerListImplTest, AddOverlapping) {
+  // Add some overlapping markers in an arbitrary order and verify that the
+  // list stores them properly
+  marker_list_->Add(CreateMarker(40, 50));
+  marker_list_->Add(CreateMarker(10, 40));
+  marker_list_->Add(CreateMarker(20, 50));
+  marker_list_->Add(CreateMarker(10, 30));
+  marker_list_->Add(CreateMarker(10, 50));
+  marker_list_->Add(CreateMarker(30, 50));
+  marker_list_->Add(CreateMarker(30, 40));
+  marker_list_->Add(CreateMarker(10, 20));
+  marker_list_->Add(CreateMarker(20, 40));
+  marker_list_->Add(CreateMarker(20, 30));
+
+  DocumentMarkerVector markers = marker_list_->GetMarkers();
+  std::sort(markers.begin(), markers.end(), compare_markers);
+
+  EXPECT_EQ(10u, markers.size());
+
+  EXPECT_EQ(10u, markers[0]->StartOffset());
+  EXPECT_EQ(20u, markers[0]->EndOffset());
+
+  EXPECT_EQ(10u, markers[1]->StartOffset());
+  EXPECT_EQ(30u, markers[1]->EndOffset());
+
+  EXPECT_EQ(10u, markers[2]->StartOffset());
+  EXPECT_EQ(40u, markers[2]->EndOffset());
+
+  EXPECT_EQ(10u, markers[3]->StartOffset());
+  EXPECT_EQ(50u, markers[3]->EndOffset());
+
+  EXPECT_EQ(20u, markers[4]->StartOffset());
+  EXPECT_EQ(30u, markers[4]->EndOffset());
+
+  EXPECT_EQ(20u, markers[5]->StartOffset());
+  EXPECT_EQ(40u, markers[5]->EndOffset());
+
+  EXPECT_EQ(20u, markers[6]->StartOffset());
+  EXPECT_EQ(50u, markers[6]->EndOffset());
+
+  EXPECT_EQ(30u, markers[7]->StartOffset());
+  EXPECT_EQ(40u, markers[7]->EndOffset());
+
+  EXPECT_EQ(30u, markers[8]->StartOffset());
+  EXPECT_EQ(50u, markers[8]->EndOffset());
+
+  EXPECT_EQ(40u, markers[9]->StartOffset());
+  EXPECT_EQ(50u, markers[9]->EndOffset());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
index 5a1ec40..1405f5b 100644
--- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
@@ -13,6 +13,8 @@
 }
 
 void SpellCheckMarkerListImpl::Add(DocumentMarker* marker) {
+  DCHECK_EQ(MarkerType(), marker->GetType());
+
   if (markers_.IsEmpty() ||
       markers_.back()->EndOffset() < marker->StartOffset()) {
     markers_.push_back(marker);
diff --git a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
index 3aac516..811682b6 100644
--- a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
@@ -63,6 +63,7 @@
 }
 
 void SuggestionMarkerListImpl::Add(DocumentMarker* marker) {
+  DCHECK_EQ(DocumentMarker::kSuggestion, marker->GetType());
   markers_.push_back(marker);
 }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
index ddc337dc..feb398f 100644
--- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
@@ -22,6 +22,7 @@
 }
 
 void TextMatchMarkerListImpl::Add(DocumentMarker* marker) {
+  DCHECK_EQ(DocumentMarker::kTextMatch, marker->GetType());
   SortedDocumentMarkerListEditor::AddMarkerWithoutMergingOverlapping(&markers_,
                                                                      marker);
 }
diff --git a/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditor.cpp b/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditor.cpp
index 93dd7774..399325b 100644
--- a/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditor.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditor.cpp
@@ -56,6 +56,37 @@
   return did_remove_marker;
 }
 
+bool UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(
+    MarkerList* list,
+    unsigned offset,
+    unsigned old_length,
+    unsigned new_length) {
+  // For an unsorted marker list, the quickest way to perform this operation is
+  // to build a new list with the markers not removed by the shift.
+  bool did_shift_marker = false;
+  HeapVector<Member<DocumentMarker>> unremoved_markers;
+  for (const Member<DocumentMarker>& marker : *list) {
+    Optional<DocumentMarker::MarkerOffsets> result =
+        marker->ComputeOffsetsAfterShift(offset, old_length, new_length);
+    if (!result) {
+      did_shift_marker = true;
+      continue;
+    }
+
+    if (marker->StartOffset() != result.value().start_offset ||
+        marker->EndOffset() != result.value().end_offset) {
+      marker->SetStartOffset(result.value().start_offset);
+      marker->SetEndOffset(result.value().end_offset);
+      did_shift_marker = true;
+    }
+
+    unremoved_markers.push_back(marker);
+  }
+
+  *list = std::move(unremoved_markers);
+  return did_shift_marker;
+}
+
 DocumentMarker* UnsortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
     const MarkerList& list,
     unsigned start_offset,
diff --git a/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditorTest.cpp b/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditorTest.cpp
index 92ac36f6..0c36b01 100644
--- a/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditorTest.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/UnsortedDocumentMarkerListEditorTest.cpp
@@ -135,6 +135,230 @@
 }
 
 TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceStartOfMarker) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  // Replace with shorter text
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   5, 4);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(9u, markers[0]->EndOffset());
+
+  // Replace with longer text
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   4, 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  // Replace with text of same length
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   5, 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceContainsStartOfMarker) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 15));
+
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   10, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(10u, markers[0]->StartOffset());
+  EXPECT_EQ(15u, markers[0]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceEndOfMarker) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  // Replace with shorter text
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5,
+                                                                   5, 4);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(9u, markers[0]->EndOffset());
+
+  // Replace with longer text
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5,
+                                                                   4, 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  // Replace with text of same length
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5,
+                                                                   5, 5);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceContainsEndOfMarker) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5,
+                                                                   10, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceEntireMarker) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  // Replace with shorter text
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   10, 9);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(9u, markers[0]->EndOffset());
+
+  // Replace with longer text
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   9, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+
+  // Replace with text of same length
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   10, 10);
+
+  EXPECT_EQ(1u, markers.size());
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(10u, markers[0]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceTextWithMarkerAtBeginning) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   15, 15);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_ReplaceTextWithMarkerAtEnd) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(5, 15));
+
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   15, 15);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_Deletions) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+  markers.push_back(CreateMarker(15, 20));
+  markers.push_back(CreateMarker(20, 25));
+
+  // Delete range containing the end of the second marker, the entire third
+  // marker, and the start of the fourth marker
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 8,
+                                                                   9, 0);
+
+  EXPECT_EQ(4u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(5u, markers[1]->StartOffset());
+  EXPECT_EQ(8u, markers[1]->EndOffset());
+
+  EXPECT_EQ(8u, markers[2]->StartOffset());
+  EXPECT_EQ(11u, markers[2]->EndOffset());
+
+  EXPECT_EQ(11u, markers[3]->StartOffset());
+  EXPECT_EQ(16u, markers[3]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_DeleteExactlyOnMarker) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 10));
+
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 0,
+                                                                   10, 0);
+
+  EXPECT_EQ(0u, markers.size());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_InsertInMarkerInterior) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+
+  // insert in middle of second marker
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 7,
+                                                                   0, 5);
+
+  EXPECT_EQ(3u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(5u, markers[1]->StartOffset());
+  EXPECT_EQ(15u, markers[1]->EndOffset());
+
+  EXPECT_EQ(15u, markers[2]->StartOffset());
+  EXPECT_EQ(20u, markers[2]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
+       ContentIndependentMarker_InsertBetweenMarkers) {
+  UnsortedDocumentMarkerListEditor::MarkerList markers;
+  markers.push_back(CreateMarker(0, 5));
+  markers.push_back(CreateMarker(5, 10));
+  markers.push_back(CreateMarker(10, 15));
+
+  // insert before second marker
+  UnsortedDocumentMarkerListEditor::ShiftMarkersContentIndependent(&markers, 5,
+                                                                   0, 5);
+
+  EXPECT_EQ(3u, markers.size());
+
+  EXPECT_EQ(0u, markers[0]->StartOffset());
+  EXPECT_EQ(5u, markers[0]->EndOffset());
+
+  EXPECT_EQ(10u, markers[1]->StartOffset());
+  EXPECT_EQ(15u, markers[1]->EndOffset());
+
+  EXPECT_EQ(15u, markers[2]->StartOffset());
+  EXPECT_EQ(20u, markers[2]->EndOffset());
+}
+
+TEST_F(UnsortedDocumentMarkerListEditorTest,
        FirstMarkerIntersectingRange_Empty) {
   DocumentMarker* marker =
       UnsortedDocumentMarkerListEditor::FirstMarkerIntersectingRange(
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index acb70c5..7550fa6 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -2129,7 +2129,11 @@
                 "description": "Data entry.",
                 "properties": [
                     { "name": "requestURL", "type": "string", "description": "Request URL." },
+                    { "name": "requestMethod", "type": "string", "description": "Request method." },
+                    { "name": "requestHeaders", "type": "array", "items": { "$ref": "Header" }, "description": "Request headers" },
                     { "name": "responseTime", "type": "number", "description": "Number of seconds since epoch." },
+                    { "name": "responseStatus", "type": "integer", "description": "HTTP response status code." },
+                    { "name": "responseStatusText", "type": "string", "description": "HTTP response status text." },
                     { "name": "responseHeaders", "type": "array", "items": { "$ref": "Header" }, "description": "Response headers" }
                 ]
             },
diff --git a/third_party/WebKit/Source/devtools/front_end/common/ResourceType.js b/third_party/WebKit/Source/devtools/front_end/common/ResourceType.js
index 80243849..121f283b 100644
--- a/third_party/WebKit/Source/devtools/front_end/common/ResourceType.js
+++ b/third_party/WebKit/Source/devtools/front_end/common/ResourceType.js
@@ -49,17 +49,24 @@
    * @return {!Common.ResourceType}
    */
   static fromMimeType(mimeType) {
-    var contentTypeAndTopLevelType = mimeType.match(/(\w*)\/\w*/);
-    if (!contentTypeAndTopLevelType)
+    if (mimeType.startsWith('text/html'))
+      return Common.resourceTypes.Document;
+    if (mimeType.startsWith('text/css'))
+      return Common.resourceTypes.Stylesheet;
+    if (mimeType.startsWith('image/'))
+      return Common.resourceTypes.Image;
+    if (mimeType.startsWith('text/'))
+      return Common.resourceTypes.Script;
+
+    if (mimeType.includes('font'))
+      return Common.resourceTypes.Font;
+    if (mimeType.includes('script'))
+      return Common.resourceTypes.Script;
+    if (mimeType.includes('octet'))
       return Common.resourceTypes.Other;
+    if (mimeType.includes('application'))
+      return Common.resourceTypes.Script;
 
-    var resourceType = Common.ResourceType._resourceTypeByMimeType.get(contentTypeAndTopLevelType[0]);
-    if (resourceType)
-      return resourceType;
-
-    resourceType = Common.ResourceType._resourceTypeByMimeType.get(contentTypeAndTopLevelType[1]);
-    if (resourceType)
-      return resourceType;
     return Common.resourceTypes.Other;
   }
 
@@ -315,15 +322,3 @@
   // Font
   ['ttf', 'font/opentype'], ['otf', 'font/opentype'], ['ttc', 'font/opentype'], ['woff', 'application/font-woff']
 ]);
-
-Common.ResourceType._resourceTypeByMimeType = new Map([
-  // Web types
-  ['text/javascript', Common.resourceTypes.Script], ['text/css', Common.resourceTypes.Stylesheet],
-  ['text/html', Common.resourceTypes.Document], ['application/json', Common.resourceTypes.Script],
-
-  // Image
-  ['image', Common.resourceTypes.Image],
-
-  // Font
-  ['font', Common.resourceTypes.Font], ['application/font-woff', Common.resourceTypes.Font]
-]);
diff --git a/third_party/WebKit/Source/devtools/front_end/har_importer/HARImporter.js b/third_party/WebKit/Source/devtools/front_end/har_importer/HARImporter.js
index 7a940210..184b4a24 100644
--- a/third_party/WebKit/Source/devtools/front_end/har_importer/HARImporter.js
+++ b/third_party/WebKit/Source/devtools/front_end/har_importer/HARImporter.js
@@ -96,7 +96,7 @@
     var contentData = {error: null, content: null, encoded: entry.response.content.encoding === 'base64'};
     if (entry.response.content.text !== undefined)
       contentData.content = entry.response.content.text;
-    request.setContentData(contentData);
+    request.setContentDataProvider(async () => contentData);
 
     // Timing data.
     HARImporter.Importer._setupTiming(request, issueTime, entry.time, entry.timings);
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ServiceWorkerCacheViews.js b/third_party/WebKit/Source/devtools/front_end/resources/ServiceWorkerCacheViews.js
index cd10121..df76aa0 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ServiceWorkerCacheViews.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ServiceWorkerCacheViews.js
@@ -97,19 +97,59 @@
    */
   _createDataGrid() {
     var columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([
-      // {id: 'number', title: Common.UIString('#'), width: '50px'},
-      {id: 'path', title: Common.UIString('Path')},
-      // {id: 'response', title: Common.UIString('Response')},
-      {id: 'responseTime', title: Common.UIString('Time Cached'), width: '12em'}
+      {id: 'path', title: Common.UIString('Path'), weight: 4, sortable: true},
+      {id: 'contentType', title: Common.UIString('Content-Type'), weight: 1, sortable: true}, {
+        id: 'contentLength',
+        title: Common.UIString('Content-Length'),
+        weight: 1,
+        align: DataGrid.DataGrid.Align.Right,
+        sortable: true
+      },
+      {
+        id: 'responseTime',
+        title: Common.UIString('Time Cached'),
+        width: '12em',
+        weight: 1,
+        align: DataGrid.DataGrid.Align.Right,
+        sortable: true
+      }
     ]);
     var dataGrid = new DataGrid.DataGrid(
         columns, undefined, this._deleteButtonClicked.bind(this), this._updateData.bind(this, true));
+
+    dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._sortingChanged, this);
+
     dataGrid.addEventListener(
         DataGrid.DataGrid.Events.SelectedNode, event => this._previewCachedResponse(event.data.data), this);
     dataGrid.setStriped(true);
     return dataGrid;
   }
 
+  _sortingChanged() {
+    if (!this._dataGrid)
+      return;
+
+    var accending = this._dataGrid.isSortOrderAscending();
+    var columnId = this._dataGrid.sortColumnId();
+    var comparator;
+    if (columnId === 'path')
+      comparator = (a, b) => a._path.localeCompare(b._path);
+    else if (columnId === 'contentType')
+      comparator = (a, b) => a.data.mimeType.localeCompare(b.data.mimeType);
+    else if (columnId === 'contentLength')
+      comparator = (a, b) => a.data.resourceSize - b.data.resourceSize;
+    else if (columnId === 'responseTime')
+      comparator = (a, b) => a.data.endTime - b.data.endTime;
+
+    var children = this._dataGrid.rootNode().children.slice();
+    this._dataGrid.rootNode().removeChildren();
+    children.sort((a, b) => {
+      var result = comparator(a, b);
+      return accending ? result : -result;
+    });
+    children.forEach(child => this._dataGrid.rootNode().appendChild(child));
+  }
+
   /**
    * @param {!Common.Event} event
    */
@@ -135,7 +175,7 @@
       if (!node)
         return;
     }
-    await this._model.deleteCacheEntry(this._cache, /** @type {string} */ (node.data.requestURL));
+    await this._model.deleteCacheEntry(this._cache, /** @type {string} */ (node.data.url()));
     node.remove();
   }
 
@@ -160,7 +200,7 @@
    * @this {Resources.ServiceWorkerCacheView}
    */
   _updateDataCallback(skipCount, entries, hasMore) {
-    var selected = this._dataGrid.selectedNode && this._dataGrid.selectedNode.data.requestURL;
+    var selected = this._dataGrid.selectedNode && this._dataGrid.selectedNode.data.url();
     this._refreshButton.setEnabled(true);
     this._entriesForTest = entries;
 
@@ -174,7 +214,7 @@
     for (var entry of entries) {
       var node = oldEntries.get(entry.requestURL);
       if (!node || node.data.responseTime !== entry.responseTime) {
-        node = new Resources.ServiceWorkerCacheView.DataGridNode(entry);
+        node = new Resources.ServiceWorkerCacheView.DataGridNode(this._createRequest(entry));
         node.selectable = true;
       }
       rootNode.appendChild(node);
@@ -227,61 +267,83 @@
   }
 
   /**
-   * @param {!Protocol.CacheStorage.DataEntry} entry
+   * @param {!SDK.NetworkRequest} request
    */
-  async _previewCachedResponse(entry) {
-    var preview = entry[Resources.ServiceWorkerCacheView._previewSymbol];
+  async _previewCachedResponse(request) {
+    var preview = request[Resources.ServiceWorkerCacheView._previewSymbol];
     if (!preview) {
-      preview = await this._entryPreview(entry);
-      entry[Resources.ServiceWorkerCacheView._previewSymbol] = preview;
+      preview = new Resources.ServiceWorkerCacheView.RequestView(request);
+      request[Resources.ServiceWorkerCacheView._previewSymbol] = preview;
     }
 
     // It is possible that table selection changes before the preview opens.
-    if (entry === this._dataGrid.selectedNode.data)
+    if (request === this._dataGrid.selectedNode.data)
       this._showPreview(preview);
   }
 
   /**
    * @param {!Protocol.CacheStorage.DataEntry} entry
-   * @return {!Promise<!UI.Widget>}
+   * @return {!SDK.NetworkRequest}
    */
-  async _entryPreview(entry) {
-    var response = await this._cache.requestCachedResponse(entry.requestURL);
-    if (!response)
-      return new UI.EmptyWidget(Common.UIString('Preview is not available'));
+  _createRequest(entry) {
+    var request = new SDK.NetworkRequest('cache-storage-' + entry.requestURL, entry.requestURL, '', '', '', null);
+    request.requestMethod = entry.requestMethod;
+    request.setRequestHeaders(entry.requestHeaders);
+    request.statusCode = entry.responseStatus;
+    request.statusText = entry.responseStatusText;
+    request.protocol = new Common.ParsedURL(entry.requestURL).scheme;
+    request.responseHeaders = entry.responseHeaders;
+    request.setRequestHeadersText('');
+    request.endTime = entry.responseTime;
 
     var header = entry.responseHeaders.find(header => header.name.toLowerCase() === 'content-type');
     var contentType = header ? header.value : 'text/plain';
+    request.mimeType = contentType;
+
+    header = entry.responseHeaders.find(header => header.name.toLowerCase() === 'content-length');
+    request.resourceSize = (header && header.value) | 0;
+
     var resourceType = Common.ResourceType.fromMimeType(contentType);
-    var body = resourceType.isTextType() ? window.atob(response.body) : response.body;
-    var provider = new Common.StaticContentProvider(entry.requestURL, resourceType, () => Promise.resolve(body));
-    var preview = SourceFrame.PreviewFactory.createPreview(provider, contentType);
-    if (!preview)
-      return new UI.EmptyWidget(Common.UIString('Preview is not available'));
-    return preview;
+    if (!resourceType)
+      resourceType = Common.ResourceType.fromURL(entry.requestURL) || Common.resourceTypes.Other;
+    request.setResourceType(resourceType);
+    request.setContentDataProvider(this._requestContent.bind(this, request));
+    return request;
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   * @return {!Promise<!SDK.NetworkRequest.ContentData>}
+   */
+  async _requestContent(request) {
+    var isText = request.resourceType().isTextType();
+    var contentData = {error: null, content: null, encoded: !isText};
+    var response = await this._cache.requestCachedResponse(request.url());
+    if (response)
+      contentData.content = isText ? window.atob(response.body) : response.body;
+    return contentData;
   }
 
   _updatedForTest() {
   }
 };
 
-
 Resources.ServiceWorkerCacheView._previewSymbol = Symbol('preview');
 
 Resources.ServiceWorkerCacheView._RESPONSE_CACHE_SIZE = 10;
 
 Resources.ServiceWorkerCacheView.DataGridNode = class extends DataGrid.DataGridNode {
   /**
-   * @param {!Protocol.CacheStorage.DataEntry} entry
+   * @param {!SDK.NetworkRequest} request
    */
-  constructor(entry) {
-    super(entry);
-    this._path = Common.ParsedURL.extractPath(entry.requestURL);
+  constructor(request) {
+    super(request);
+    this._path = Common.ParsedURL.extractPath(request.url());
     if (!this._path)
-      this._path = entry.requestURL;
+      this._path = request.url();
     if (this._path.length > 1 && this._path.startsWith('/'))
       this._path = this._path.substring(1);
-    this._responseTime = new Date(entry.responseTime * 1000).toLocaleString();
+    this._request = request;
   }
 
   /**
@@ -294,9 +356,54 @@
     var value;
     if (columnId === 'path')
       value = this._path;
+    else if (columnId === 'contentType')
+      value = this._request.mimeType;
+    else if (columnId === 'contentLength')
+      value = (this._request.resourceSize | 0).toLocaleString('en-US');
     else if (columnId === 'responseTime')
-      value = this._responseTime;
+      value = new Date(this._request.endTime * 1000).toLocaleString();
     DataGrid.DataGrid.setElementText(cell, value || '', true);
     return cell;
   }
 };
+
+Resources.ServiceWorkerCacheView.RequestView = class extends UI.VBox {
+  /**
+   * @param {!SDK.NetworkRequest} request
+   */
+  constructor(request) {
+    super();
+
+    this._tabbedPane = new UI.TabbedPane();
+    this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, this._tabSelected, this);
+    this._resourceViewTabSetting = Common.settings.createSetting('cacheStorageViewTab', 'preview');
+
+    this._tabbedPane.appendTab('headers', Common.UIString('Headers'), new Network.RequestHeadersView(request));
+    this._tabbedPane.appendTab('preview', Common.UIString('Preview'), new Network.RequestPreviewView(request));
+    this._tabbedPane.show(this.element);
+  }
+
+  /**
+   * @override
+   */
+  wasShown() {
+    super.wasShown();
+    this._selectTab();
+  }
+
+  /**
+   * @param {string=} tabId
+   */
+  _selectTab(tabId) {
+    if (!tabId)
+      tabId = this._resourceViewTabSetting.get();
+    if (!this._tabbedPane.selectTab(tabId))
+      this._tabbedPane.selectTab('headers');
+  }
+
+  _tabSelected(event) {
+    if (!event.data.isUserGesture)
+      return;
+    this._resourceViewTabSetting.set(event.data.tabId);
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/module.json b/third_party/WebKit/Source/devtools/front_end/resources/module.json
index f1ce7dfa..49a4afa 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/resources/module.json
@@ -24,7 +24,8 @@
         "components",
         "object_ui",
         "perf_ui",
-        "mobile_throttling"
+        "mobile_throttling",
+        "network"
     ],
     "scripts": [
         "ApplicationCacheModel.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
index f77d6924..f5e2c7ca 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
@@ -897,16 +897,19 @@
   contentData() {
     if (this._contentData)
       return this._contentData;
-    this._contentData = SDK.NetworkManager.requestContentData(this);
+    if (this._contentDataProvider)
+      this._contentData = this._contentDataProvider();
+    else
+      this._contentData = SDK.NetworkManager.requestContentData(this);
     return this._contentData;
   }
 
   /**
-   * @param {!SDK.NetworkRequest.ContentData} data
+   * @param {function():!Promise<!SDK.NetworkRequest.ContentData>} dataProvider
    */
-  setContentData(data) {
+  setContentDataProvider(dataProvider) {
     console.assert(!this._contentData, 'contentData can only be set once.');
-    this._contentData = Promise.resolve(data);
+    this._contentDataProvider = dataProvider;
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js
index 411d6a7..4eab933 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js
@@ -339,9 +339,10 @@
     var flowEventsEnabled = Runtime.experiments.isEnabled('timelineFlowEvents');
     var blackboxingEnabled = !isExtension && Runtime.experiments.isEnabled('blackboxJSFramesOnTimeline');
     var maxStackDepth = 0;
+    var markerEventsFilter = Timeline.TimelineUIUtils.paintEventsFilter();
     for (var i = 0; i < events.length; ++i) {
       var e = events[i];
-      if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e)) {
+      if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e) && markerEventsFilter.accept(e)) {
         this._markers.push(new Timeline.TimelineFlameChartMarker(
             e.startTime, e.startTime - this._model.minimumRecordTime(),
             Timeline.TimelineUIUtils.markerStyleForEvent(e)));
@@ -956,9 +957,7 @@
    * @return {boolean}
    */
   _isVisible(event) {
-    return this._filters.every(function(filter) {
-      return filter.accept(event);
-    });
+    return this._filters.every(filter => filter.accept(event));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 6b4ef7e..7047745d 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -69,8 +69,6 @@
       this._filters.push(Timeline.TimelineUIUtils.visibleEventsFilter());
       this._filters.push(new TimelineModel.ExcludeTopLevelFilter());
     }
-    if (!Runtime.experiments.isEnabled('timelinePaintTimingMarkers'))
-      this._filters.push(Timeline.TimelineUIUtils.paintEventsFilter());
 
     /** @type {?Timeline.PerformanceModel} */
     this._performanceModel = null;
@@ -846,9 +844,12 @@
     var markers = new Map();
     var recordTypes = TimelineModel.TimelineModel.RecordType;
     var zeroTime = timelineModel.minimumRecordTime();
+    var filter = Timeline.TimelineUIUtils.paintEventsFilter();
     for (var event of timelineModel.eventDividers()) {
       if (event.name === recordTypes.TimeStamp || event.name === recordTypes.ConsoleTime)
         continue;
+      if (!filter.accept(event))
+        continue;
       markers.set(event.startTime, Timeline.TimelineUIUtils.createEventDivider(event, zeroTime));
     }
     this._overviewPane.setMarkers(markers);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
index 6bb5bbd..1e6b3c7 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
@@ -1471,7 +1471,10 @@
    */
   static paintEventsFilter() {
     var recordTypes = TimelineModel.TimelineModel.RecordType;
-    return new TimelineModel.TimelineInvisibleEventsFilter([recordTypes.MarkFMP, recordTypes.MarkFMPCandidate]);
+    return new TimelineModel.TimelineInvisibleEventsFilter(
+        !Runtime.experiments.isEnabled('timelinePaintTimingMarkers') ?
+            [recordTypes.MarkFMP, recordTypes.MarkFMPCandidate] :
+            []);
   }
 
   /**
diff --git a/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp b/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp
index 87da598..8dac57f 100644
--- a/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp
+++ b/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp
@@ -17,6 +17,7 @@
 #include "platform/SharedBuffer.h"
 #include "platform/blob/BlobData.h"
 #include "platform/heap/Handle.h"
+#include "platform/network/HTTPHeaderMap.h"
 #include "platform/weborigin/KURL.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "platform/wtf/Functional.h"
@@ -205,8 +206,12 @@
 
 struct RequestResponse {
   String request_url;
+  String request_method;
+  HTTPHeaderMap request_headers;
+  int response_status;
+  String response_status_text;
   double response_time;
-  Vector<std::pair<String, String>> headers;
+  HTTPHeaderMap response_headers;
 };
 
 class ResponsesAccumulator : public RefCounted<ResponsesAccumulator> {
@@ -226,12 +231,14 @@
     DCHECK_GT(num_responses_left_, 0);
     RequestResponse& request_response =
         responses_.at(responses_.size() - num_responses_left_);
+
     request_response.request_url = request.Url().GetString();
+    request_response.request_method = request.Method();
+    request_response.request_headers = request.Headers();
+    request_response.response_status = response.Status();
+    request_response.response_status_text = response.StatusText();
     request_response.response_time = response.ResponseTime().ToDoubleT();
-    for (const auto& header : response.GetHeaderKeys()) {
-      request_response.headers.push_back(
-          std::make_pair(header, response.GetHeader(header)));
-    }
+    request_response.response_headers = response.Headers();
 
     if (--num_responses_left_ != 0)
       return;
@@ -251,18 +258,17 @@
     }
     std::unique_ptr<Array<DataEntry>> array = Array<DataEntry>::create();
     for (const auto& request_response : responses_) {
-      std::unique_ptr<Array<Header>> headers = Array<Header>::create();
-      for (const auto& header : request_response.headers) {
-        headers->addItem(Header::create()
-                             .setName(header.first)
-                             .setValue(header.second)
-                             .build());
-      }
       std::unique_ptr<DataEntry> entry =
           DataEntry::create()
               .setRequestURL(request_response.request_url)
+              .setRequestMethod(request_response.request_method)
+              .setRequestHeaders(
+                  SerializeHeaders(request_response.request_headers))
+              .setResponseStatus(request_response.response_status)
+              .setResponseStatusText(request_response.response_status_text)
               .setResponseTime(request_response.response_time)
-              .setResponseHeaders(std::move(headers))
+              .setResponseHeaders(
+                  SerializeHeaders(request_response.response_headers))
               .build();
       array->addItem(std::move(entry));
     }
@@ -273,6 +279,18 @@
     callback_->sendFailure(error);
   }
 
+  std::unique_ptr<Array<Header>> SerializeHeaders(
+      const HTTPHeaderMap& headers) {
+    std::unique_ptr<Array<Header>> result = Array<Header>::create();
+    for (HTTPHeaderMap::const_iterator it = headers.begin(),
+                                       end = headers.end();
+         it != end; ++it) {
+      result->addItem(
+          Header::create().setName(it->key).setValue(it->value).build());
+    }
+    return result;
+  }
+
  private:
   DataRequestParams params_;
   int num_responses_left_;
diff --git a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
index c966856..e13b967 100644
--- a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
+++ b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
@@ -18,6 +18,7 @@
 #include "modules/vr/VRDisplay.h"
 #include "modules/vr/VRGetDevicesCallback.h"
 #include "modules/vr/VRPose.h"
+#include "platform/feature_policy/FeaturePolicy.h"
 #include "platform/wtf/PtrUtil.h"
 #include "public/platform/Platform.h"
 
@@ -72,6 +73,23 @@
     return promise;
   }
 
+  LocalFrame* frame = GetDocument()->GetFrame();
+  // TODO(bshe): Add different error string for cases when promise is rejected.
+  if (!frame) {
+    RejectNavigatorDetached(resolver);
+    return promise;
+  }
+  if (IsSupportedInFeaturePolicy(WebFeaturePolicyFeature::kWebVr)) {
+    if (!frame->IsFeatureEnabled(WebFeaturePolicyFeature::kWebVr)) {
+      RejectNavigatorDetached(resolver);
+      return promise;
+    }
+  } else if (!frame->HasReceivedUserGesture() &&
+             frame->IsCrossOriginSubframe()) {
+    RejectNavigatorDetached(resolver);
+    return promise;
+  }
+
   UseCounter::Count(*GetDocument(), WebFeature::kVRGetDisplays);
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   if (!execution_context->IsSecureContext())
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
index 358d129..ecbdd70 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
@@ -11,6 +11,7 @@
 #include "bindings/core/v8/WorkerOrWorkletScriptController.h"
 #include "bindings/modules/v8/V8AudioParamDescriptor.h"
 #include "core/dom/ExceptionCode.h"
+#include "modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h"
 #include "modules/webaudio/AudioBuffer.h"
 #include "modules/webaudio/AudioParamDescriptor.h"
 #include "modules/webaudio/AudioWorkletProcessor.h"
@@ -208,6 +209,23 @@
   return processor_definition_map_.at(name);
 }
 
+unsigned AudioWorkletGlobalScope::NumberOfRegisteredDefinitions() {
+  return processor_definition_map_.size();
+}
+
+std::unique_ptr<Vector<CrossThreadAudioWorkletProcessorInfo>>
+AudioWorkletGlobalScope::WorkletProcessorInfoListForSynchronization() {
+  auto processor_info_list =
+      WTF::MakeUnique<Vector<CrossThreadAudioWorkletProcessorInfo>>();
+  for (auto definition_entry : processor_definition_map_) {
+    if (!definition_entry.value->IsSynchronized()) {
+      definition_entry.value->MarkAsSynchronized();
+      processor_info_list->emplace_back(*definition_entry.value);
+    }
+  }
+  return processor_info_list;
+}
+
 DEFINE_TRACE(AudioWorkletGlobalScope) {
   visitor->Trace(processor_definition_map_);
   visitor->Trace(processor_instances_);
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.h
index 9023bef..b32f2d4 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.h
@@ -9,6 +9,7 @@
 #include "core/dom/ExecutionContext.h"
 #include "core/workers/ThreadedWorkletGlobalScope.h"
 #include "modules/ModulesExport.h"
+#include "modules/webaudio/AudioParamDescriptor.h"
 #include "platform/bindings/ScriptWrappable.h"
 
 namespace blink {
@@ -16,6 +17,7 @@
 class AudioBuffer;
 class AudioWorkletProcessor;
 class AudioWorkletProcessorDefinition;
+class CrossThreadAudioWorkletProcessorInfo;
 class ExceptionState;
 
 // This is constructed and destroyed on a worker thread, and all methods also
@@ -50,6 +52,11 @@
 
   AudioWorkletProcessorDefinition* FindDefinition(const String& name);
 
+  unsigned NumberOfRegisteredDefinitions();
+
+  std::unique_ptr<Vector<CrossThreadAudioWorkletProcessorInfo>>
+      WorkletProcessorInfoListForSynchronization();
+
   DECLARE_TRACE();
   DECLARE_TRACE_WRAPPERS();
 
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.cpp
index f9d23c1..64893c1 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.cpp
@@ -4,6 +4,7 @@
 
 #include "modules/webaudio/AudioWorkletMessagingProxy.h"
 
+#include "modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h"
 #include "modules/webaudio/AudioWorkletObjectProxy.h"
 #include "modules/webaudio/AudioWorkletThread.h"
 
@@ -16,18 +17,32 @@
 
 AudioWorkletMessagingProxy::~AudioWorkletMessagingProxy() {}
 
-void AudioWorkletMessagingProxy::SynchronizeWorkletData() {
+void AudioWorkletMessagingProxy::SynchronizeWorkletProcessorInfoList(
+    std::unique_ptr<Vector<CrossThreadAudioWorkletProcessorInfo>> info_list) {
   DCHECK(IsMainThread());
 
-  // TODO(crbug.com/755566): the argument will be a set of a node name and
-  // parameter descriptors. Use the information to update the copy in
-  // AudioWorkletMessagingProxy.
+  for (auto& processor_info : *info_list) {
+    processor_info_map_.insert(processor_info.Name(),
+                               processor_info.ParamInfoList());
+  }
+}
+
+bool AudioWorkletMessagingProxy::IsProcessorRegistered(
+    const String& name) const {
+  return processor_info_map_.Contains(name);
+}
+
+const Vector<CrossThreadAudioParamInfo>
+AudioWorkletMessagingProxy::GetParamInfoListForProcessor(
+    const String& name) const {
+  DCHECK(IsProcessorRegistered(name));
+  return processor_info_map_.at(name);
 }
 
 std::unique_ptr<ThreadedWorkletObjectProxy>
-    AudioWorkletMessagingProxy::CreateObjectProxy(
-        ThreadedWorkletMessagingProxy* messaging_proxy,
-        ParentFrameTaskRunners* parent_frame_task_runners) {
+AudioWorkletMessagingProxy::CreateObjectProxy(
+    ThreadedWorkletMessagingProxy* messaging_proxy,
+    ParentFrameTaskRunners* parent_frame_task_runners) {
   return WTF::MakeUnique<AudioWorkletObjectProxy>(
       static_cast<AudioWorkletMessagingProxy*>(messaging_proxy),
       parent_frame_task_runners);
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.h
index 216f208..b20d4d5c5 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletMessagingProxy.h
@@ -10,6 +10,8 @@
 
 namespace blink {
 
+class CrossThreadAudioParamInfo;
+class CrossThreadAudioWorkletProcessorInfo;
 class ExecutionContext;
 class WorkerThread;
 
@@ -20,9 +22,20 @@
  public:
   AudioWorkletMessagingProxy(ExecutionContext*, WorkerClients*);
 
-  // Invoked by AudioWorkletObjectProxy to synchronize the information from
-  // AudioWorkletGlobalScope after the script code evaluation.
-  void SynchronizeWorkletData();
+  // Invoked by AudioWorkletObjectProxy on AudioWorkletThread to fetch the
+  // information from AudioWorkletGlobalScope to AudioWorkletMessagingProxy
+  // after the script code evaluation. It copies the information about newly
+  // added AudioWorkletProcessor since the previous synchronization. (e.g.
+  // processor name and AudioParam list)
+  void SynchronizeWorkletProcessorInfoList(
+      std::unique_ptr<Vector<CrossThreadAudioWorkletProcessorInfo>>);
+
+  // Returns true if the processor with given name is registered in
+  // AudioWorkletGlobalScope.
+  bool IsProcessorRegistered(const String& name) const;
+
+  const Vector<CrossThreadAudioParamInfo> GetParamInfoListForProcessor(
+      const String& name) const;
 
  private:
   ~AudioWorkletMessagingProxy() override;
@@ -33,6 +46,9 @@
       ParentFrameTaskRunners*) override;
 
   std::unique_ptr<WorkerThread> CreateWorkerThread() override;
+
+  // Each entry consists of processor name and associated AudioParam list.
+  HashMap<String, Vector<CrossThreadAudioParamInfo>> processor_info_map_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.cpp
index 5829a1e..479180db 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletObjectProxy.cpp
@@ -6,6 +6,7 @@
 
 #include "core/workers/ThreadedWorkletMessagingProxy.h"
 #include "core/workers/WorkerThread.h"
+#include "modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h"
 #include "modules/webaudio/AudioWorkletGlobalScope.h"
 #include "modules/webaudio/AudioWorkletMessagingProxy.h"
 #include "platform/CrossThreadFunctional.h"
@@ -26,15 +27,23 @@
 
 void AudioWorkletObjectProxy::DidEvaluateModuleScript(bool success) {
   DCHECK(global_scope_);
-  // TODO(crbug.com/755566): Extract/build the information for synchronization
-  // and send it to the associated AudioWorkletMessagingProxy. Currently this
-  // is an empty cross-thread call for the future implementation.
-  GetParentFrameTaskRunners()->Get(TaskType::kUnthrottled)
-       ->PostTask(
-           BLINK_FROM_HERE,
-           CrossThreadBind(
-                &AudioWorkletMessagingProxy::SynchronizeWorkletData,
-                GetAudioWorkletMessagingProxyWeakPtr()));
+
+  if (!success || global_scope_->NumberOfRegisteredDefinitions() == 0)
+    return;
+
+  std::unique_ptr<Vector<CrossThreadAudioWorkletProcessorInfo>>
+      processor_info_list =
+          global_scope_->WorkletProcessorInfoListForSynchronization();
+
+  if (processor_info_list->size() == 0)
+    return;
+
+  GetParentFrameTaskRunners()->Get(TaskType::kUnthrottled)->PostTask(
+      BLINK_FROM_HERE,
+      CrossThreadBind(
+          &AudioWorkletMessagingProxy::SynchronizeWorkletProcessorInfoList,
+          GetAudioWorkletMessagingProxyWeakPtr(),
+          WTF::Passed(std::move(processor_info_list))));
 }
 
 void AudioWorkletObjectProxy::WillDestroyWorkerGlobalScope() {
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h
index bc08de3..d3f20f5 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorDefinition.h
@@ -41,6 +41,11 @@
   const Vector<String> GetAudioParamDescriptorNames() const;
   const AudioParamDescriptor* GetAudioParamDescriptor(const String& key) const;
 
+  // Flag for data synchronization of definition between
+  // AudioWorkletMessagingProxy and AudioWorkletGlobalScope.
+  bool IsSynchronized() const { return is_synchronized_; }
+  void MarkAsSynchronized() { is_synchronized_ = true; }
+
   DEFINE_INLINE_TRACE() { visitor->Trace(audio_param_descriptors_); };
   DECLARE_TRACE_WRAPPERS();
 
@@ -52,6 +57,7 @@
       v8::Local<v8::Function> process);
 
   const String name_;
+  bool is_synchronized_ = false;
 
   // The definition is per global scope. The active instance of
   // |AudioProcessorWorklet| should be passed into these to perform JS function.
diff --git a/third_party/WebKit/Source/modules/webaudio/BUILD.gn b/third_party/WebKit/Source/modules/webaudio/BUILD.gn
index fe0f3da4..c487eb82 100644
--- a/third_party/WebKit/Source/modules/webaudio/BUILD.gn
+++ b/third_party/WebKit/Source/modules/webaudio/BUILD.gn
@@ -72,6 +72,7 @@
     "ConstantSourceNode.h",
     "ConvolverNode.cpp",
     "ConvolverNode.h",
+    "CrossThreadAudioWorkletProcessorInfo.h",
     "DefaultAudioDestinationNode.cpp",
     "DefaultAudioDestinationNode.h",
     "DeferredTaskHandler.cpp",
diff --git a/third_party/WebKit/Source/modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h b/third_party/WebKit/Source/modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h
new file mode 100644
index 0000000..c556ff12
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CrossThreadAudioWorkletProcessorInfo_h
+#define CrossThreadAudioWorkletProcessorInfo_h
+
+#include "modules/webaudio/AudioParamDescriptor.h"
+#include "modules/webaudio/AudioWorkletProcessorDefinition.h"
+
+namespace blink {
+
+// A class for shallow repackage of |AudioParamDescriptor|. This is created only
+// when requested when the synchronization between AudioWorkletMessagingProxy
+// and AudioWorkletGlobalScope.
+class CrossThreadAudioParamInfo {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+  explicit CrossThreadAudioParamInfo(const AudioParamDescriptor* descriptor)
+      : name_(descriptor->name().IsolatedCopy()),
+        default_value_(descriptor->defaultValue()),
+        max_value_(descriptor->maxValue()),
+        min_value_(descriptor->minValue()) {}
+
+  const String& Name() const { return name_; }
+  float DefaultValue() const { return default_value_; }
+  float MaxValue() const { return max_value_; }
+  float MinValue() const { return min_value_; }
+
+ private:
+  const String name_;
+  const float default_value_;
+  const float max_value_;
+  const float min_value_;
+};
+
+// A class for shallow repackage of |AudioWorkletProcessorDefinition|. This is
+// created only when requested when the synchronization between
+// AudioWorkletMessagingProxy and AudioWorkletGlobalScope.
+class CrossThreadAudioWorkletProcessorInfo {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+  explicit CrossThreadAudioWorkletProcessorInfo(
+      const AudioWorkletProcessorDefinition& definition)
+      : name_(definition.GetName().IsolatedCopy()) {
+    // To avoid unnecessary reallocations of the vector.
+    param_info_list_.ReserveInitialCapacity(
+        definition.GetAudioParamDescriptorNames().size());
+
+    for (const String& name : definition.GetAudioParamDescriptorNames()) {
+      param_info_list_.emplace_back(
+          definition.GetAudioParamDescriptor(name));
+    }
+  }
+
+  const String& Name() const { return name_; }
+  Vector<CrossThreadAudioParamInfo> ParamInfoList() { return param_info_list_; }
+
+ private:
+  const String name_;
+  Vector<CrossThreadAudioParamInfo> param_info_list_;
+};
+
+}  // namespace blink
+
+#endif  // CrossThreadAudioWorkletProcessorInfo_h
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
index ab9d06e7..2a0f48a 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
@@ -1,4 +1,3 @@
-// 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.
 
@@ -31,6 +30,9 @@
   // Old syntax only allows whitespace as valid delimiter.
   if (policy.Contains(';') || policy.Contains(','))
     return false;
+  // An empty policy is also allowed in the new syntax.
+  if (policy.ContainsOnlyWhitespace())
+    return false;
   // Old syntax does not support specifying wildcards / origins for any feature.
   if (policy.Contains("self") || policy.Contains("src") ||
       policy.Contains("none") || policy.Contains("*")) {
@@ -126,6 +128,9 @@
       //     "name value1 value2" or "name".
       Vector<String> tokens;
       entry.Split(' ', tokens);
+      // Empty policy. Skip.
+      if (tokens.IsEmpty())
+        continue;
       if (!feature_names.Contains(tokens[0])) {
         if (messages)
           messages->push_back("Unrecognized feature: '" + tokens[0] + "'.");
@@ -184,6 +189,7 @@
     case WebFeaturePolicyFeature::kFullscreen:
     case WebFeaturePolicyFeature::kPayment:
     case WebFeaturePolicyFeature::kUsb:
+    case WebFeaturePolicyFeature::kWebVr:
       return true;
     case WebFeaturePolicyFeature::kVibrate:
       return RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled();
@@ -208,6 +214,7 @@
     default_feature_name_map.Set("geolocation",
                                  WebFeaturePolicyFeature::kGeolocation);
     default_feature_name_map.Set("midi", WebFeaturePolicyFeature::kMidiFeature);
+    default_feature_name_map.Set("vr", WebFeaturePolicyFeature::kWebVr);
     if (RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled()) {
       default_feature_name_map.Set("vibrate",
                                    WebFeaturePolicyFeature::kVibrate);
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
index 785d8ca..2a86539e 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
@@ -17,7 +17,13 @@
 namespace {
 
 const char* const kValidPolicies[] = {
-    ";;",  // Empty policies.
+    "",      // An empty policy.
+    " ",     // An empty policy.
+    ";;",    // Empty policies.
+    ",,",    // Empty policies.
+    " ; ;",  // Empty policies.
+    " , ,",  // Empty policies.
+    ",;,",   // Empty policies.
     "vibrate 'none'",
     "vibrate 'self'",
     "vibrate 'src'",  // Only valid for iframe allow attribute.
diff --git a/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp b/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp
index 1a37e94..920568f 100644
--- a/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp
@@ -15,10 +15,104 @@
 
 namespace {
 
+// This must match ICCProfileAnalyzeResult enum in histograms.xml.
+enum ICCAnalyzeResult {
+  kICCExtractedMatrixAndAnalyticTrFn = 0,
+  kICCExtractedMatrixAndApproximatedTrFn = 1,
+  kICCFailedToApproximateTrFn = 2,
+  kICCFailedToExtractRawTrFn = 3,
+  kICCFailedToExtractMatrix = 4,
+  kICCFailedToParse = 5,
+  kICCProfileAnalyzeLast = kICCFailedToParse
+};
+
 // The output device color space is global and shared across multiple threads.
 SpinLock g_target_color_space_lock;
 gfx::ColorSpace* g_target_color_space = nullptr;
 
+ICCAnalyzeResult HistogramICCProfile(const gfx::ICCProfile& profile) {
+  std::vector<char> data = profile.GetData();
+  sk_sp<SkICC> sk_icc = SkICC::Make(data.data(), data.size());
+  if (!sk_icc)
+    return kICCFailedToParse;
+
+  SkMatrix44 to_xyzd50;
+  bool to_xyzd50_result = sk_icc->toXYZD50(&to_xyzd50);
+  if (!to_xyzd50_result)
+    return kICCFailedToExtractMatrix;
+
+  SkColorSpaceTransferFn fn;
+  bool is_numerical_transfer_fn_result = sk_icc->isNumericalTransferFn(&fn);
+  if (is_numerical_transfer_fn_result)
+    return kICCExtractedMatrixAndAnalyticTrFn;
+
+  // Analyze the numerical approximation of table-based transfer functions.
+  // This should never fail in practice, because any profile from which a
+  // primary matrix was extracted will also provide raw transfer data.
+  SkICC::Tables tables;
+  bool raw_transfer_fn_result = sk_icc->rawTransferFnData(&tables);
+  DCHECK(raw_transfer_fn_result);
+  if (!raw_transfer_fn_result)
+    return kICCFailedToExtractRawTrFn;
+
+  // Analyze the channels separately.
+  std::vector<float> x_combined;
+  std::vector<float> t_combined;
+  for (size_t c = 0; c < 3; ++c) {
+    SkICC::Channel* channels[3] = {&tables.fRed, &tables.fGreen, &tables.fBlue};
+    SkICC::Channel* channel = channels[c];
+    DCHECK_GE(channel->fCount, 2);
+    const float* data = reinterpret_cast<const float*>(
+        tables.fStorage->bytes() + channel->fOffset);
+    std::vector<float> x;
+    std::vector<float> t;
+    for (int i = 0; i < channel->fCount; ++i) {
+      float xi = i / (channel->fCount - 1.f);
+      float ti = data[i];
+      x.push_back(xi);
+      t.push_back(ti);
+      x_combined.push_back(xi);
+      t_combined.push_back(ti);
+    }
+
+    bool nonlinear_fit_converged =
+        gfx::SkApproximateTransferFn(x.data(), t.data(), x.size(), &fn);
+    UMA_HISTOGRAM_BOOLEAN("Blink.ColorSpace.Destination.NonlinearFitConverged",
+                          nonlinear_fit_converged);
+
+    // Record the accuracy of the fit, separating out by nonlinear and
+    // linear fits.
+    if (nonlinear_fit_converged) {
+      float max_error = 0.f;
+      for (size_t i = 0; i < x.size(); ++i) {
+        float fn_of_xi = gfx::SkTransferFnEval(fn, x[i]);
+        float error_at_xi = std::abs(t[i] - fn_of_xi);
+        max_error = std::max(max_error, error_at_xi);
+      }
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Blink.ColorSpace.Destination.NonlinearFitError",
+          static_cast<int>(max_error * 255), 0, 127, 16);
+    }
+  }
+
+  bool combined_nonlinear_fit_converged = gfx::SkApproximateTransferFn(
+      x_combined.data(), t_combined.data(), x_combined.size(), &fn);
+  if (!combined_nonlinear_fit_converged)
+    return kICCFailedToApproximateTrFn;
+
+  float combined_max_error = 0.f;
+  for (size_t i = 0; i < x_combined.size(); ++i) {
+    float fn_of_xi = gfx::SkTransferFnEval(fn, x_combined[i]);
+    float error_at_xi = std::abs(t_combined[i] - fn_of_xi);
+    combined_max_error = std::max(combined_max_error, error_at_xi);
+  }
+  UMA_HISTOGRAM_CUSTOM_COUNTS(
+      "Blink.ColorSpace.Destination.NonlinearFitErrorCombined",
+      static_cast<int>(combined_max_error * 255), 0, 127, 16);
+
+  return kICCExtractedMatrixAndApproximatedTrFn;
+}
+
 }  // namespace
 
 // static
@@ -33,9 +127,14 @@
     return;
 
   // Attempt to convert the ICC profile to an SkColorSpace.
-  if (profile != gfx::ICCProfile())
+  if (profile != gfx::ICCProfile()) {
     g_target_color_space = new gfx::ColorSpace(profile.GetColorSpace());
 
+    ICCAnalyzeResult analyze_result = HistogramICCProfile(profile);
+    UMA_HISTOGRAM_ENUMERATION("Blink.ColorSpace.Destination.ICCResult",
+                              analyze_result, kICCProfileAnalyzeLast);
+  }
+
   // If we do not succeed, assume sRGB.
   if (!g_target_color_space)
     g_target_color_space = new gfx::ColorSpace(gfx::ColorSpace::CreateSRGB());
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index 9e62623..e968433 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -8,7 +8,6 @@
 #include <set>
 
 #include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "platform/scheduler/base/real_time_domain.h"
 #include "platform/scheduler/base/task_queue_impl.h"
@@ -25,19 +24,6 @@
 namespace scheduler {
 
 namespace {
-const size_t kRecordRecordTaskDelayHistogramsEveryNTasks = 10;
-
-void RecordDelayedTaskLateness(base::TimeDelta lateness) {
-  UMA_HISTOGRAM_TIMES("RendererScheduler.TaskQueueManager.DelayedTaskLateness",
-                      lateness);
-}
-
-void RecordImmediateTaskQueueingDuration(base::TimeDelta duration) {
-  UMA_HISTOGRAM_TIMES(
-      "RendererScheduler.TaskQueueManager.ImmediateTaskQueueingDuration",
-      duration);
-}
-
 double MonotonicTimeInSeconds(base::TimeTicks time_ticks) {
   return (time_ticks - base::TimeTicks()).InSecondsF();
 }
@@ -51,14 +37,13 @@
   return base::BindRepeating([](base::OnceClosure cb) { std::move(cb).Run(); },
                              base::Passed(&cb));
 }
-}
+}  // namespace
 
 TaskQueueManager::TaskQueueManager(
     scoped_refptr<TaskQueueManagerDelegate> delegate)
     : real_time_domain_(new RealTimeDomain()),
       delegate_(delegate),
       task_was_run_on_quiescence_monitored_queue_(false),
-      record_task_delay_histograms_(true),
       work_batch_size_(1),
       task_count_(0),
       currently_executing_task_queue_(nullptr),
@@ -498,9 +483,6 @@
     return ProcessTaskResult::DEFERRED;
   }
 
-  if (record_task_delay_histograms_)
-    MaybeRecordTaskDelayHistograms(pending_task, queue);
-
   double task_start_time_sec = 0;
   base::TimeTicks task_start_time;
   TRACE_TASK_EXECUTION("TaskQueueManager::ProcessTaskFromWorkQueue",
@@ -565,22 +547,6 @@
   return ProcessTaskResult::EXECUTED;
 }
 
-void TaskQueueManager::MaybeRecordTaskDelayHistograms(
-    const internal::TaskQueueImpl::Task& pending_task,
-    const internal::TaskQueueImpl* queue) {
-  if ((task_count_++ % kRecordRecordTaskDelayHistogramsEveryNTasks) != 0)
-    return;
-
-  // Record delayed task lateness and immediate task queuing durations.
-  if (!pending_task.delayed_run_time.is_null()) {
-    RecordDelayedTaskLateness(delegate_->NowTicks() -
-                              pending_task.delayed_run_time);
-  } else if (!pending_task.time_posted.is_null()) {
-    RecordImmediateTaskQueueingDuration(base::TimeTicks::Now() -
-                                        pending_task.time_posted);
-  }
-}
-
 bool TaskQueueManager::RunsTasksInCurrentSequence() const {
   return delegate_->RunsTasksInCurrentSequence();
 }
@@ -703,12 +669,6 @@
   return !selector_.EnabledWorkQueuesEmpty();
 }
 
-void TaskQueueManager::SetRecordTaskDelayHistograms(
-    bool record_task_delay_histograms) {
-  DCHECK(main_thread_checker_.CalledOnValidThread());
-  record_task_delay_histograms_ = record_task_delay_histograms;
-}
-
 void TaskQueueManager::SweepCanceledDelayedTasks() {
   std::map<TimeDomain*, base::TimeTicks> time_domain_now;
   for (const auto& queue : queues_) {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
index 2bbf7095..d1e856b 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -151,10 +151,6 @@
   // Removes all canceled delayed tasks.
   void SweepCanceledDelayedTasks();
 
-  // There is a small overhead to recording task delay histograms. If you don't
-  // need them, you can turn them off.
-  void SetRecordTaskDelayHistograms(bool record_task_delay_histograms);
-
  protected:
   friend class LazyNow;
   friend class internal::TaskQueueImpl;
@@ -287,10 +283,6 @@
   base::Optional<NextTaskDelay> ComputeDelayTillNextTaskLocked(
       LazyNow* lazy_now);
 
-  void MaybeRecordTaskDelayHistograms(
-      const internal::TaskQueueImpl::Task& pending_task,
-      const internal::TaskQueueImpl* queue);
-
   std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
   AsValueWithSelectorResult(bool should_run,
                             internal::WorkQueue* selected_work_queue) const;
@@ -367,8 +359,6 @@
 
   NextDelayedDoWork next_delayed_do_work_;
 
-  bool record_task_delay_histograms_;
-
   int work_batch_size_;
   size_t task_count_;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
index e134a23..d83d9a3 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
@@ -42,15 +42,6 @@
   task_queue_manager_delegate_->RestoreDefaultTaskRunner();
 }
 
-void SchedulerHelper::SetRecordTaskDelayHistograms(
-    bool record_task_delay_histograms) {
-  if (!task_queue_manager_)
-    return;
-
-  task_queue_manager_->SetRecordTaskDelayHistograms(
-      record_task_delay_histograms);
-}
-
 size_t SchedulerHelper::GetNumberOfPendingTasks() const {
   return task_queue_manager_->GetNumberOfPendingTasks();
 }
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
index 9a38772..7dae827 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
@@ -25,10 +25,6 @@
       scoped_refptr<SchedulerTqmDelegate> task_queue_manager_delegate);
   ~SchedulerHelper() override;
 
-  // There is a small overhead to recording task delay histograms, we may not
-  // wish to do this on all threads.
-  void SetRecordTaskDelayHistograms(bool record_task_delay_histograms);
-
   // TaskQueueManager::Observer implementation:
   void OnTriedToExecuteBlockedTask() override;
   void OnBeginNestedRunLoop() override;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/user_model.cc b/third_party/WebKit/Source/platform/scheduler/renderer/user_model.cc
index 34ad48dd..8868026 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/user_model.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/user_model.cc
@@ -4,29 +4,9 @@
 
 #include "platform/scheduler/renderer/user_model.h"
 
-#include "base/metrics/histogram_macros.h"
-
 namespace blink {
 namespace scheduler {
 
-namespace {
-// This enum is used to back a histogram, and should therefore be treated as
-// append-only.
-enum GesturePredictionResult {
-  GESTURE_OCCURED_WAS_PREDICTED = 0,
-  GESTURE_OCCURED_BUT_NOT_PREDICTED = 1,
-  GESTURE_PREDICTED_BUT_DID_NOT_OCCUR = 2,
-  GESTURE_PREDICTION_RESULT_COUNT = 3
-};
-
-void RecordGesturePrediction(GesturePredictionResult result) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "RendererScheduler.UserModel.GesturePredictedCorrectly", result,
-      GESTURE_PREDICTION_RESULT_COUNT);
-}
-
-}  // namespace
-
 UserModel::UserModel()
     : pending_input_event_count_(0),
       is_gesture_active_(false),
@@ -40,31 +20,9 @@
       type == blink::WebInputEvent::kGestureScrollBegin ||
       type == blink::WebInputEvent::kGesturePinchBegin) {
     // Only update stats once per gesture.
-    if (!is_gesture_active_) {
+    if (!is_gesture_active_)
       last_gesture_start_time_ = now;
 
-      RecordGesturePrediction(is_gesture_expected_
-                                  ? GESTURE_OCCURED_WAS_PREDICTED
-                                  : GESTURE_OCCURED_BUT_NOT_PREDICTED);
-
-      if (!last_reset_time_.is_null()) {
-        base::TimeDelta time_since_reset = now - last_reset_time_;
-        UMA_HISTOGRAM_MEDIUM_TIMES(
-            "RendererScheduler.UserModel.GestureStartTimeSinceModelReset",
-            time_since_reset);
-      }
-
-      // If there has been a previous gesture, record a UMA metric for the time
-      // interval between then and now.
-      if (!last_continuous_gesture_time_.is_null()) {
-        base::TimeDelta time_since_last_gesture =
-            now - last_continuous_gesture_time_;
-        UMA_HISTOGRAM_MEDIUM_TIMES(
-            "RendererScheduler.UserModel.TimeBetweenGestures",
-            time_since_last_gesture);
-      }
-    }
-
     is_gesture_active_ = true;
   }
 
@@ -87,12 +45,6 @@
       type == blink::WebInputEvent::kGesturePinchEnd ||
       type == blink::WebInputEvent::kGestureFlingStart ||
       type == blink::WebInputEvent::kTouchEnd) {
-    // Only update stats once per gesture.
-    if (is_gesture_active_) {
-      base::TimeDelta duration = now - last_gesture_start_time_;
-      UMA_HISTOGRAM_TIMES("RendererScheduler.UserModel.GestureDuration",
-                          duration);
-    }
     is_gesture_active_ = false;
   }
 
@@ -134,11 +86,6 @@
   // gesture actually happened.
   if (!was_gesture_expected && is_gesture_expected_)
     last_gesture_expected_start_time_ = now;
-
-  if (was_gesture_expected && !is_gesture_expected_ &&
-      last_gesture_expected_start_time_ > last_gesture_start_time_) {
-    RecordGesturePrediction(GESTURE_PREDICTED_BUT_DID_NOT_OCCUR);
-  }
   return is_gesture_expected_;
 }
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
index a5f8e1f..c220c1e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
@@ -191,33 +191,43 @@
         port = self.make_port()
         self.assertTrue(port.test_configuration())
 
-    def test_get_crash_log(self):
+    def test_get_crash_log_all_none(self):
         port = self.make_port()
-        self.assertEqual(port._get_crash_log(None, None, None, None, newer_than=None),
-                         (None,
-                          'crash log for <unknown process name> (pid <unknown>):\n'
-                          'STDOUT: <empty>\n'
-                          'STDERR: <empty>\n'))
+        stderr, details = port._get_crash_log(None, None, None, None, newer_than=None)
+        self.assertIsNone(stderr)
+        self.assertEqual(details,
+                         'crash log for <unknown process name> (pid <unknown>):\n'
+                         'STDOUT: <empty>\n'
+                         'STDERR: <empty>\n')
 
-        self.assertEqual(port._get_crash_log('foo', 1234, 'out bar\nout baz', 'err bar\nerr baz\n', newer_than=None),
-                         ('err bar\nerr baz\n',
-                          'crash log for foo (pid 1234):\n'
-                          'STDOUT: out bar\n'
-                          'STDOUT: out baz\n'
-                          'STDERR: err bar\n'
-                          'STDERR: err baz\n'))
+    def test_get_crash_log_simple(self):
+        port = self.make_port()
+        stderr, details = port._get_crash_log('foo', 1234, 'out bar\nout baz', 'err bar\nerr baz\n', newer_than=None)
+        self.assertEqual(stderr, 'err bar\nerr baz\n')
+        self.assertEqual(details,
+                         'crash log for foo (pid 1234):\n'
+                         'STDOUT: out bar\n'
+                         'STDOUT: out baz\n'
+                         'STDERR: err bar\n'
+                         'STDERR: err baz\n')
 
-        self.assertEqual(port._get_crash_log('foo', 1234, 'foo\xa6bar', 'foo\xa6bar', newer_than=None),
-                         ('foo\xa6bar',
-                          u'crash log for foo (pid 1234):\n'
-                          u'STDOUT: foo\ufffdbar\n'
-                          u'STDERR: foo\ufffdbar\n'))
+    def test_get_crash_log_non_ascii(self):
+        port = self.make_port()
+        stderr, details = port._get_crash_log('foo', 1234, 'foo\xa6bar', 'foo\xa6bar', newer_than=None)
+        self.assertEqual(stderr, 'foo\xa6bar')
+        self.assertEqual(details,
+                         u'crash log for foo (pid 1234):\n'
+                         u'STDOUT: foo\ufffdbar\n'
+                         u'STDERR: foo\ufffdbar\n')
 
-        self.assertEqual(port._get_crash_log('foo', 1234, 'foo\xa6bar', 'foo\xa6bar', newer_than=1.0),
-                         ('foo\xa6bar',
-                          u'crash log for foo (pid 1234):\n'
-                          u'STDOUT: foo\ufffdbar\n'
-                          u'STDERR: foo\ufffdbar\n'))
+    def test_get_crash_log_newer_than(self):
+        port = self.make_port()
+        stderr, details = port._get_crash_log('foo', 1234, 'foo\xa6bar', 'foo\xa6bar', newer_than=1.0)
+        self.assertEqual(stderr, 'foo\xa6bar')
+        self.assertEqual(details,
+                         u'crash log for foo (pid 1234):\n'
+                         u'STDOUT: foo\ufffdbar\n'
+                         u'STDERR: foo\ufffdbar\n')
 
     def test_expectations_files(self):
         port = self.make_port()
diff --git a/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h b/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
index 2c271ba5..b67f918 100644
--- a/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
+++ b/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
@@ -47,7 +47,9 @@
   kUsb,
   // Controls access to AOM event listeners.
   kAccessibilityEvents,
-  LAST_FEATURE = kAccessibilityEvents
+  // Controls use of WebVR API.
+  kWebVr,
+  LAST_FEATURE = kWebVr
 };
 
 }  // namespace blink
diff --git a/third_party/android_support_test_runner/BUILD.gn b/third_party/android_support_test_runner/BUILD.gn
index 1f0a6e8b..7383d4b 100644
--- a/third_party/android_support_test_runner/BUILD.gn
+++ b/third_party/android_support_test_runner/BUILD.gn
@@ -19,5 +19,6 @@
 }
 
 android_aar_prebuilt("rules_java") {
+  testonly = true
   aar_path = "lib/rules-0.5.aar"
 }
diff --git a/third_party/bazel/desugar/BUILD.gn b/third_party/bazel/desugar/BUILD.gn
index bff746ab..6d9df28f 100644
--- a/third_party/bazel/desugar/BUILD.gn
+++ b/third_party/bazel/desugar/BUILD.gn
@@ -8,4 +8,5 @@
 java_prebuilt("desugar_runtime_java") {
   supports_android = true
   jar_path = "Desugar-runtime.jar"
+  no_build_hooks = true
 }
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index 2d7a0c0a7..981b6f4 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -215,7 +215,7 @@
   tries really hard to always write something to the output JSON and convey
   errors that way rather than via return codes.
 ```
-### <a name="args"></a>**gn args <out_dir> [\--list] [\--short] [\--args]**
+### <a name="args"></a>**gn args <out_dir> [\--list] [\--short] [\--args] [\--overrides-only]**
 
 ```
   See also "gn help buildargs" for a more high-level overview of how
@@ -240,7 +240,7 @@
       Note: you can edit the build args manually by editing the file "args.gn"
       in the build directory and then running "gn gen <out_dir>".
 
-  gn args <out_dir> --list[=<exact_arg>] [--short]
+  gn args <out_dir> --list[=<exact_arg>] [--short] [--overrides-only]
       Lists all build arguments available in the current configuration, or, if
       an exact_arg is specified for the list flag, just that one build
       argument.
@@ -251,6 +251,10 @@
 
       If --short is specified, only the names and current values will be
       printed.
+
+      If --overrides-only is specified, only the names and current values of
+      arguments that have been overridden (i.e. non-default arguments) will
+      be printed. Overrides come from the <out_dir>/args.gn file and //.gn
 ```
 
 #### **Examples**
@@ -263,6 +267,9 @@
     Prints all arguments with their default values for the out/Debug
     build.
 
+  gn args out/Debug --list --short --overrides-only
+    Prints overridden arguments for the out/Debug build.
+
   gn args out/Debug --list=target_cpu
     Prints information about the "target_cpu" argument for the "
    "out/Debug
@@ -6090,13 +6097,6 @@
   itself).
 ```
 
-#### **Shared libraries**
-
-```
-  The results of shared_library targets are runtime dependencies, unless the
-  targets are depended upon only through action/action_foreach.
-```
-
 #### **Multiple outputs**
 
 ```
@@ -6242,3 +6242,4 @@
     *   [-v: Verbose logging.](#-v)
     *   [--version: Prints the GN version number and exits.](#--version)
 ```
+
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4ee8bdad..3e3cbab 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19933,14 +19933,6 @@
   <int value="3" label="Failed to extract transfer function"/>
   <int value="4" label="Failed to extract primary matrix"/>
   <int value="5" label="Failed to parse"/>
-  <int value="6" label="Parsed, but failed to extract SkColorSpace"/>
-  <int value="7"
-      label="Parsed and extracteed SkColorSpace, but failed to create
-             SkColorSpaceXform to this space"/>
-  <int value="8"
-      label="Converged to an insufficiently accurate approximation of
-             transfer function"/>
-  <int value="9" label="Extracted an sRGB profile directly"/>
 </enum>
 
 <enum name="IceCandidatePairTypes">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0df2901..1f8e391 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -64941,6 +64941,9 @@
 
 <histogram name="RendererScheduler.TaskQueueManager.DelayedTaskLateness"
     units="ms">
+  <obsolete>
+    Removed from code 2017-08.
+  </obsolete>
   <owner>alexclarke@chromium.org</owner>
   <summary>
     The delta between when a delayed task was scheduled to run and when the
@@ -64952,6 +64955,9 @@
 <histogram
     name="RendererScheduler.TaskQueueManager.ImmediateTaskQueueingDuration"
     units="ms">
+  <obsolete>
+    Removed from code 2017-08.
+  </obsolete>
   <owner>alexclarke@chromium.org</owner>
   <summary>
     The queueing duration for non-delayed tasks posted to the RendererScheduler.
@@ -64995,18 +65001,27 @@
 </histogram>
 
 <histogram name="RendererScheduler.UserModel.GestureDuration" units="ms">
+  <obsolete>
+    Removed from code 2017-08.
+  </obsolete>
   <owner>alexclarke@chromium.org</owner>
   <summary>Duration of gestures (scrolls and pinches).</summary>
 </histogram>
 
 <histogram name="RendererScheduler.UserModel.GesturePredictedCorrectly"
     units="GesturePredictionResult">
+  <obsolete>
+    Removed from code 2017-08.
+  </obsolete>
   <owner>alexclarke@chromium.org</owner>
   <summary>Whether a user gesture was predicted correctly.</summary>
 </histogram>
 
 <histogram name="RendererScheduler.UserModel.GestureStartTimeSinceModelReset"
     units="ms">
+  <obsolete>
+    Removed from code 2017-08.
+  </obsolete>
   <owner>alexclarke@chromium.org</owner>
   <summary>
     Time between when the UserModel was last reset (which happens on navigation)
@@ -65015,6 +65030,9 @@
 </histogram>
 
 <histogram name="RendererScheduler.UserModel.TimeBetweenGestures" units="ms">
+  <obsolete>
+    Removed from code 2017-08.
+  </obsolete>
   <owner>alexclarke@chromium.org</owner>
   <summary>Time between subsequent gestures (scrolls and pinches).</summary>
 </histogram>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index d60397d..7e5cff0 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -46,8 +46,6 @@
 octane,"bmeurer@chromium.org, mvstanton@chromium.org",
 oortonline,ulan@chromium.org,
 oortonline_tbmv2,ulan@chromium.org,
-page_cycler_v2.basic_oopif,nasko@chromium.org,
-page_cycler_v2_site_isolation.basic_oopif,nasko@chromium.org,
 performance_browser_tests,miu@chromium.org,
 power.idle_platform,,
 power.steady_state,,
diff --git a/tools/perf/benchmarks/oopif.py b/tools/perf/benchmarks/oopif.py
deleted file mode 100644
index 71b2090e..0000000
--- a/tools/perf/benchmarks/oopif.py
+++ /dev/null
@@ -1,81 +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.
-
-# TODO(rnephew): Migrate to loading benchmark harness.
-from core import perf_benchmark
-import page_sets
-
-from benchmarks import loading_metrics_category
-from telemetry import benchmark
-from telemetry import story
-from telemetry.page import cache_temperature
-from telemetry.web_perf import timeline_based_measurement
-
-class _OopifBase(perf_benchmark.PerfBenchmark):
-  options = {'pageset_repeat': 2}
-
-  def CreateCoreTimelineBasedMeasurementOptions(self):
-    tbm_options = timeline_based_measurement.Options()
-    loading_metrics_category.AugmentOptionsForLoadingMetrics(tbm_options)
-    return tbm_options
-
-  @classmethod
-  def ShouldDisable(cls, possible_browser):
-    # crbug.com/619254
-    if possible_browser.browser_type == 'reference':
-      return True
-
-    # crbug.com/616781
-    if (cls.IsSvelte(possible_browser) or
-        possible_browser.platform.GetDeviceTypeName() == 'Nexus 5X' or
-        possible_browser.platform.GetDeviceTypeName() == 'AOSP on BullHead'):
-      return True
-
-    return False
-
-
-@benchmark.Owner(emails=['nasko@chromium.org'])
-class PageCyclerV2BasicOopifIsolated(_OopifBase):
-  """ A benchmark measuring performance of out-of-process iframes. """
-  page_set = page_sets.OopifBasicPageSet
-  SUPPORTED_PLATFORMS = [story.expectations.ALL_DESKTOP]
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2_site_isolation.basic_oopif'
-
-  def SetExtraBrowserOptions(self, options):
-    options.AppendExtraBrowserArgs(['--site-per-process'])
-
-  def CreateStorySet(self, options):
-    return page_sets.OopifBasicPageSet(cache_temperatures=[
-          cache_temperature.PCV1_COLD, cache_temperature.PCV1_WARM])
-
-  def GetExpectations(self):
-    class StoryExpectations(story.expectations.StoryExpectations):
-      def SetExpectations(self):
-        pass
-    return StoryExpectations()
-
-
-@benchmark.Owner(emails=['nasko@chromium.org'])
-class PageCyclerV2BasicOopif(_OopifBase):
-  """ A benchmark measuring performance of the out-of-process iframes page
-  set, without running in out-of-process iframes mode.. """
-  page_set = page_sets.OopifBasicPageSet
-  SUPPORTED_PLATFORMS = [story.expectations.ALL_DESKTOP]
-
-  @classmethod
-  def Name(cls):
-    return 'page_cycler_v2.basic_oopif'
-
-  def CreateStorySet(self, options):
-    return page_sets.OopifBasicPageSet(cache_temperatures=[
-          cache_temperature.PCV1_COLD, cache_temperature.PCV1_WARM])
-
-  def GetExpectations(self):
-    class StoryExpectations(story.expectations.StoryExpectations):
-      def SetExpectations(self):
-        pass
-    return StoryExpectations()
diff --git a/tools/perf/page_sets/data/oopif_basic.json b/tools/perf/page_sets/data/oopif_basic.json
deleted file mode 100644
index 05307367..0000000
--- a/tools/perf/page_sets/data/oopif_basic.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-    "archives": {
-        "http://arstechnica.com/": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        },
-        "http://booking.com": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        },
-        "http://www.cnn.com": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        },
-        "http://www.ebay.com": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        },
-        "http://www.fifa.com/": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        },
-        "http://www.nationalgeographic.com/": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        },
-        "http://www.rei.com/": {
-            "DEFAULT": "oopif_basic_000.wpr"
-        }
-    },
-    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
-    "platform_specific": true
-}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/oopif_basic_000.wpr.sha1 b/tools/perf/page_sets/data/oopif_basic_000.wpr.sha1
deleted file mode 100644
index 8316337..0000000
--- a/tools/perf/page_sets/data/oopif_basic_000.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4403d9d9a4878d07df2d22b82c2c2939c007eb8b
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/simple_mobile_sites.json b/tools/perf/page_sets/data/simple_mobile_sites.json
index 529e3bf8..bc34d83 100644
--- a/tools/perf/page_sets/data/simple_mobile_sites.json
+++ b/tools/perf/page_sets/data/simple_mobile_sites.json
@@ -1,25 +1,25 @@
 {
     "archives": {
         "http://m.nytimes.com/": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         },
         "http://m.us.wsj.com/": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         },
         "http://www.apple.com/mac/": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         },
         "http://www.ebay.co.uk/": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         },
         "http://www.nyc.gov": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         },
         "https://www.flickr.com/": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         },
         "https://www.yahoo.com/": {
-            "DEFAULT": "simple_mobile_sites_002.wpr"
+            "DEFAULT": "simple_mobile_sites_002.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/simple_mobile_sites_002.wpr.sha1 b/tools/perf/page_sets/data/simple_mobile_sites_002.wpr.sha1
deleted file mode 100644
index a9a38d4..0000000
--- a/tools/perf/page_sets/data/simple_mobile_sites_002.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-74b7f3166ef59c887988b21a39ff722ec621b1d4
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/simple_mobile_sites_002.wprgo.sha1 b/tools/perf/page_sets/data/simple_mobile_sites_002.wprgo.sha1
new file mode 100644
index 0000000..0ff89ff
--- /dev/null
+++ b/tools/perf/page_sets/data/simple_mobile_sites_002.wprgo.sha1
@@ -0,0 +1 @@
+f485f5b4b805ddb196b69d4d14ce74e5868a9041
\ No newline at end of file
diff --git a/tools/perf/page_sets/oopif_basic_page_set.py b/tools/perf/page_sets/oopif_basic_page_set.py
deleted file mode 100644
index 6aa886e..0000000
--- a/tools/perf/page_sets/oopif_basic_page_set.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.page import cache_temperature as cache_temperature_module
-from telemetry.page import page
-from telemetry import story
-
-class OopifBasicPageSet(story.StorySet):
-  """ Basic set of pages used to measure performance of out-of-process
-  iframes.
-  """
-
-  def __init__(self, cache_temperatures=None):
-    super(OopifBasicPageSet, self).__init__(
-        archive_data_file='data/oopif_basic.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
-    if cache_temperatures is None:
-      cache_temperatures = [cache_temperature_module.ANY]
-
-    urls = [
-        'http://www.cnn.com',
-        'http://www.ebay.com',
-        'http://booking.com',
-        # Disabled because it causes flaky runs https://crbug.com/522870
-        #'http://www.rei.com/',
-        'http://www.fifa.com/',
-        # Disabled because it is flaky on Windows and Android
-        #'http://arstechnica.com/',
-        'http://www.nationalgeographic.com/',
-        # Cross-site heavy! Enable them once they render without crashing.
-        #'http://www.nba.com/',
-        #'http://www.phonearena.com/',
-        #'http://slickdeals.net/',
-        #'http://www.163.com/',
-    ]
-
-    for url in urls:
-      for temp in cache_temperatures:
-        self.AddStory(page.Page(url, self, cache_temperature=temp, name=url))
diff --git a/ui/aura/local/window_port_local.cc b/ui/aura/local/window_port_local.cc
index f44680e..fc60d26d 100644
--- a/ui/aura/local/window_port_local.cc
+++ b/ui/aura/local/window_port_local.cc
@@ -149,6 +149,10 @@
   return local_surface_id_;
 }
 
+viz::FrameSinkId WindowPortLocal::GetFrameSinkId() const {
+  return frame_sink_id_;
+}
+
 void WindowPortLocal::OnWindowAddedToRootWindow() {
   if (frame_sink_id_.is_valid())
     window_->layer()->GetCompositor()->AddFrameSink(frame_sink_id_);
diff --git a/ui/aura/local/window_port_local.h b/ui/aura/local/window_port_local.h
index cdaf21b1..68a29a2 100644
--- a/ui/aura/local/window_port_local.h
+++ b/ui/aura/local/window_port_local.h
@@ -48,6 +48,7 @@
   viz::SurfaceId GetSurfaceId() const override;
   void AllocateLocalSurfaceId() override;
   const viz::LocalSurfaceId& GetLocalSurfaceId() override;
+  viz::FrameSinkId GetFrameSinkId() const override;
   void OnWindowAddedToRootWindow() override;
   void OnWillRemoveWindowFromRootWindow() override;
   void OnEventTargetingPolicyChanged() override;
diff --git a/ui/aura/mus/DEPS b/ui/aura/mus/DEPS
index cf9f8fc..1f379237 100644
--- a/ui/aura/mus/DEPS
+++ b/ui/aura/mus/DEPS
@@ -13,9 +13,6 @@
   "+mojo/public/cpp/system/platform_handle.h",
   "+services/ui/common/accelerator_util.h",
   "+services/ui/common/task_runner_test_base.h",
-  "+services/ui/public/cpp/gpu",
-  "+services/ui/public/cpp/property_type_converters.h",
-  "+services/ui/public/cpp/raster_thread_helper.h",
-  "+services/ui/public/cpp/client_layer_tree_frame_sink.h",
+  "+services/ui/public",
   "+ui/gl/gl_bindings.h",
 ]
diff --git a/ui/aura/mus/hit_test_data_provider_aura.cc b/ui/aura/mus/hit_test_data_provider_aura.cc
index 078d113..db915077 100644
--- a/ui/aura/mus/hit_test_data_provider_aura.cc
+++ b/ui/aura/mus/hit_test_data_provider_aura.cc
@@ -5,24 +5,23 @@
 #include "ui/aura/mus/hit_test_data_provider_aura.h"
 
 #include "base/containers/adapters.h"
-#include "ui/aura/mus/window_port_mus.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
 
 namespace {
 
-viz::mojom::HitTestRegionPtr CreateHitTestRegion(
-    const aura::WindowPortMus* window_port,
-    uint32_t flags,
-    const gfx::Rect& rect) {
-  const ui::Layer* layer = window_port->window()->layer();
+viz::mojom::HitTestRegionPtr CreateHitTestRegion(const aura::Window* window,
+                                                 uint32_t flags,
+                                                 const gfx::Rect& rect) {
+  const ui::Layer* layer = window->layer();
   DCHECK(layer);
 
   auto hit_test_region = viz::mojom::HitTestRegion::New();
-  DCHECK(window_port->GetFrameSinkId().is_valid());
-  hit_test_region->frame_sink_id = window_port->GetFrameSinkId();
+  DCHECK(window->GetFrameSinkId().is_valid());
+  hit_test_region->frame_sink_id = window->GetFrameSinkId();
   if (layer->GetPrimarySurfaceInfo()) {
-    DCHECK(window_port->GetFrameSinkId() ==
+    DCHECK(window->GetFrameSinkId() ==
            layer->GetPrimarySurfaceInfo()->id().frame_sink_id());
     hit_test_region->local_surface_id =
         layer->GetPrimarySurfaceInfo()->id().local_surface_id();
@@ -83,7 +82,6 @@
       gfx::Rect rect_mouse(child->bounds());
       gfx::Rect rect_touch;
       bool touch_and_mouse_are_same = true;
-      const WindowPortMus* window_port = WindowPortMus::Get(child);
       uint32_t flags = child->layer()->GetPrimarySurfaceInfo()
                            ? viz::mojom::kHitTestChildSurface
                            : viz::mojom::kHitTestMine;
@@ -107,7 +105,7 @@
           if (rect.IsEmpty())
             continue;
           hit_test_region_list->regions.push_back(CreateHitTestRegion(
-              window_port,
+              child,
               flags | viz::mojom::kHitTestMouse | viz::mojom::kHitTestTouch,
               rect));
         }
@@ -115,7 +113,7 @@
         // The |child| has possibly same mouse and touch hit-test areas.
         if (!rect_mouse.IsEmpty()) {
           hit_test_region_list->regions.push_back(CreateHitTestRegion(
-              window_port,
+              child,
               flags | (touch_and_mouse_are_same ? (viz::mojom::kHitTestMouse |
                                                    viz::mojom::kHitTestTouch)
                                                 : viz::mojom::kHitTestMouse),
@@ -123,7 +121,7 @@
         }
         if (!touch_and_mouse_are_same && !rect_touch.IsEmpty()) {
           hit_test_region_list->regions.push_back(CreateHitTestRegion(
-              window_port, flags | viz::mojom::kHitTestTouch, rect_touch));
+              child, flags | viz::mojom::kHitTestTouch, rect_touch));
         }
       }
     }
diff --git a/ui/aura/mus/hit_test_data_provider_aura_unittest.cc b/ui/aura/mus/hit_test_data_provider_aura_unittest.cc
index b73c30f..5f8611fe 100644
--- a/ui/aura/mus/hit_test_data_provider_aura_unittest.cc
+++ b/ui/aura/mus/hit_test_data_provider_aura_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "components/viz/client/hit_test_data_provider.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/aura/mus/window_mus.h"
 #include "ui/aura/mus/window_port_mus.h"
 #include "ui/aura/test/aura_mus_test_base.h"
 #include "ui/aura/window.h"
@@ -113,6 +112,8 @@
 
  protected:
   const viz::HitTestDataProvider* hit_test_data_provider() const {
+    // TODO(varkha): Find a way to get the HitTestDataProvider without depending
+    // on WindowPortMus
     WindowPortMus* port = WindowPortMus::Get(root_.get());
     return port->local_layer_tree_frame_sink_->hit_test_data_provider();
   }
@@ -148,8 +149,7 @@
     EXPECT_EQ(region->flags, viz::mojom::kHitTestMine |
                                  viz::mojom::kHitTestMouse |
                                  viz::mojom::kHitTestTouch);
-    EXPECT_EQ(region->frame_sink_id,
-              WindowPortMus::Get(expected_order_1[i])->GetFrameSinkId());
+    EXPECT_EQ(region->frame_sink_id, expected_order_1[i]->GetFrameSinkId());
     EXPECT_EQ(region->rect.ToString(),
               expected_order_1[i]->bounds().ToString());
     i++;
@@ -168,8 +168,7 @@
     EXPECT_EQ(region->flags, viz::mojom::kHitTestMine |
                                  viz::mojom::kHitTestMouse |
                                  viz::mojom::kHitTestTouch);
-    EXPECT_EQ(region->frame_sink_id,
-              WindowPortMus::Get(expected_order_2[i])->GetFrameSinkId());
+    EXPECT_EQ(region->frame_sink_id, expected_order_2[i]->GetFrameSinkId());
     EXPECT_EQ(region->rect.ToString(),
               expected_order_2[i]->bounds().ToString());
     i++;
@@ -201,8 +200,7 @@
   ASSERT_EQ(hit_test_data->regions.size(), arraysize(expected_insets));
   int i = 0;
   for (const auto& region : hit_test_data->regions) {
-    EXPECT_EQ(region->frame_sink_id,
-              WindowPortMus::Get(expected_windows[i])->GetFrameSinkId());
+    EXPECT_EQ(region->frame_sink_id, expected_windows[i]->GetFrameSinkId());
     EXPECT_EQ(region->flags, expected_flags[i]);
     gfx::Rect expected_bounds = expected_windows[i]->bounds();
     expected_bounds.Inset(gfx::Insets(expected_insets[i]));
@@ -242,8 +240,7 @@
   ASSERT_EQ(hit_test_data->regions.size(), expected_bounds.size());
   int i = 0;
   for (const auto& region : hit_test_data->regions) {
-    EXPECT_EQ(region->frame_sink_id,
-              WindowPortMus::Get(expected_windows[i])->GetFrameSinkId());
+    EXPECT_EQ(region->frame_sink_id, expected_windows[i]->GetFrameSinkId());
     EXPECT_EQ(region->flags, expected_flags);
     EXPECT_EQ(region->rect.ToString(), expected_bounds[i].ToString());
     i++;
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 7b6223a3e..3b937f13 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -64,12 +64,6 @@
   return static_cast<WindowPortMus*>(WindowPort::Get(window));
 }
 
-viz::FrameSinkId WindowPortMus::GetFrameSinkId() const {
-  if (embed_frame_sink_id_.is_valid())
-    return embed_frame_sink_id_;
-  return viz::FrameSinkId(0, server_id());
-}
-
 void WindowPortMus::SetTextInputState(mojo::TextInputStatePtr state) {
   window_tree_client_->SetWindowTextInputState(this, std::move(state));
 }
@@ -134,6 +128,12 @@
   return layer_tree_frame_sink;
 }
 
+viz::FrameSinkId WindowPortMus::GetFrameSinkId() const {
+  if (embed_frame_sink_id_.is_valid())
+    return embed_frame_sink_id_;
+  return viz::FrameSinkId(0, server_id());
+}
+
 WindowPortMus::ServerChangeIdType WindowPortMus::ScheduleChange(
     const ServerChangeType type,
     const ServerChangeData& data) {
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index 47cc985..b0d5d62 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -66,10 +66,6 @@
     return primary_surface_info_;
   }
 
-  // Returns either the FrameSinkId set by window server or its server_id with
-  // the client id part 0.
-  viz::FrameSinkId GetFrameSinkId() const;
-
   void SetTextInputState(mojo::TextInputStatePtr state);
   void SetImeVisibility(bool visible, mojo::TextInputStatePtr state);
 
@@ -97,6 +93,11 @@
       scoped_refptr<viz::ContextProvider> context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
 
+  // WindowPort:
+  // Returns either the FrameSinkId set by window server or its server_id with
+  // the client id part 0.
+  viz::FrameSinkId GetFrameSinkId() const override;
+
  private:
   friend class WindowPortMusTestApi;
   friend class WindowTreeClient;
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index 07409d9..e618ea8 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -1026,6 +1026,10 @@
   return port_->GetLocalSurfaceId();
 }
 
+viz::FrameSinkId Window::GetFrameSinkId() const {
+  return port_->GetFrameSinkId();
+}
+
 void Window::OnPaintLayer(const ui::PaintContext& context) {
   Paint(context);
 }
diff --git a/ui/aura/window.h b/ui/aura/window.h
index 62db430..15009ff 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -328,6 +328,11 @@
   // Gets the current viz::LocalSurfaceId.
   const viz::LocalSurfaceId& GetLocalSurfaceId() const;
 
+  // Returns the FrameSinkId. In LOCAL mode, this returns a valid FrameSinkId
+  // only if a LayerTreeFrameSink has been created. In MUS mode, this always
+  // return a valid FrameSinkId.
+  viz::FrameSinkId GetFrameSinkId() const;
+
  protected:
   // Deletes (or removes if not owned by parent) all child windows. Intended for
   // use from the destructor.
diff --git a/ui/aura/window_port.h b/ui/aura/window_port.h
index 5ab4d6c..47a6d22 100644
--- a/ui/aura/window_port.h
+++ b/ui/aura/window_port.h
@@ -97,6 +97,9 @@
   // factor.
   virtual const viz::LocalSurfaceId& GetLocalSurfaceId() = 0;
 
+  // This can return invalid FrameSinkId.
+  virtual viz::FrameSinkId GetFrameSinkId() const = 0;
+
   virtual void OnWindowAddedToRootWindow() = 0;
   virtual void OnWillRemoveWindowFromRootWindow() = 0;
 
diff --git a/ui/aura/window_port_for_shutdown.cc b/ui/aura/window_port_for_shutdown.cc
index 087f47e1..73fd7a9 100644
--- a/ui/aura/window_port_for_shutdown.cc
+++ b/ui/aura/window_port_for_shutdown.cc
@@ -66,6 +66,10 @@
   return local_surface_id_;
 }
 
+viz::FrameSinkId WindowPortForShutdown::GetFrameSinkId() const {
+  return frame_sink_id_;
+}
+
 void WindowPortForShutdown::OnWindowAddedToRootWindow() {}
 
 void WindowPortForShutdown::OnWillRemoveWindowFromRootWindow() {}
diff --git a/ui/aura/window_port_for_shutdown.h b/ui/aura/window_port_for_shutdown.h
index 8c94640..0807e61a 100644
--- a/ui/aura/window_port_for_shutdown.h
+++ b/ui/aura/window_port_for_shutdown.h
@@ -41,12 +41,14 @@
   viz::SurfaceId GetSurfaceId() const override;
   void AllocateLocalSurfaceId() override;
   const viz::LocalSurfaceId& GetLocalSurfaceId() override;
+  viz::FrameSinkId GetFrameSinkId() const override;
   void OnWindowAddedToRootWindow() override;
   void OnWillRemoveWindowFromRootWindow() override;
   void OnEventTargetingPolicyChanged() override;
 
  private:
   viz::LocalSurfaceId local_surface_id_;
+  viz::FrameSinkId frame_sink_id_;
   DISALLOW_COPY_AND_ASSIGN(WindowPortForShutdown);
 };
 
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index da15115..11eca13 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -90,10 +90,8 @@
         base::FeatureList::IsEnabled(features::kColorCorrectRendering);
     if (base::mac::IsAtLeastOS10_12() && !color_correct_rendering_enabled)
       color_space = base::mac::GetSystemColorSpace();
-    gfx::ICCProfile icc_profile =
-        gfx::ICCProfile::FromCGColorSpace(color_space);
-    icc_profile.HistogramDisplay(display.id());
-    display.set_color_space(icc_profile.GetColorSpace());
+    display.set_color_space(
+        gfx::ICCProfile::FromCGColorSpace(color_space).GetColorSpace());
   }
   display.set_color_depth(NSBitsPerPixelFromDepth([screen depth]));
   display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth]));
diff --git a/ui/display/win/color_profile_reader.cc b/ui/display/win/color_profile_reader.cc
index 2a61591..d1220cfd 100644
--- a/ui/display/win/color_profile_reader.cc
+++ b/ui/display/win/color_profile_reader.cc
@@ -110,10 +110,9 @@
     if (profile_data.empty()) {
       display_id_to_color_space_map_[display_id] = default_color_space_;
     } else {
-      gfx::ICCProfile icc_profile =
-          gfx::ICCProfile::FromData(profile_data.data(), profile_data.size());
-      icc_profile.HistogramDisplay(display_id);
-      display_id_to_color_space_map_[display_id] = icc_profile.GetColorSpace();
+      display_id_to_color_space_map_[display_id] =
+          gfx::ICCProfile::FromData(profile_data.data(), profile_data.size())
+              .GetColorSpace();
     }
   }
 
diff --git a/ui/gfx/icc_profile.cc b/ui/gfx/icc_profile.cc
index 6067769..a982db5 100644
--- a/ui/gfx/icc_profile.cc
+++ b/ui/gfx/icc_profile.cc
@@ -5,12 +5,10 @@
 #include "ui/gfx/icc_profile.h"
 
 #include <list>
-#include <set>
 
 #include "base/command_line.h"
 #include "base/containers/mru_cache.h"
 #include "base/lazy_instance.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/synchronization/lock.h"
 #include "third_party/skia/include/core/SkColorSpaceXform.h"
 #include "third_party/skia/include/core/SkICC.h"
@@ -27,170 +25,32 @@
 const uint64_t ICCProfile::test_id_a2b_only_ = 6;
 const uint64_t ICCProfile::test_id_overshoot_ = 7;
 
-// A MRU cache of ICC profiles. The cache key is a uin64_t which a
-// gfx::ColorSpace may use to refer back to an ICC profile in the cache. The
-// data cached for each profile is the gfx::ICCProfile structure (which includes
-// the associated gfx::ColorSpace approximations and SkColorSpace structures)
-// and whether or not the ICC profile has been histogrammed.
-class ICCProfileCache {
- public:
-  // Allow keeping around a maximum of 16 cached ICC profiles. Beware that
-  // we will do a linear search thorugh currently-cached ICC profiles,
-  // when creating a new ICC profile.
-  static const size_t kMaxCachedICCProfiles = 16;
-
-  ICCProfileCache() : id_to_icc_profile_mru_(kMaxCachedICCProfiles) {}
-  ~ICCProfileCache() {}
-
-  // Add |icc_profile| to the cache. If |icc_profile| does not have an id set
-  // yet, assign an id to it.
-  void InsertAndSetIdIfNeeded(ICCProfile* icc_profile) {
-    base::AutoLock lock(lock_);
-
-    if (FindByIdUnderLock(icc_profile->id_, icc_profile))
-      return;
-
-    if (FindByDataUnderLock(icc_profile->data_.data(),
-                            icc_profile->data_.size(), icc_profile)) {
-      return;
-    }
-
-    if (!icc_profile->id_)
-      icc_profile->id_ = next_unused_id_++;
-
-    Entry entry;
-    entry.icc_profile = *icc_profile;
-    id_to_icc_profile_mru_.Put(icc_profile->id_, entry);
-  }
-
-  // We maintain UMA histograms of display ICC profiles. Only histogram a
-  // display once for each |display_id| (because we will re-read the same
-  // ICC profile repeatedly when reading other display profiles, which will
-  // skew samples). Return whether or not we have histogrammed this profile
-  // for |display_id|. Ensure that all future calls will return true for
-  // |display_id|.
-  bool GetAndSetNeedsHistogram(uint64_t display_id,
-                               const ICCProfile& icc_profile) {
-    base::AutoLock lock(lock_);
-
-    auto found = id_to_icc_profile_mru_.Get(icc_profile.id_);
-    if (found != id_to_icc_profile_mru_.end())
-      return false;
-
-    std::set<int64_t>& histogrammed_display_ids =
-        found->second.histogrammed_display_ids;
-    if (histogrammed_display_ids.count(display_id))
-      return false;
-
-    histogrammed_display_ids.insert(display_id);
-    return true;
-  }
-
-  // Move this ICC profile to the most recently used end of the cache,
-  // re-inserting if needed.
-  void TouchEntry(const ICCProfile& icc_profile) {
-    base::AutoLock lock(lock_);
-
-    if (!icc_profile.id_)
-      return;
-
-    // Look up the profile by id to move it to the front of the MRU.
-    auto found = id_to_icc_profile_mru_.Get(icc_profile.id_);
-    if (found != id_to_icc_profile_mru_.end())
-      return;
-
-    // If the entry was not found, insert it.
-    Entry entry;
-    entry.icc_profile = icc_profile;
-    id_to_icc_profile_mru_.Put(icc_profile.id_, entry);
-  }
-
-  // Look up an ICC profile in the cache by its data (to ensure that the same
-  // data gets the same id every time). On success, return true and populate
-  // |icc_profile| with the associated profile.
-  bool FindByData(const void* data, size_t size, ICCProfile* icc_profile) {
-    base::AutoLock lock(lock_);
-    return FindByDataUnderLock(data, size, icc_profile);
-  }
-
-  // Look up an ICC profile in the cache by its id. On success, return true and
-  // populate |icc_profile| with the associated profile.
-  bool FindById(uint64_t id, ICCProfile* icc_profile) {
-    base::AutoLock lock(lock_);
-    return FindByIdUnderLock(id, icc_profile);
-  }
-
- private:
-  struct Entry {
-    ICCProfile icc_profile;
-
-    // The set of display ids which have have caused this ICC profile to be
-    // recorded in UMA histograms. Only record an ICC profile once per display
-    // id (since the same profile will be re-read repeatedly, e.g, when displays
-    // are resized).
-    std::set<int64_t> histogrammed_display_ids;
-  };
-
-  // Body for FindById, executed when the cache lock is already held.
-  bool FindByIdUnderLock(uint64_t id, ICCProfile* icc_profile) {
-    lock_.AssertAcquired();
-    if (!id)
-      return false;
-
-    auto found = id_to_icc_profile_mru_.Get(id);
-    if (found == id_to_icc_profile_mru_.end())
-      return false;
-
-    *icc_profile = found->second.icc_profile;
-    return true;
-  }
-
-  // Body for FindByData, executed when the cache lock is already held.
-  bool FindByDataUnderLock(const void* data,
-                           size_t size,
-                           ICCProfile* icc_profile) {
-    lock_.AssertAcquired();
-    if (size == 0)
-      return false;
-
-    for (const auto& id_entry_pair : id_to_icc_profile_mru_) {
-      const ICCProfile& cached_profile = id_entry_pair.second.icc_profile;
-      const std::vector<char>& iter_data = cached_profile.data_;
-      if (iter_data.size() != size || memcmp(data, iter_data.data(), size))
-        continue;
-
-      *icc_profile = cached_profile;
-      id_to_icc_profile_mru_.Get(cached_profile.id_);
-      return true;
-    }
-    return false;
-  }
-
-  // Start from-ICC-data IDs at the end of the hard-coded test id list above.
-  uint64_t next_unused_id_ = 10;
-  base::MRUCache<uint64_t, Entry> id_to_icc_profile_mru_;
-
-  // Lock that must be held to access |id_to_icc_profile_mru_| and
-  // |next_unused_id_|.
-  base::Lock lock_;
-};
-
 namespace {
 
-static base::LazyInstance<ICCProfileCache>::DestructorAtExit g_cache =
+// Allow keeping around a maximum of 8 cached ICC profiles. Beware that
+// we will do a linear search thorugh currently-cached ICC profiles,
+// when creating a new ICC profile.
+const size_t kMaxCachedICCProfiles = 8;
+
+struct Cache {
+  Cache() : id_to_icc_profile_mru(kMaxCachedICCProfiles) {}
+  ~Cache() {}
+
+  // Start from-ICC-data IDs at the end of the hard-coded test id list above.
+  uint64_t next_unused_id = 10;
+  base::MRUCache<uint64_t, ICCProfile> id_to_icc_profile_mru;
+  base::Lock lock;
+};
+static base::LazyInstance<Cache>::DestructorAtExit g_cache =
     LAZY_INSTANCE_INITIALIZER;
 
-}  // namespace
-
-// static
-ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces(
-    const std::vector<char>& data,
-    gfx::ColorSpace* parametric_color_space,
-    float* parametric_tr_fn_max_error,
-    sk_sp<SkColorSpace>* useable_sk_color_space) {
+void ExtractColorSpaces(const std::vector<char>& data,
+                        gfx::ColorSpace* parametric_color_space,
+                        bool* parametric_color_space_is_accurate,
+                        sk_sp<SkColorSpace>* useable_sk_color_space) {
   // Initialize the output parameters as invalid.
   *parametric_color_space = gfx::ColorSpace();
-  *parametric_tr_fn_max_error = 0;
+  *parametric_color_space_is_accurate = false;
   *useable_sk_color_space = nullptr;
 
   // Parse the profile and attempt to create a SkColorSpaceXform out of it.
@@ -198,20 +58,20 @@
   sk_sp<SkICC> sk_icc = SkICC::Make(data.data(), data.size());
   if (!sk_icc) {
     DLOG(ERROR) << "Failed to parse ICC profile to SkICC.";
-    return kICCFailedToParse;
+    return;
   }
   sk_sp<SkColorSpace> sk_icc_color_space =
       SkColorSpace::MakeICC(data.data(), data.size());
   if (!sk_icc_color_space) {
     DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace.";
-    return kICCFailedToExtractSkColorSpace;
+    return;
   }
   std::unique_ptr<SkColorSpaceXform> sk_color_space_xform =
       SkColorSpaceXform::New(sk_srgb_color_space.get(),
                              sk_icc_color_space.get());
   if (!sk_color_space_xform) {
     DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform.";
-    return kICCFailedToCreateXform;
+    return;
   }
 
   // Because this SkColorSpace can be used to construct a transform, mark it
@@ -222,14 +82,15 @@
   // If our SkColorSpace representation is sRGB then return that.
   if (SkColorSpace::Equals(sk_srgb_color_space.get(),
                            sk_icc_color_space.get())) {
-    return kICCExtractedSRGBColorSpace;
+    *parametric_color_space_is_accurate = true;
+    return;
   }
 
   // A primary matrix is required for our parametric approximation.
   SkMatrix44 to_XYZD50_matrix;
   if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) {
     DLOG(ERROR) << "Failed to extract ICC profile primary matrix.";
-    return kICCFailedToExtractMatrix;
+    return;
   }
 
   // Try to directly extract a numerical transfer function.
@@ -237,7 +98,27 @@
   if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) {
     *parametric_color_space =
         gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, exact_tr_fn);
-    return kICCExtractedMatrixAndAnalyticTrFn;
+    *parametric_color_space_is_accurate = true;
+    return;
+  }
+
+  // If that fails, try to numerically approximate the transfer function.
+  SkColorSpaceTransferFn approx_tr_fn;
+  float approx_tr_fn_max_error = 0;
+  if (SkApproximateTransferFn(sk_icc, &approx_tr_fn_max_error, &approx_tr_fn)) {
+    const float kMaxError = 2.f / 256.f;
+    if (approx_tr_fn_max_error < kMaxError) {
+      *parametric_color_space =
+          gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn);
+      *parametric_color_space_is_accurate = true;
+      return;
+    } else {
+      DLOG(ERROR)
+          << "Failed to accurately approximate transfer function, error: "
+          << 256.f * approx_tr_fn_max_error << "/256";
+    }
+  } else {
+    DLOG(ERROR) << "Failed approximate transfer function.";
   }
 
   // If we fail to get a transfer function, use the sRGB transfer function,
@@ -246,32 +127,10 @@
   // SkColorSpace.
   *parametric_color_space = gfx::ColorSpace::CreateCustom(
       to_XYZD50_matrix, ColorSpace::TransferID::IEC61966_2_1);
-
-  // Attempt to fit a parametric transfer function to the table data in the
-  // profile.
-  SkColorSpaceTransferFn approx_tr_fn;
-  if (!SkApproximateTransferFn(sk_icc, parametric_tr_fn_max_error,
-                               &approx_tr_fn)) {
-    DLOG(ERROR) << "Failed approximate transfer function.";
-    return kICCFailedToConvergeToApproximateTrFn;
-  }
-
-  // If this converged, but has too high error, use the sRGB transfer function
-  // from above.
-  const float kMaxError = 2.f / 256.f;
-  if (*parametric_tr_fn_max_error >= kMaxError) {
-    DLOG(ERROR) << "Failed to accurately approximate transfer function, error: "
-                << 256.f * (*parametric_tr_fn_max_error) << "/256";
-    return kICCFailedToApproximateTrFnAccurately;
-  };
-
-  // If the error is sufficiently low, declare that the approximation is
-  // accurate.
-  *parametric_color_space =
-      gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn);
-  return kICCExtractedMatrixAndApproximatedTrFn;
 }
 
+}  // namespace
+
 ICCProfile::ICCProfile() = default;
 ICCProfile::ICCProfile(ICCProfile&& other) = default;
 ICCProfile::ICCProfile(const ICCProfile& other) = default;
@@ -288,15 +147,7 @@
 }
 
 bool ICCProfile::IsValid() const {
-  switch (analyze_result_) {
-    case kICCFailedToParse:
-    case kICCFailedToExtractSkColorSpace:
-    case kICCFailedToCreateXform:
-      return false;
-    default:
-      break;
-  }
-  return true;
+  return successfully_parsed_by_sk_icc_;
 }
 
 // static
@@ -308,14 +159,30 @@
 ICCProfile ICCProfile::FromDataWithId(const void* data,
                                       size_t size,
                                       uint64_t new_profile_id) {
-  ICCProfile icc_profile;
-
   if (!size)
-    return icc_profile;
+    return ICCProfile();
+
+  const char* data_as_char = reinterpret_cast<const char*>(data);
+  {
+    // Linearly search the cached ICC profiles to find one with the same data.
+    // If it exists, re-use its id and touch it in the cache.
+    Cache& cache = g_cache.Get();
+    base::AutoLock lock(cache.lock);
+    for (auto iter = cache.id_to_icc_profile_mru.begin();
+         iter != cache.id_to_icc_profile_mru.end(); ++iter) {
+      const std::vector<char>& iter_data = iter->second.data_;
+      if (iter_data.size() != size || memcmp(data, iter_data.data(), size))
+        continue;
+      auto found = cache.id_to_icc_profile_mru.Get(iter->second.id_);
+      return found->second;
+    }
+    if (!new_profile_id)
+      new_profile_id = cache.next_unused_id++;
+  }
 
   // Create a new cached id and add it to the cache.
+  ICCProfile icc_profile;
   icc_profile.id_ = new_profile_id;
-  const char* data_as_char = reinterpret_cast<const char*>(data);
   icc_profile.data_.insert(icc_profile.data_.begin(), data_as_char,
                            data_as_char + size);
   icc_profile.ComputeColorSpaceAndCache();
@@ -336,91 +203,93 @@
 }
 
 const ColorSpace& ICCProfile::GetColorSpace() const {
-  g_cache.Get().TouchEntry(*this);
+  // Move this ICC profile to the most recently used end of the cache,
+  // inserting if needed.
+  if (id_) {
+    Cache& cache = g_cache.Get();
+    base::AutoLock lock(cache.lock);
+    auto found = cache.id_to_icc_profile_mru.Get(id_);
+    if (found == cache.id_to_icc_profile_mru.end())
+      found = cache.id_to_icc_profile_mru.Put(id_, *this);
+  }
   return color_space_;
 }
 
 const ColorSpace& ICCProfile::GetParametricColorSpace() const {
-  g_cache.Get().TouchEntry(*this);
+  // Move this ICC profile to the most recently used end of the cache,
+  // inserting if needed.
+  if (id_) {
+    Cache& cache = g_cache.Get();
+    base::AutoLock lock(cache.lock);
+    auto found = cache.id_to_icc_profile_mru.Get(id_);
+    if (found == cache.id_to_icc_profile_mru.end())
+      found = cache.id_to_icc_profile_mru.Put(id_, *this);
+  }
   return parametric_color_space_;
 }
 
 // static
 bool ICCProfile::FromId(uint64_t id,
                         ICCProfile* icc_profile) {
-  return g_cache.Get().FindById(id, icc_profile);
+  if (!id)
+    return false;
+
+  Cache& cache = g_cache.Get();
+  base::AutoLock lock(cache.lock);
+
+  auto found = cache.id_to_icc_profile_mru.Get(id);
+  if (found == cache.id_to_icc_profile_mru.end())
+    return false;
+
+  *icc_profile = found->second;
+  return true;
 }
 
 void ICCProfile::ComputeColorSpaceAndCache() {
-  // Early out for empty entries.
-  if (data_.empty())
+  if (!id_)
     return;
 
-  // If this id already exists in the cache, copy |this| from the cache entry.
-  if (g_cache.Get().FindById(id_, this))
-    return;
-
-  // If this data already exists in the cache, copy |this| from the cache entry.
-  if (g_cache.Get().FindByData(data_.data(), data_.size(), this))
-    return;
+  // If this already exists in the cache, just update its |color_space_|.
+  {
+    Cache& cache = g_cache.Get();
+    base::AutoLock lock(cache.lock);
+    auto found = cache.id_to_icc_profile_mru.Get(id_);
+    if (found != cache.id_to_icc_profile_mru.end()) {
+      color_space_ = found->second.color_space_;
+      parametric_color_space_ = found->second.parametric_color_space_;
+      successfully_parsed_by_sk_icc_ =
+          found->second.successfully_parsed_by_sk_icc_;
+      return;
+    }
+  }
 
   // Parse the ICC profile
   sk_sp<SkColorSpace> useable_sk_color_space;
-  analyze_result_ =
-      ExtractColorSpaces(data_, &parametric_color_space_,
-                         &parametric_tr_fn_error_, &useable_sk_color_space);
-  switch (analyze_result_) {
-    case kICCExtractedSRGBColorSpace:
-    case kICCExtractedMatrixAndAnalyticTrFn:
-    case kICCExtractedMatrixAndApproximatedTrFn:
-      // Successfully and accurately extracted color space.
-      parametric_color_space_.icc_profile_id_ = id_;
-      color_space_ = parametric_color_space_;
-      break;
-    case kICCFailedToExtractRawTrFn:
-    case kICCFailedToExtractMatrix:
-    case kICCFailedToConvergeToApproximateTrFn:
-    case kICCFailedToApproximateTrFnAccurately:
-      // Successfully but extracted a color space, but it isn't accurate enough.
-      color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED,
-                                ColorSpace::TransferID::ICC_BASED);
-      color_space_.icc_profile_id_ = id_;
-      color_space_.icc_profile_sk_color_space_ = useable_sk_color_space;
-      break;
-    case kICCFailedToParse:
-    case kICCFailedToExtractSkColorSpace:
-    case kICCFailedToCreateXform:
-      // Can't even use this color space as a LUT.
-      DCHECK(!parametric_color_space_.IsValid());
-      color_space_ = parametric_color_space_;
-      break;
+  bool parametric_color_space_is_accurate = false;
+  ExtractColorSpaces(data_, &parametric_color_space_,
+                     &parametric_color_space_is_accurate,
+                     &useable_sk_color_space);
+  if (parametric_color_space_is_accurate) {
+    successfully_parsed_by_sk_icc_ = true;
+    parametric_color_space_.icc_profile_id_ = id_;
+    color_space_ = parametric_color_space_;
+  } else if (useable_sk_color_space) {
+    successfully_parsed_by_sk_icc_ = true;
+    color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED,
+                              ColorSpace::TransferID::ICC_BASED);
+    color_space_.icc_profile_id_ = id_;
+    color_space_.icc_profile_sk_color_space_ = useable_sk_color_space;
+  } else {
+    successfully_parsed_by_sk_icc_ = false;
+    DCHECK(!color_space_.IsValid());
+    color_space_ = parametric_color_space_;
   }
 
   // Add to the cache.
-  g_cache.Get().InsertAndSetIdIfNeeded(this);
-}
-
-void ICCProfile::HistogramDisplay(int64_t display_id) const {
-  if (g_cache.Get().GetAndSetNeedsHistogram(display_id, *this))
-    return;
-
-  UMA_HISTOGRAM_ENUMERATION("Blink.ColorSpace.Destination.ICCResult",
-                            analyze_result_, kICCProfileAnalyzeLast);
-
-  // Add histograms for numerical approximation.
-  bool nonlinear_fit_converged =
-      analyze_result_ == kICCExtractedMatrixAndApproximatedTrFn ||
-      analyze_result_ == kICCFailedToApproximateTrFnAccurately;
-  bool nonlinear_fit_did_not_converge =
-      analyze_result_ == kICCFailedToConvergeToApproximateTrFn;
-  if (nonlinear_fit_converged || nonlinear_fit_did_not_converge) {
-    UMA_HISTOGRAM_BOOLEAN("Blink.ColorSpace.Destination.NonlinearFitConverged",
-                          nonlinear_fit_converged);
-  }
-  if (nonlinear_fit_converged) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Blink.ColorSpace.Destination.NonlinearFitError",
-        static_cast<int>(parametric_tr_fn_error_ * 255), 0, 127, 16);
+  {
+    Cache& cache = g_cache.Get();
+    base::AutoLock lock(cache.lock);
+    cache.id_to_icc_profile_mru.Put(id_, *this);
   }
 }
 
diff --git a/ui/gfx/icc_profile.h b/ui/gfx/icc_profile.h
index 4049df8..03170ad 100644
--- a/ui/gfx/icc_profile.h
+++ b/ui/gfx/icc_profile.h
@@ -23,8 +23,6 @@
 
 namespace gfx {
 
-class ICCProfileCache;
-
 // Used to represent a full ICC profile, usually retrieved from a monitor. It
 // can be lossily compressed into a ColorSpace object. This structure should
 // only be sent from higher-privilege processes to lower-privilege processes,
@@ -70,10 +68,6 @@
 
   const std::vector<char>& GetData() const;
 
-  // Histogram how we this was approximated by a gfx::ColorSpace. Only
-  // histogram a given profile once per display.
-  void HistogramDisplay(int64_t display_id) const;
-
 #if defined(OS_WIN)
   // This will read monitor ICC profiles from disk and cache the results for the
   // other functions to read. This should not be called on the UI or IO thread.
@@ -82,22 +76,6 @@
 #endif
 
  private:
-  // This must match ICCProfileAnalyzeResult enum in histograms.xml.
-  enum AnalyzeResult {
-    kICCExtractedMatrixAndAnalyticTrFn = 0,
-    kICCExtractedMatrixAndApproximatedTrFn = 1,
-    kICCFailedToConvergeToApproximateTrFn = 2,
-    kICCFailedToExtractRawTrFn = 3,
-    kICCFailedToExtractMatrix = 4,
-    kICCFailedToParse = 5,
-    kICCFailedToExtractSkColorSpace = 6,
-    kICCFailedToCreateXform = 7,
-    kICCFailedToApproximateTrFnAccurately = 8,
-    kICCExtractedSRGBColorSpace = 9,
-    kICCProfileAnalyzeLast = kICCExtractedSRGBColorSpace,
-  };
-
-  friend class ICCProfileCache;
   friend ICCProfile ICCProfileForTestingAdobeRGB();
   friend ICCProfile ICCProfileForTestingColorSpin();
   friend ICCProfile ICCProfileForTestingGenericRGB();
@@ -125,12 +103,6 @@
                                    size_t size,
                                    uint64_t id);
 
-  static AnalyzeResult ExtractColorSpaces(
-      const std::vector<char>& data,
-      gfx::ColorSpace* parametric_color_space,
-      float* parametric_tr_fn_max_error,
-      sk_sp<SkColorSpace>* useable_sk_color_space);
-
   void ComputeColorSpaceAndCache();
 
   // This globally identifies this ICC profile. It is used to look up this ICC
@@ -139,9 +111,6 @@
   uint64_t id_ = 0;
   std::vector<char> data_;
 
-  // The result of attepting to extract a color space from the color profile.
-  AnalyzeResult analyze_result_ = kICCFailedToParse;
-
   // |color_space| always links back to this ICC profile, and its SkColorSpace
   // is always equal to the SkColorSpace created from this ICCProfile.
   gfx::ColorSpace color_space_;
@@ -150,10 +119,8 @@
   // is accurate, and its SkColorSpace will always be parametrically created.
   gfx::ColorSpace parametric_color_space_;
 
-  // The L-infinity error of the parametric color space fit. This is undefined
-  // unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or
-  // kICCExtractedMatrixAndApproximatedTrFn.
-  float parametric_tr_fn_error_ = -1;
+  // This is set to true if SkICC successfully parsed this profile.
+  bool successfully_parsed_by_sk_icc_ = false;
 
   FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, BT709toSRGBICC);
   FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace);
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 05f649d..732e2d52 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -16,8 +16,6 @@
     "common/drm_util.h",
     "common/scoped_drm_types.cc",
     "common/scoped_drm_types.h",
-    "cursor_proxy_mojo.cc",
-    "cursor_proxy_mojo.h",
     "gpu/crtc_controller.cc",
     "gpu/crtc_controller.h",
     "gpu/drm_buffer.cc",
@@ -103,8 +101,10 @@
     "host/drm_window_host_manager.h",
     "host/gpu_thread_adapter.h",
     "host/gpu_thread_observer.h",
-    "mus_thread_proxy.cc",
-    "mus_thread_proxy.h",
+    "host/host_cursor_proxy.cc",
+    "host/host_cursor_proxy.h",
+    "host/host_drm_device.cc",
+    "host/host_drm_device.h",
     "ozone_platform_gbm.cc",
     "ozone_platform_gbm.h",
   ]
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index ad50d98..f8d5daa 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -315,6 +315,14 @@
                                        correction_matrix);
 }
 
+void DrmThread::StartDrmDevice(StartDrmDeviceCallback callback) {
+  // We currently assume that |Init| always succeeds so return true to indicate
+  // when the DRM thread has completed launching.  In particular, the invocation
+  // of the callback in the client triggers the invocation of DRM thread
+  // readiness observers.
+  std::move(callback).Run(true);
+}
+
 // DrmThread requires a BindingSet instead of a simple Binding because it will
 // be used from multiple threads in multiple processes.
 void DrmThread::AddBindingCursorDevice(
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index c8e0cdc..973409b 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -64,7 +64,8 @@
 
   void Start();
 
-  // Must be called on the DRM thread.
+  // Must be called on the DRM thread. All methods for use from the GPU thread.
+  // DrmThreadProxy (on GPU)thread) is the client for these methods.
   void CreateBuffer(gfx::AcceleratedWidget widget,
                     const gfx::Size& size,
                     gfx::BufferFormat format,
@@ -76,9 +77,12 @@
                            std::vector<base::ScopedFD>&& fds,
                            const std::vector<gfx::NativePixmapPlane>& planes,
                            scoped_refptr<GbmBuffer>* buffer);
-
   void GetScanoutFormats(gfx::AcceleratedWidget widget,
                          std::vector<gfx::BufferFormat>* scanout_formats);
+  void AddBindingCursorDevice(ozone::mojom::DeviceCursorRequest request);
+  void AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request);
+
+  // DrmWindowProxy (on GPU thread) is the client for these methods.
   void SchedulePageFlip(gfx::AcceleratedWidget widget,
                         const std::vector<OverlayPlane>& planes,
                         SwapCompletionOnceCallback callback);
@@ -86,37 +90,27 @@
       gfx::AcceleratedWidget widget,
       const gfx::VSyncProvider::UpdateVSyncCallback& callback);
 
+  // ozone::mojom::DrmDevice
+  void StartDrmDevice(StartDrmDeviceCallback callback) override;
   void CreateWindow(const gfx::AcceleratedWidget& widget) override;
   void DestroyWindow(const gfx::AcceleratedWidget& widget) override;
   void SetWindowBounds(const gfx::AcceleratedWidget& widget,
                        const gfx::Rect& bounds) override;
-  void SetCursor(const gfx::AcceleratedWidget& widget,
-                 const std::vector<SkBitmap>& bitmaps,
-                 const gfx::Point& location,
-                 int32_t frame_delay_ms) override;
-  void MoveCursor(const gfx::AcceleratedWidget& widget,
-                  const gfx::Point& location) override;
-  void CheckOverlayCapabilities(
-      const gfx::AcceleratedWidget& widget,
-      const OverlaySurfaceCandidateList& overlays,
-      base::OnceCallback<void(const gfx::AcceleratedWidget&,
-                              const OverlaySurfaceCandidateList&,
-                              const OverlayStatusList&)> callback) override;
+  void TakeDisplayControl(base::OnceCallback<void(bool)> callback) override;
+  void RelinquishDisplayControl(
+      base::OnceCallback<void(bool)> callback) override;
   void RefreshNativeDisplays(
       base::OnceCallback<void(MovableDisplaySnapshots)> callback) override;
+  void AddGraphicsDevice(const base::FilePath& path, base::File file) override;
+  void RemoveGraphicsDevice(const base::FilePath& path) override;
+  void DisableNativeDisplay(
+      int64_t id,
+      base::OnceCallback<void(int64_t, bool)> callback) override;
   void ConfigureNativeDisplay(
       int64_t id,
       std::unique_ptr<display::DisplayMode> mode,
       const gfx::Point& origin,
       base::OnceCallback<void(int64_t, bool)> callback) override;
-  void DisableNativeDisplay(
-      int64_t id,
-      base::OnceCallback<void(int64_t, bool)> callback) override;
-  void TakeDisplayControl(base::OnceCallback<void(bool)> callback) override;
-  void RelinquishDisplayControl(
-      base::OnceCallback<void(bool)> callback) override;
-  void AddGraphicsDevice(const base::FilePath& path, base::File file) override;
-  void RemoveGraphicsDevice(const base::FilePath& path) override;
   void GetHDCPState(int64_t display_id,
                     base::OnceCallback<void(int64_t, bool, display::HDCPState)>
                         callback) override;
@@ -128,16 +122,24 @@
       const std::vector<display::GammaRampRGBEntry>& degamma_lut,
       const std::vector<display::GammaRampRGBEntry>& gamma_lut,
       const std::vector<float>& correction_matrix) override;
+  void CheckOverlayCapabilities(
+      const gfx::AcceleratedWidget& widget,
+      const OverlaySurfaceCandidateList& overlays,
+      base::OnceCallback<void(const gfx::AcceleratedWidget&,
+                              const OverlaySurfaceCandidateList&,
+                              const OverlayStatusList&)> callback) override;
+
+  // ozone::mojom::DeviceCursor
+  void SetCursor(const gfx::AcceleratedWidget& widget,
+                 const std::vector<SkBitmap>& bitmaps,
+                 const gfx::Point& location,
+                 int32_t frame_delay_ms) override;
+  void MoveCursor(const gfx::AcceleratedWidget& widget,
+                  const gfx::Point& location) override;
 
   // base::Thread:
   void Init() override;
 
-  // Mojo support for DeviceCursorRequest.
-  void AddBindingCursorDevice(ozone::mojom::DeviceCursorRequest request);
-
-  // Mojo support for DrmDevice requests.
-  void AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request);
-
  private:
   std::unique_ptr<DrmDeviceManager> device_manager_;
   std::unique_ptr<ScanoutBufferGenerator> buffer_generator_;
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc b/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
index c3814f1..8d15f12 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
@@ -22,6 +22,10 @@
   messaging_proxy->SetDrmThread(&drm_thread_);
 }
 
+void DrmThreadProxy::StartDrmThread() {
+  drm_thread_.Start();
+}
+
 std::unique_ptr<DrmWindowProxy> DrmThreadProxy::CreateDrmWindowProxy(
     gfx::AcceleratedWidget widget) {
   return base::MakeUnique<DrmWindowProxy>(widget, &drm_thread_);
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_proxy.h b/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
index af59604..c8a0b4b 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
@@ -27,6 +27,8 @@
 
   void BindThreadIntoMessagingProxy(InterThreadMessagingProxy* messaging_proxy);
 
+  void StartDrmThread();
+
   std::unique_ptr<DrmWindowProxy> CreateDrmWindowProxy(
       gfx::AcceleratedWidget widget);
 
diff --git a/ui/ozone/platform/drm/cursor_proxy_mojo.cc b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
similarity index 76%
rename from ui/ozone/platform/drm/cursor_proxy_mojo.cc
rename to ui/ozone/platform/drm/host/host_cursor_proxy.cc
index c48ddca..ba5baeec 100644
--- a/ui/ozone/platform/drm/cursor_proxy_mojo.cc
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/ozone/platform/drm/cursor_proxy_mojo.h"
+#include "ui/ozone/platform/drm/host/host_cursor_proxy.h"
 
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
@@ -10,15 +10,15 @@
 namespace ui {
 
 // We assume that this is invoked only on the UI thread.
-CursorProxyMojo::CursorProxyMojo(service_manager::Connector* connector)
+HostCursorProxy::HostCursorProxy(service_manager::Connector* connector)
     : connector_(connector->Clone()) {
   ui_thread_ref_ = base::PlatformThread::CurrentRef();
   connector->BindInterface(ui::mojom::kServiceName, &main_cursor_ptr_);
 }
 
-CursorProxyMojo::~CursorProxyMojo() {}
+HostCursorProxy::~HostCursorProxy() {}
 
-void CursorProxyMojo::CursorSet(gfx::AcceleratedWidget widget,
+void HostCursorProxy::CursorSet(gfx::AcceleratedWidget widget,
                                 const std::vector<SkBitmap>& bitmaps,
                                 const gfx::Point& location,
                                 int frame_delay_ms) {
@@ -30,7 +30,7 @@
   }
 }
 
-void CursorProxyMojo::Move(gfx::AcceleratedWidget widget,
+void HostCursorProxy::Move(gfx::AcceleratedWidget widget,
                            const gfx::Point& location) {
   InitializeOnEvdevIfNecessary();
   if (ui_thread_ref_ == base::PlatformThread::CurrentRef()) {
@@ -40,12 +40,12 @@
   }
 }
 
-// Evdev runs this method on starting. But if a CursorProxyMojo is created long
+// Evdev runs this method on starting. But if a HostCursorProxy is created long
 // after Evdev has started (e.g. if the Viz process crashes (and the
-// |CursorProxyMojo| self-destructs and then a new |CursorProxyMojo| is built
+// |HostCursorProxy| self-destructs and then a new |HostCursorProxy| is built
 // when the GpuThread/DrmThread pair are once again running), we need to run it
 // on cursor motions.
-void CursorProxyMojo::InitializeOnEvdevIfNecessary() {
+void HostCursorProxy::InitializeOnEvdevIfNecessary() {
   if (ui_thread_ref_ != base::PlatformThread::CurrentRef()) {
     connector_->BindInterface(ui::mojom::kServiceName, &evdev_cursor_ptr_);
   }
diff --git a/ui/ozone/platform/drm/cursor_proxy_mojo.h b/ui/ozone/platform/drm/host/host_cursor_proxy.h
similarity index 76%
rename from ui/ozone/platform/drm/cursor_proxy_mojo.h
rename to ui/ozone/platform/drm/host/host_cursor_proxy.h
index e1bceff..42feece 100644
--- a/ui/ozone/platform/drm/cursor_proxy_mojo.h
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.h
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_OZONE_PLATFORM_DRM_CURSOR_PROXY_MOJO_H_
-#define UI_OZONE_PLATFORM_DRM_CURSOR_PROXY_MOJO_H_
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_
 
 #include "ui/gfx/native_widget_types.h"
-#include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h"
 #include "ui/ozone/platform/drm/host/drm_cursor.h"
 #include "ui/ozone/public/interfaces/device_cursor.mojom.h"
 
@@ -21,10 +20,10 @@
 // pointer control via Mojo-style IPC. This code runs only in the mus-ws (i.e.
 // it's the client) and sends mouse pointer control messages to a less
 // priviledged process.
-class CursorProxyMojo : public DrmCursorProxy {
+class HostCursorProxy : public DrmCursorProxy {
  public:
-  explicit CursorProxyMojo(service_manager::Connector* connector);
-  ~CursorProxyMojo() override;
+  explicit HostCursorProxy(service_manager::Connector* connector);
+  ~HostCursorProxy() override;
 
  private:
   // DrmCursorProxy.
@@ -42,9 +41,9 @@
   ui::ozone::mojom::DeviceCursorPtr evdev_cursor_ptr_;
 
   base::PlatformThreadRef ui_thread_ref_;
-  DISALLOW_COPY_AND_ASSIGN(CursorProxyMojo);
+  DISALLOW_COPY_AND_ASSIGN(HostCursorProxy);
 };
 
 }  // namespace ui
 
-#endif  // UI_OZONE_PLATFORM_DRM_CURSOR_PROXY_MOJO_H_
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc
new file mode 100644
index 0000000..628b3c2
--- /dev/null
+++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -0,0 +1,348 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/host_drm_device.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/ui/public/interfaces/constants.mojom.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
+#include "ui/ozone/platform/drm/host/host_cursor_proxy.h"
+
+namespace ui {
+
+HostDrmDevice::HostDrmDevice(DrmCursor* cursor,
+                             service_manager::Connector* connector)
+    : cursor_(cursor), connector_(connector), weak_ptr_factory_(this) {
+  // Bind the viz process pointer here.
+  // TODO(rjkroege): Reconnect on error as that would indicate that the Viz
+  // process has failed.
+  connector->BindInterface(ui::mojom::kServiceName, &drm_device_ptr_);
+}
+
+HostDrmDevice::~HostDrmDevice() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  for (GpuThreadObserver& observer : gpu_thread_observers_)
+    observer.OnGpuThreadRetired();
+}
+
+void HostDrmDevice::AsyncStartDrmDevice() {
+  auto callback = base::BindOnce(&HostDrmDevice::OnDrmServiceStartedCallback,
+                                 weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->StartDrmDevice(std::move(callback));
+}
+
+void HostDrmDevice::BlockingStartDrmDevice() {
+  // Wait until startup related tasks posted to this thread that must precede
+  // blocking on
+  base::RunLoop().RunUntilIdle();
+
+  bool success;
+  drm_device_ptr_->StartDrmDevice(&success);
+  CHECK(success)
+      << "drm thread failed to successfully start in single process mode.";
+  if (!connected_)
+    OnDrmServiceStartedCallback(true);
+  return;
+}
+
+void HostDrmDevice::OnDrmServiceStartedCallback(bool success) {
+  // This can be called multiple times in the course of single-threaded startup.
+  if (connected_)
+    return;
+  if (success == true) {
+    connected_ = true;
+    RunObservers();
+  }
+  // TODO(rjkroege): Handle failure of launching a viz process.
+}
+
+void HostDrmDevice::ProvideManagers(DrmDisplayHostManager* display_manager,
+                                    DrmOverlayManager* overlay_manager) {
+  display_manager_ = display_manager;
+  overlay_manager_ = overlay_manager;
+}
+
+void HostDrmDevice::RunObservers() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  for (GpuThreadObserver& observer : gpu_thread_observers_) {
+    observer.OnGpuProcessLaunched();
+    observer.OnGpuThreadReady();
+  }
+
+  // The cursor is special since it will process input events on the IO thread
+  // and can by-pass the UI thread. This means that we need to special case it
+  // and notify it after all other observers/handlers are notified.
+  cursor_->SetDrmCursorProxy(base::MakeUnique<HostCursorProxy>(connector_));
+
+  // TODO(rjkroege): Call ResetDrmCursorProxy when the mojo connection to the
+  // DRM thread is broken.
+}
+
+void HostDrmDevice::AddGpuThreadObserver(GpuThreadObserver* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  gpu_thread_observers_.AddObserver(observer);
+  if (IsConnected()) {
+    observer->OnGpuProcessLaunched();
+    observer->OnGpuThreadReady();
+  }
+}
+
+void HostDrmDevice::RemoveGpuThreadObserver(GpuThreadObserver* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  gpu_thread_observers_.RemoveObserver(observer);
+}
+
+bool HostDrmDevice::IsConnected() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+
+  // TODO(rjkroege): Need to set to connected_ to false when we lose the Viz
+  // process connection.
+  return connected_;
+}
+
+// Services needed for DrmDisplayHostMananger.
+void HostDrmDevice::RegisterHandlerForDrmDisplayHostManager(
+    DrmDisplayHostManager* handler) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_ = handler;
+}
+
+void HostDrmDevice::UnRegisterHandlerForDrmDisplayHostManager() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_ = nullptr;
+}
+
+bool HostDrmDevice::GpuCreateWindow(gfx::AcceleratedWidget widget) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+
+  drm_device_ptr_->CreateWindow(widget);
+  return true;
+}
+
+bool HostDrmDevice::GpuDestroyWindow(gfx::AcceleratedWidget widget) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+
+  drm_device_ptr_->DestroyWindow(widget);
+  return true;
+}
+
+bool HostDrmDevice::GpuWindowBoundsChanged(gfx::AcceleratedWidget widget,
+                                           const gfx::Rect& bounds) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+
+  drm_device_ptr_->SetWindowBounds(widget, bounds);
+  return true;
+}
+
+// Services needed for DrmOverlayManager.
+void HostDrmDevice::RegisterHandlerForDrmOverlayManager(
+    DrmOverlayManager* handler) {
+  // TODO(rjkroege): Permit overlay manager to run in viz when the display
+  // compositor runs in viz.
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  overlay_manager_ = handler;
+}
+
+void HostDrmDevice::UnRegisterHandlerForDrmOverlayManager() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  overlay_manager_ = nullptr;
+}
+
+bool HostDrmDevice::GpuCheckOverlayCapabilities(
+    gfx::AcceleratedWidget widget,
+    const OverlaySurfaceCandidateList& overlays) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+
+  auto callback =
+      base::BindOnce(&HostDrmDevice::GpuCheckOverlayCapabilitiesCallback,
+                     weak_ptr_factory_.GetWeakPtr());
+
+  drm_device_ptr_->CheckOverlayCapabilities(widget, overlays,
+                                            std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuRefreshNativeDisplays() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  auto callback =
+      base::BindOnce(&HostDrmDevice::GpuRefreshNativeDisplaysCallback,
+                     weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->RefreshNativeDisplays(std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuConfigureNativeDisplay(int64_t id,
+                                              const DisplayMode_Params& pmode,
+                                              const gfx::Point& origin) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+
+  // TODO(rjkroege): Remove the use of mode here.
+  auto mode = CreateDisplayModeFromParams(pmode);
+  auto callback =
+      base::BindOnce(&HostDrmDevice::GpuConfigureNativeDisplayCallback,
+                     weak_ptr_factory_.GetWeakPtr());
+
+  drm_device_ptr_->ConfigureNativeDisplay(id, std::move(mode), origin,
+                                          std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuDisableNativeDisplay(int64_t id) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  auto callback =
+      base::BindOnce(&HostDrmDevice::GpuDisableNativeDisplayCallback,
+                     weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->DisableNativeDisplay(id, std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuTakeDisplayControl() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  auto callback = base::BindOnce(&HostDrmDevice::GpuTakeDisplayControlCallback,
+                                 weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->TakeDisplayControl(std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuRelinquishDisplayControl() {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  auto callback =
+      base::BindOnce(&HostDrmDevice::GpuRelinquishDisplayControlCallback,
+                     weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->TakeDisplayControl(std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuAddGraphicsDevice(const base::FilePath& path,
+                                         base::ScopedFD fd) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  base::File file(fd.release());
+  drm_device_ptr_->AddGraphicsDevice(path, std::move(file));
+  return true;
+}
+
+bool HostDrmDevice::GpuRemoveGraphicsDevice(const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  drm_device_ptr_->RemoveGraphicsDevice(std::move(path));
+  return true;
+}
+
+bool HostDrmDevice::GpuGetHDCPState(int64_t display_id) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  auto callback = base::BindOnce(&HostDrmDevice::GpuGetHDCPStateCallback,
+                                 weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->GetHDCPState(display_id, std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuSetHDCPState(int64_t display_id,
+                                    display::HDCPState state) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+  auto callback = base::BindOnce(&HostDrmDevice::GpuSetHDCPStateCallback,
+                                 weak_ptr_factory_.GetWeakPtr());
+  drm_device_ptr_->SetHDCPState(display_id, state, std::move(callback));
+  return true;
+}
+
+bool HostDrmDevice::GpuSetColorCorrection(
+    int64_t id,
+    const std::vector<display::GammaRampRGBEntry>& degamma_lut,
+    const std::vector<display::GammaRampRGBEntry>& gamma_lut,
+    const std::vector<float>& correction_matrix) {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  if (!IsConnected())
+    return false;
+
+  drm_device_ptr_->SetColorCorrection(id, degamma_lut, gamma_lut,
+                                      correction_matrix);
+
+  return true;
+}
+
+void HostDrmDevice::GpuCheckOverlayCapabilitiesCallback(
+    const gfx::AcceleratedWidget& widget,
+    const OverlaySurfaceCandidateList& overlays,
+    const OverlayStatusList& returns) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  overlay_manager_->GpuSentOverlayResult(widget, overlays, returns);
+}
+
+void HostDrmDevice::GpuConfigureNativeDisplayCallback(int64_t display_id,
+                                                      bool success) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuConfiguredDisplay(display_id, success);
+}
+
+// TODO(rjkroege): Remove the unnecessary conversion back into params.
+void HostDrmDevice::GpuRefreshNativeDisplaysCallback(
+    std::vector<std::unique_ptr<display::DisplaySnapshot>> displays) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuHasUpdatedNativeDisplays(
+      CreateParamsFromSnapshot(displays));
+}
+
+void HostDrmDevice::GpuDisableNativeDisplayCallback(int64_t display_id,
+                                                    bool success) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuConfiguredDisplay(display_id, success);
+}
+
+void HostDrmDevice::GpuTakeDisplayControlCallback(bool success) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuTookDisplayControl(success);
+}
+
+void HostDrmDevice::GpuRelinquishDisplayControlCallback(bool success) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuRelinquishedDisplayControl(success);
+}
+
+void HostDrmDevice::GpuGetHDCPStateCallback(int64_t display_id,
+                                            bool success,
+                                            display::HDCPState state) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuReceivedHDCPState(display_id, success, state);
+}
+
+void HostDrmDevice::GpuSetHDCPStateCallback(int64_t display_id,
+                                            bool success) const {
+  DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_);
+  display_manager_->GpuUpdatedHDCPState(display_id, success);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/mus_thread_proxy.h b/ui/ozone/platform/drm/host/host_drm_device.h
similarity index 66%
rename from ui/ozone/platform/drm/mus_thread_proxy.h
rename to ui/ozone/platform/drm/host/host_drm_device.h
index 49f6b5fa..47c5b18 100644
--- a/ui/ozone/platform/drm/mus_thread_proxy.h
+++ b/ui/ozone/platform/drm/host/host_drm_device.h
@@ -2,24 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_
-#define UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_HOST_DRM_DEVICE_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_HOST_DRM_DEVICE_H_
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/ozone/platform/drm/common/display_types.h"
-#include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h"
 #include "ui/ozone/platform/drm/host/drm_cursor.h"
 #include "ui/ozone/platform/drm/host/gpu_thread_adapter.h"
 #include "ui/ozone/public/interfaces/device_cursor.mojom.h"
 #include "ui/ozone/public/interfaces/drm_device.mojom.h"
 
-namespace base {
-class SingleThreadTaskRunner;
+namespace display {
+class DisplaySnapshot;
 }
 
 namespace service_manager {
@@ -27,37 +26,30 @@
 }
 
 namespace ui {
-
 class DrmDisplayHostManager;
 class DrmOverlayManager;
-class DrmThread;
 class GpuThreadObserver;
-class MusThreadProxy;
 
-// TODO(rjkroege): Originally we had planned on running the window server, gpu,
-// compositor and drm threads together in the same process. However, system
-// security requires separating event handling and embedding decisions (the
-// window server) into a process separate from the viz server
-// (//services/viz/README.md). The separated implementation remains incomplete
-// and will be completed in subsequent CLs. At that point, this class will
-// become the viz host for ozone services and a separate class will contain the
-// viz service (DRM interface.)
-class MusThreadProxy : public GpuThreadAdapter,
-                       public InterThreadMessagingProxy {
+// This is the Viz host-side library for the DRM device service provided by the
+// viz process.
+class HostDrmDevice : public GpuThreadAdapter {
  public:
-  MusThreadProxy(DrmCursor* cursor, service_manager::Connector* connector);
-  ~MusThreadProxy() override;
+  HostDrmDevice(DrmCursor* cursor, service_manager::Connector* connector);
+  ~HostDrmDevice() override;
 
-  void StartDrmThread();
+  // Start the DRM service. Runs the |OnDrmServiceStartedCallback| when the
+  // service has launched and initiates the remaining startup.
+  void AsyncStartDrmDevice();
+
+  // Blocks until the DRM service has come up. Use this entry point only when
+  // supporting launch of the service where the ozone UI and GPU
+  // reponsibilities are performed by the same underlying thread.
+  void BlockingStartDrmDevice();
+
   void ProvideManagers(DrmDisplayHostManager* display_manager,
                        DrmOverlayManager* overlay_manager);
 
-  // InterThreadMessagingProxy.
-  // TODO(rjkroege): Remove when mojo everywhere.
-  void SetDrmThread(DrmThread* thread) override;
-
-  // This is the core functionality. They are invoked when we have a main
-  // thread, a gpu thread and we have called initialize on both.
+  // GpuThreadAdapter
   void AddGpuThreadObserver(GpuThreadObserver* observer) override;
   void RemoveGpuThreadObserver(GpuThreadObserver* observer) override;
   bool IsConnected() override;
@@ -101,8 +93,9 @@
                               const gfx::Rect& bounds) override;
 
  private:
+  void OnDrmServiceStartedCallback(bool success);
+  void PollForSingleThreadReady(int previous_delay);
   void RunObservers();
-  void DispatchObserversFromDrmThread();
 
   void GpuCheckOverlayCapabilitiesCallback(
       const gfx::AcceleratedWidget& widget,
@@ -112,7 +105,8 @@
   void GpuConfigureNativeDisplayCallback(int64_t display_id,
                                          bool success) const;
 
-  void GpuRefreshNativeDisplaysCallback(MovableDisplaySnapshots displays) const;
+  void GpuRefreshNativeDisplaysCallback(
+      std::vector<std::unique_ptr<display::DisplaySnapshot>> displays) const;
   void GpuDisableNativeDisplayCallback(int64_t display_id, bool success) const;
   void GpuTakeDisplayControlCallback(bool success) const;
   void GpuRelinquishDisplayControlCallback(bool success) const;
@@ -121,32 +115,24 @@
                                display::HDCPState state) const;
   void GpuSetHDCPStateCallback(int64_t display_id, bool success) const;
 
-  scoped_refptr<base::SingleThreadTaskRunner> ws_task_runner_;
-
   // Mojo implementation of the DrmDevice.
-  ui::ozone::mojom::DrmDevicePtr gpu_adapter_;
-
-  // TODO(rjkroege): Remove this in a subsequent CL (http://crbug.com/620927)
-  DrmThread* drm_thread_;  // Not owned.
-
-  // Guards  for multi-theaded access to drm_thread_.
-  base::Lock lock_;
+  ui::ozone::mojom::DrmDevicePtr drm_device_ptr_;
 
   DrmDisplayHostManager* display_manager_;  // Not owned.
   DrmOverlayManager* overlay_manager_;      // Not owned.
   DrmCursor* cursor_;                       // Not owned.
 
   service_manager::Connector* connector_;
+  THREAD_CHECKER(on_window_server_thread_);
 
+  bool connected_ = false;
   base::ObserverList<GpuThreadObserver> gpu_thread_observers_;
 
-  base::ThreadChecker on_window_server_thread_;
+  base::WeakPtrFactory<HostDrmDevice> weak_ptr_factory_;
 
-  base::WeakPtrFactory<MusThreadProxy> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(MusThreadProxy);
+  DISALLOW_COPY_AND_ASSIGN(HostDrmDevice);
 };
 
 }  // namespace ui
 
-#endif  // UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_HOST_DRM_DEVICE_H_
diff --git a/ui/ozone/platform/drm/mus_thread_proxy.cc b/ui/ozone/platform/drm/mus_thread_proxy.cc
deleted file mode 100644
index 95fb8621..0000000
--- a/ui/ozone/platform/drm/mus_thread_proxy.cc
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/ozone/platform/drm/mus_thread_proxy.h"
-
-#include "base/bind.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "ui/display/types/display_snapshot.h"
-#include "ui/ozone/platform/drm/common/drm_util.h"
-#include "ui/ozone/platform/drm/cursor_proxy_mojo.h"
-#include "ui/ozone/platform/drm/gpu/drm_thread.h"
-#include "ui/ozone/platform/drm/gpu/proxy_helpers.h"
-#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
-#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
-
-namespace ui {
-
-MusThreadProxy::MusThreadProxy(DrmCursor* cursor,
-                               service_manager::Connector* connector)
-    : ws_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      drm_thread_(nullptr),
-      cursor_(cursor),
-      connector_(connector),
-      weak_ptr_factory_(this) {
-  // Bind the viz process pointer here.
-  connector->BindInterface(ui::mojom::kServiceName, &gpu_adapter_);
-}
-
-MusThreadProxy::~MusThreadProxy() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  for (GpuThreadObserver& observer : gpu_thread_observers_)
-    observer.OnGpuThreadRetired();
-}
-
-// TODO(rjkroege): Relocate to a viz process specific class.
-void MusThreadProxy::SetDrmThread(DrmThread* thread) {
-  base::AutoLock acquire(lock_);
-  drm_thread_ = thread;
-}
-
-void MusThreadProxy::ProvideManagers(DrmDisplayHostManager* display_manager,
-                                     DrmOverlayManager* overlay_manager) {
-  display_manager_ = display_manager;
-  overlay_manager_ = overlay_manager;
-}
-
-// TODO(rjkroege): Relocate to a viz process specific class.
-void MusThreadProxy::StartDrmThread() {
-  DCHECK(drm_thread_);
-  drm_thread_->Start();
-
-  drm_thread_->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&MusThreadProxy::DispatchObserversFromDrmThread,
-                            base::Unretained(this)));
-}
-
-// TODO(rjkroege): Relocate to a viz process specific class.
-void MusThreadProxy::DispatchObserversFromDrmThread() {
-  ws_task_runner_->PostTask(FROM_HERE, base::Bind(&MusThreadProxy::RunObservers,
-                                                  base::Unretained(this)));
-}
-
-void MusThreadProxy::RunObservers() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  for (GpuThreadObserver& observer : gpu_thread_observers_) {
-    // TODO(rjkroege): This needs to be different when gpu process split
-    // happens.
-    observer.OnGpuProcessLaunched();
-    observer.OnGpuThreadReady();
-  }
-
-  // The cursor is special since it will process input events on the IO thread
-  // and can by-pass the UI thread. This means that we need to special case it
-  // and notify it after all other observers/handlers are notified.
-  cursor_->SetDrmCursorProxy(base::MakeUnique<CursorProxyMojo>(connector_));
-
-  // TODO(rjkroege): Call ResetDrmCursorProxy when the mojo connection to the
-  // DRM thread is broken.
-}
-
-void MusThreadProxy::AddGpuThreadObserver(GpuThreadObserver* observer) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-
-  gpu_thread_observers_.AddObserver(observer);
-  if (IsConnected()) {
-    // TODO(rjkroege): This needs to be different when gpu process split
-    // happens.
-    observer->OnGpuProcessLaunched();
-    observer->OnGpuThreadReady();
-  }
-}
-
-void MusThreadProxy::RemoveGpuThreadObserver(GpuThreadObserver* observer) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  gpu_thread_observers_.RemoveObserver(observer);
-}
-
-bool MusThreadProxy::IsConnected() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  base::AutoLock acquire(lock_);
-  if (drm_thread_)
-    return drm_thread_->IsRunning();
-  return false;
-}
-
-// Services needed for DrmDisplayHostMananger.
-void MusThreadProxy::RegisterHandlerForDrmDisplayHostManager(
-    DrmDisplayHostManager* handler) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_ = handler;
-}
-
-void MusThreadProxy::UnRegisterHandlerForDrmDisplayHostManager() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_ = nullptr;
-}
-
-bool MusThreadProxy::GpuCreateWindow(gfx::AcceleratedWidget widget) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-
-  gpu_adapter_->CreateWindow(widget);
-  return true;
-}
-
-bool MusThreadProxy::GpuDestroyWindow(gfx::AcceleratedWidget widget) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-
-  gpu_adapter_->DestroyWindow(widget);
-  return true;
-}
-
-bool MusThreadProxy::GpuWindowBoundsChanged(gfx::AcceleratedWidget widget,
-                                            const gfx::Rect& bounds) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-
-  gpu_adapter_->SetWindowBounds(widget, bounds);
-  return true;
-}
-
-// Services needed for DrmOverlayManager.
-void MusThreadProxy::RegisterHandlerForDrmOverlayManager(
-    DrmOverlayManager* handler) {
-  // TODO(rjkroege): Permit overlay manager to run in viz when the display
-  // compositor runs in viz.
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  overlay_manager_ = handler;
-}
-
-void MusThreadProxy::UnRegisterHandlerForDrmOverlayManager() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  overlay_manager_ = nullptr;
-}
-
-bool MusThreadProxy::GpuCheckOverlayCapabilities(
-    gfx::AcceleratedWidget widget,
-    const OverlaySurfaceCandidateList& overlays) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-
-  auto callback =
-      base::BindOnce(&MusThreadProxy::GpuCheckOverlayCapabilitiesCallback,
-                     weak_ptr_factory_.GetWeakPtr());
-
-  gpu_adapter_->CheckOverlayCapabilities(widget, overlays, std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuRefreshNativeDisplays() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  auto callback =
-      base::BindOnce(&MusThreadProxy::GpuRefreshNativeDisplaysCallback,
-                     weak_ptr_factory_.GetWeakPtr());
-  gpu_adapter_->RefreshNativeDisplays(std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuConfigureNativeDisplay(int64_t id,
-                                               const DisplayMode_Params& pmode,
-                                               const gfx::Point& origin) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-
-  // TODO(rjkroege): Remove the use of mode here.
-  auto mode = CreateDisplayModeFromParams(pmode);
-  auto callback =
-      base::BindOnce(&MusThreadProxy::GpuConfigureNativeDisplayCallback,
-                     weak_ptr_factory_.GetWeakPtr());
-
-  gpu_adapter_->ConfigureNativeDisplay(id, std::move(mode), origin,
-                                       std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuDisableNativeDisplay(int64_t id) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  auto callback =
-      base::BindOnce(&MusThreadProxy::GpuDisableNativeDisplayCallback,
-                     weak_ptr_factory_.GetWeakPtr());
-  gpu_adapter_->DisableNativeDisplay(id, std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuTakeDisplayControl() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  auto callback = base::BindOnce(&MusThreadProxy::GpuTakeDisplayControlCallback,
-                                 weak_ptr_factory_.GetWeakPtr());
-  gpu_adapter_->TakeDisplayControl(std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuRelinquishDisplayControl() {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  auto callback =
-      base::BindOnce(&MusThreadProxy::GpuRelinquishDisplayControlCallback,
-                     weak_ptr_factory_.GetWeakPtr());
-  gpu_adapter_->TakeDisplayControl(std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuAddGraphicsDevice(const base::FilePath& path,
-                                          base::ScopedFD fd) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  base::File file(fd.release());
-  gpu_adapter_->AddGraphicsDevice(path, std::move(file));
-  return true;
-}
-
-bool MusThreadProxy::GpuRemoveGraphicsDevice(const base::FilePath& path) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  gpu_adapter_->RemoveGraphicsDevice(std::move(path));
-  return true;
-}
-
-bool MusThreadProxy::GpuGetHDCPState(int64_t display_id) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  auto callback = base::BindOnce(&MusThreadProxy::GpuGetHDCPStateCallback,
-                                 weak_ptr_factory_.GetWeakPtr());
-  gpu_adapter_->GetHDCPState(display_id, std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuSetHDCPState(int64_t display_id,
-                                     display::HDCPState state) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-  auto callback = base::BindOnce(&MusThreadProxy::GpuSetHDCPStateCallback,
-                                 weak_ptr_factory_.GetWeakPtr());
-  gpu_adapter_->SetHDCPState(display_id, state, std::move(callback));
-  return true;
-}
-
-bool MusThreadProxy::GpuSetColorCorrection(
-    int64_t id,
-    const std::vector<display::GammaRampRGBEntry>& degamma_lut,
-    const std::vector<display::GammaRampRGBEntry>& gamma_lut,
-    const std::vector<float>& correction_matrix) {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  if (!drm_thread_ || !drm_thread_->IsRunning())
-    return false;
-
-  gpu_adapter_->SetColorCorrection(id, degamma_lut, gamma_lut,
-                                   correction_matrix);
-
-  return true;
-}
-
-void MusThreadProxy::GpuCheckOverlayCapabilitiesCallback(
-    const gfx::AcceleratedWidget& widget,
-    const OverlaySurfaceCandidateList& overlays,
-    const OverlayStatusList& returns) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  overlay_manager_->GpuSentOverlayResult(widget, overlays, returns);
-}
-
-void MusThreadProxy::GpuConfigureNativeDisplayCallback(int64_t display_id,
-                                                       bool success) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuConfiguredDisplay(display_id, success);
-}
-
-// TODO(rjkroege): Remove the unnecessary conversion back into params.
-void MusThreadProxy::GpuRefreshNativeDisplaysCallback(
-    MovableDisplaySnapshots displays) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuHasUpdatedNativeDisplays(
-      CreateParamsFromSnapshot(displays));
-}
-
-void MusThreadProxy::GpuDisableNativeDisplayCallback(int64_t display_id,
-                                                     bool success) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuConfiguredDisplay(display_id, success);
-}
-
-void MusThreadProxy::GpuTakeDisplayControlCallback(bool success) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuTookDisplayControl(success);
-}
-
-void MusThreadProxy::GpuRelinquishDisplayControlCallback(bool success) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuRelinquishedDisplayControl(success);
-}
-
-void MusThreadProxy::GpuGetHDCPStateCallback(int64_t display_id,
-                                             bool success,
-                                             display::HDCPState state) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuReceivedHDCPState(display_id, success, state);
-}
-
-void MusThreadProxy::GpuSetHDCPStateCallback(int64_t display_id,
-                                             bool success) const {
-  DCHECK(on_window_server_thread_.CalledOnValidThread());
-  display_manager_->GpuUpdatedHDCPState(display_id, success);
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.cc b/ui/ozone/platform/drm/ozone_platform_gbm.cc
index e4152dd..1de8eab6 100644
--- a/ui/ozone/platform/drm/ozone_platform_gbm.cc
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/threading/platform_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
@@ -26,7 +27,6 @@
 #include "ui/events/ozone/evdev/event_factory_evdev.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
-#include "ui/ozone/platform/drm/cursor_proxy_mojo.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
@@ -43,7 +43,7 @@
 #include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
 #include "ui/ozone/platform/drm/host/drm_window_host.h"
 #include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
-#include "ui/ozone/platform/drm/mus_thread_proxy.h"
+#include "ui/ozone/platform/drm/host/host_drm_device.h"
 #include "ui/ozone/public/cursor_factory_ozone.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -146,7 +146,7 @@
       const gfx::Rect& bounds) override {
     GpuThreadAdapter* adapter = gpu_platform_support_host_.get();
     if (using_mojo_ || single_process_) {
-      adapter = mus_thread_proxy_.get();
+      adapter = host_drm_device_.get();
     }
 
     std::unique_ptr<DrmWindowHost> platform_window(new DrmWindowHost(
@@ -169,6 +169,7 @@
     //      via mojo IPC.
     single_process_ = args.single_process;
     using_mojo_ = args.connector != nullptr;
+    host_thread_ = base::PlatformThread::CurrentRef();
 
     DCHECK(!(using_mojo_ && !single_process_))
         << "Multiprocess Mojo is not supported yet.";
@@ -194,9 +195,9 @@
       gl_api_loader_.reset(new GlApiLoader());
 
     if (using_mojo_) {
-      mus_thread_proxy_ =
-          base::MakeUnique<MusThreadProxy>(cursor_.get(), args.connector);
-      adapter = mus_thread_proxy_.get();
+      host_drm_device_ =
+          base::MakeUnique<HostDrmDevice>(cursor_.get(), args.connector);
+      adapter = host_drm_device_.get();
     } else {
       gpu_platform_support_host_.reset(
           new DrmGpuPlatformSupportHost(cursor_.get()));
@@ -211,22 +212,26 @@
     cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone);
 
     if (using_mojo_) {
-      mus_thread_proxy_->ProvideManagers(display_manager_.get(),
-                                         overlay_manager_.get());
+      host_drm_device_->ProvideManagers(display_manager_.get(),
+                                        overlay_manager_.get());
+      host_drm_device_->AsyncStartDrmDevice();
     }
   }
+
   void InitializeGPU(const InitParams& args) override {
     // TODO(rjkroege): services/ui should initialize this with a connector.
     // However, in-progress refactorings in services/ui make it difficult to
     // require this at present. Set using_mojo_ like below once this is
     // complete.
+    // TODO(rjk): Make it possible to turn this on.
     // using_mojo_ = args.connector != nullptr;
     gpu_task_runner_ = base::ThreadTaskRunnerHandle::Get();
-    InterThreadMessagingProxy* itmp;
-    if (using_mojo_) {
-      itmp = mus_thread_proxy_.get();
-    } else {
+
+    if (!single_process_)
       gl_api_loader_.reset(new GlApiLoader());
+
+    InterThreadMessagingProxy* itmp;
+    if (!using_mojo_) {
       scoped_refptr<DrmThreadMessageProxy> message_proxy(
           new DrmThreadMessageProxy());
       itmp = message_proxy.get();
@@ -236,11 +241,12 @@
     // NOTE: Can't start the thread here since this is called before sandbox
     // initialization in multi-process Chrome. In mus, we start the DRM thread.
     drm_thread_proxy_.reset(new DrmThreadProxy());
-    drm_thread_proxy_->BindThreadIntoMessagingProxy(itmp);
 
     surface_factory_.reset(new GbmSurfaceFactory(drm_thread_proxy_.get()));
-    if (using_mojo_) {
-      mus_thread_proxy_->StartDrmThread();
+    if (!using_mojo_) {
+      drm_thread_proxy_->BindThreadIntoMessagingProxy(itmp);
+    } else {
+      drm_thread_proxy_->StartDrmThread();
     }
 
     // When the viz process (and hence the gpu portion of ozone/gbm) is
@@ -255,26 +261,38 @@
     for (auto& request : pending_gpu_adapter_requests_)
       drm_thread_proxy_->AddBindingDrmDevice(std::move(request));
     pending_gpu_adapter_requests_.clear();
+
+    // If InitializeGPU and InitializeUI are invoked on the same thread, startup
+    // sequencing is complicated because tasks are queued on the unbound mojo
+    // pipe connecting the UI (the host) to the DRM thread before the DRM thread
+    // is launched above. Special case this sequence vis the
+    // BlockingStartDrmDevice API.
+    // TODO(rjkroege): In a future when we have completed splitting Viz, it will
+    // be possible to simplify this logic.
+    if (using_mojo_ && single_process_ &&
+        host_thread_ == base::PlatformThread::CurrentRef()) {
+      CHECK(host_drm_device_)
+          << "Mojo single-process mode requires a HostDrmDevice.";
+      host_drm_device_->BlockingStartDrmDevice();
+    }
   }
 
  private:
   bool using_mojo_;
   bool single_process_;
-
-  // Bridges the DRM, GPU and main threads in mus. This must be destroyed last.
-  std::unique_ptr<MusThreadProxy> mus_thread_proxy_;
+  base::PlatformThreadRef host_thread_;
 
   // Objects in the GPU process.
   std::unique_ptr<DrmThreadProxy> drm_thread_proxy_;
   std::unique_ptr<GlApiLoader> gl_api_loader_;
   std::unique_ptr<GbmSurfaceFactory> surface_factory_;
   scoped_refptr<IPC::MessageFilter> gpu_message_filter_;
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
 
-  // TODO(rjkroege,sadrul): Once the mus gpu process split happens, this can go
-  // away.
+  // TODO(rjkroege,sadrul): Provide a more elegant solution for this issue when
+  // running in single process mode.
   std::vector<ozone::mojom::DeviceCursorRequest> pending_cursor_requests_;
   std::vector<ozone::mojom::DrmDeviceRequest> pending_gpu_adapter_requests_;
-  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
 
   // Objects in the Browser process.
   std::unique_ptr<DeviceManager> device_manager_;
@@ -282,10 +300,17 @@
   std::unique_ptr<DrmWindowHostManager> window_manager_;
   std::unique_ptr<DrmCursor> cursor_;
   std::unique_ptr<EventFactoryEvdev> event_factory_ozone_;
-  std::unique_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_;
   std::unique_ptr<DrmDisplayHostManager> display_manager_;
   std::unique_ptr<DrmOverlayManager> overlay_manager_;
 
+  // gpu_platform_support_host_ is the IPC bridge to the GPU process while
+  // host_drm_device_ is the mojo bridge to the Viz process. Only one can be in
+  // use at any time.
+  // TODO(rjkroege): Remove gpu_platform_support_host_ once ozone/drm with mojo
+  // has reached the stable channel.
+  std::unique_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_;
+  std::unique_ptr<HostDrmDevice> host_drm_device_;
+
 #if BUILDFLAG(USE_XKBCOMMON)
   XkbEvdevCodes xkb_evdev_code_converter_;
 #endif
diff --git a/ui/ozone/public/interfaces/drm_device.mojom b/ui/ozone/public/interfaces/drm_device.mojom
index 0c7ddcd..eede424 100644
--- a/ui/ozone/public/interfaces/drm_device.mojom
+++ b/ui/ozone/public/interfaces/drm_device.mojom
@@ -20,6 +20,10 @@
 // All functions in DrmDevice are implemented by the lower privilege viz
 // process.
 interface DrmDevice {
+  // Starts the DRM service and returns true on success.
+  [Sync]
+  StartDrmDevice() => (bool success);
+
   // Creates scanout capable DRM buffers to back |widget|.
   CreateWindow(gfx.mojom.AcceleratedWidget widget);
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index e242311..6a7ad028 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -53,6 +53,7 @@
     "animation/ink_drop_ripple_observer.h",
     "animation/ink_drop_state.h",
     "animation/ink_drop_stub.h",
+    "animation/ink_drop_util.h",
     "animation/scroll_animator.h",
     "animation/square_ink_drop_ripple.h",
     "background.h",
@@ -251,6 +252,7 @@
     "animation/ink_drop_ripple.cc",
     "animation/ink_drop_state.cc",
     "animation/ink_drop_stub.cc",
+    "animation/ink_drop_util.cc",
     "animation/scroll_animator.cc",
     "animation/square_ink_drop_ripple.cc",
     "background.cc",
diff --git a/ui/views/animation/flood_fill_ink_drop_ripple.cc b/ui/views/animation/flood_fill_ink_drop_ripple.cc
index 7f2c4c1..849ee7dc 100644
--- a/ui/views/animation/flood_fill_ink_drop_ripple.cc
+++ b/ui/views/animation/flood_fill_ink_drop_ripple.cc
@@ -14,6 +14,7 @@
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/views/animation/ink_drop_util.h"
 
 namespace {
 
@@ -426,6 +427,10 @@
       circle_layer_delegate_.GetCenteringOffset();
   transform.Translate(-drawn_center_offset.x(), -drawn_center_offset.y());
 
+  // Add subpixel correction to the transform.
+  transform.ConcatTransform(GetTransformSubpixelCorrection(
+      transform, painted_layer_.device_scale_factor()));
+
   return transform;
 }
 
diff --git a/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc b/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc
index 44ea59f..f61dae86 100644
--- a/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc
+++ b/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc
@@ -7,6 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.h"
@@ -29,10 +30,11 @@
                                 SK_ColorWHITE, 0.175f);
   FloodFillInkDropRippleTestApi test_api(&ripple);
 
-  gfx::Point actual_center = test_api.GetDrawnCenterPoint();
+  gfx::Point3F actual_center(gfx::PointF(test_api.GetDrawnCenterPoint()));
   test_api.TransformPoint(10, &actual_center);
 
-  EXPECT_EQ(expected_center_point, actual_center);
+  EXPECT_EQ(expected_center_point,
+            gfx::ToRoundedPoint(actual_center.AsPointF()));
 }
 
 TEST(FloodFillInkDropRippleTest, MaxDistanceToCorners) {
@@ -107,5 +109,40 @@
       activated_transform.ApproximatelyEqual(pending_activated_transform));
 }
 
+TEST(FloodFillInkDropRippleTest, TransformIsPixelAligned) {
+  const float kEpsilon = 0.001f;
+
+  const gfx::Size host_size(11, 11);
+  // Keep the draw center different from the the host center to have a non zero
+  // offset in the transformation.
+  const gfx::Point center_point(host_size.width() / 3, host_size.height() / 3);
+  const SkColor color = SK_ColorYELLOW;
+  const float visible_opacity = 0.3f;
+
+  FloodFillInkDropRipple ripple(host_size, center_point, color,
+                                visible_opacity);
+  FloodFillInkDropRippleTestApi test_api(&ripple);
+
+  for (auto dsf : {1.25, 1.33, 1.5, 1.6, 1.75, 1.8, 2.25}) {
+    SCOPED_TRACE(testing::Message()
+                 << std::endl
+                 << "Device Scale Factor: " << dsf << std::endl);
+    ripple.GetRootLayer()->OnDeviceScaleFactorChanged(dsf);
+    gfx::Point3F ripple_origin;
+
+    test_api.TransformPoint(host_size.width() / 2, &ripple_origin);
+
+    // Apply device scale factor to get the final offset.
+    gfx::Transform dsf_transform;
+    dsf_transform.Scale(dsf, dsf);
+    dsf_transform.TransformPoint(&ripple_origin);
+
+    EXPECT_NEAR(ripple_origin.x(), gfx::ToRoundedInt(ripple_origin.x()),
+                kEpsilon);
+    EXPECT_NEAR(ripple_origin.y(), gfx::ToRoundedInt(ripple_origin.y()),
+                kEpsilon);
+  }
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_highlight.cc b/ui/views/animation/ink_drop_highlight.cc
index effde49..98c9030 100644
--- a/ui/views/animation/ink_drop_highlight.cc
+++ b/ui/views/animation/ink_drop_highlight.cc
@@ -17,6 +17,7 @@
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/animation/ink_drop_highlight_observer.h"
 #include "ui/views/animation/ink_drop_painted_layer_delegates.h"
+#include "ui/views/animation/ink_drop_util.h"
 
 namespace views {
 
@@ -161,6 +162,11 @@
                   size_.height() == 0 ? 0 : size.height() / size_.height());
   gfx::Vector2dF layer_offset = layer_delegate_->GetCenteringOffset();
   transform.Translate(-layer_offset.x(), -layer_offset.y());
+
+  // Add subpixel correction to the transform.
+  transform.ConcatTransform(
+      GetTransformSubpixelCorrection(transform, layer_->device_scale_factor()));
+
   return transform;
 }
 
diff --git a/ui/views/animation/ink_drop_highlight_unittest.cc b/ui/views/animation/ink_drop_highlight_unittest.cc
index 94dc954..95380db 100644
--- a/ui/views/animation/ink_drop_highlight_unittest.cc
+++ b/ui/views/animation/ink_drop_highlight_unittest.cc
@@ -10,10 +10,12 @@
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/animation/animation_test_api.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
 #include "ui/views/animation/test/ink_drop_highlight_test_api.h"
 #include "ui/views/animation/test/test_ink_drop_highlight_observer.h"
 
@@ -206,5 +208,35 @@
                                 false /* explode */);
 }
 
+TEST_F(InkDropHighlightTest, TransformIsPixelAligned) {
+  const float kEpsilon = 0.001f;
+  gfx::Size highlight_size(10, 10);
+  InitHighlight(base::MakeUnique<InkDropHighlight>(
+      highlight_size, 3, gfx::PointF(3.5f, 3.5f), SK_ColorYELLOW));
+  const gfx::PointF layer_origin(
+      ink_drop_highlight()->layer()->bounds().origin());
+  for (auto dsf : {1.25, 1.33, 1.5, 1.6, 1.75, 1.8, 2.25}) {
+    SCOPED_TRACE(testing::Message()
+                 << std::endl
+                 << "Device Scale Factor: " << dsf << std::endl);
+    ink_drop_highlight()->layer()->OnDeviceScaleFactorChanged(dsf);
+
+    const gfx::SizeF size(highlight_size);
+    gfx::Transform transform = test_api()->CalculateTransform(size);
+    gfx::Point3F transformed_layer_origin(layer_origin.x(), layer_origin.y(),
+                                          0);
+    transform.TransformPoint(&transformed_layer_origin);
+
+    // Apply device scale factor to get the final offset.
+    gfx::Transform dsf_transform;
+    dsf_transform.Scale(dsf, dsf);
+    dsf_transform.TransformPoint(&transformed_layer_origin);
+    EXPECT_NEAR(transformed_layer_origin.x(),
+                gfx::ToRoundedInt(transformed_layer_origin.x()), kEpsilon);
+    EXPECT_NEAR(transformed_layer_origin.y(),
+                gfx::ToRoundedInt(transformed_layer_origin.y()), kEpsilon);
+  }
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_mask.cc b/ui/views/animation/ink_drop_mask.cc
index d95286d9..104c4b3 100644
--- a/ui/views/animation/ink_drop_mask.cc
+++ b/ui/views/animation/ink_drop_mask.cc
@@ -48,9 +48,15 @@
   flags.setAntiAlias(true);
 
   ui::PaintRecorder recorder(context, layer()->size());
-  gfx::RectF bounds(layer()->bounds());
-  bounds.Inset(mask_insets_);
-  recorder.canvas()->DrawRoundRect(bounds, corner_radius_, flags);
+  const float dsf = recorder.canvas()->UndoDeviceScaleFactor();
+
+  gfx::RectF masking_bound(layer()->bounds());
+  masking_bound.Inset(mask_insets_);
+
+  const gfx::Rect masking_bound_scaled =
+      gfx::ScaleToRoundedRect(gfx::ToNearestRect(masking_bound), dsf);
+  recorder.canvas()->DrawRoundRect(masking_bound_scaled, corner_radius_ * dsf,
+                                   flags);
 }
 
 // CircleInkDropMask
diff --git a/ui/views/animation/ink_drop_util.cc b/ui/views/animation/ink_drop_util.cc
new file mode 100644
index 0000000..4344d479
--- /dev/null
+++ b/ui/views/animation/ink_drop_util.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/animation/ink_drop_util.h"
+
+#include <math.h>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/transform.h"
+
+namespace views {
+
+gfx::Transform GetTransformSubpixelCorrection(const gfx::Transform& transform,
+                                              float device_scale_factor) {
+  gfx::Point3F origin;
+  transform.TransformPoint(&origin);
+
+  const gfx::Vector2dF offset_in_dip = origin.AsPointF().OffsetFromOrigin();
+
+  // Scale the origin to screen space
+  origin.Scale(device_scale_factor);
+
+  // Compute the rounded offset in screen space and finally unscale it back to
+  // DIP space.
+  gfx::Vector2dF aligned_offset_in_dip = origin.AsPointF().OffsetFromOrigin();
+  aligned_offset_in_dip.set_x(gfx::ToRoundedInt(aligned_offset_in_dip.x()));
+  aligned_offset_in_dip.set_y(gfx::ToRoundedInt(aligned_offset_in_dip.y()));
+  aligned_offset_in_dip.Scale(1.f / device_scale_factor);
+
+  // Compute the subpixel offset correction and apply it to the transform.
+  gfx::Transform subpixel_correction;
+  subpixel_correction.Translate(aligned_offset_in_dip - offset_in_dip);
+#if DCHECK_IS_ON()
+  const float kEpsilon = 0.0001f;
+  gfx::Point3F offset;
+
+  gfx::Transform transform_corrected(transform);
+  transform_corrected.ConcatTransform(subpixel_correction);
+  transform_corrected.TransformPoint(&offset);
+  offset.Scale(device_scale_factor);
+
+  if (!std::isnan(offset.x()))
+    DCHECK_LT(std::abs(gfx::ToRoundedInt(offset.x()) - offset.x()), kEpsilon);
+  if (!std::isnan(offset.y()))
+    DCHECK_LT(std::abs(gfx::ToRoundedInt(offset.y()) - offset.y()), kEpsilon);
+#endif
+  return subpixel_correction;
+}
+
+}  // namespace views
diff --git a/ui/views/animation/ink_drop_util.h b/ui/views/animation/ink_drop_util.h
new file mode 100644
index 0000000..883b789
--- /dev/null
+++ b/ui/views/animation/ink_drop_util.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_ANIMATION_INK_DROP_UTIL_H_
+#define UI_VIEWS_ANIMATION_INK_DROP_UTIL_H_
+
+#include "ui/views/views_export.h"
+
+namespace gfx {
+class Transform;
+}  // namespace gfx
+
+namespace views {
+
+// A layer |transform| may add an offset to its layer relative to the parent
+// layer. This offset does not take into consideration the subpixel positioning.
+// A subpixel correction needs to be applied to make sure the layers are pixel
+// aligned after the transform is applied. Use this function to compute the
+// subpixel correction transform.
+VIEWS_EXPORT gfx::Transform GetTransformSubpixelCorrection(
+    const gfx::Transform& transform,
+    float device_scale_factor);
+
+}  // namespace views
+
+#endif  // UI_VIEWS_ANIMATION_INK_DROP_UTIL_H_
diff --git a/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.cc b/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.cc
index 11ee71d..b567302 100644
--- a/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.cc
+++ b/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.cc
@@ -23,7 +23,7 @@
 FloodFillInkDropRippleTestApi::~FloodFillInkDropRippleTestApi() {}
 
 void FloodFillInkDropRippleTestApi::TransformPoint(float radius,
-                                                   gfx::Point* point) {
+                                                   gfx::Point3F* point) {
   ink_drop_ripple()->CalculateTransform(radius).TransformPoint(point);
 }
 
diff --git a/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.h b/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.h
index 7e83d96..2793dcdb 100644
--- a/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.h
+++ b/ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.h
@@ -28,7 +28,7 @@
 
   // Transforms |point| into the FloodFillInkDropRipples clip layer coordinate
   // space for the given radius.
-  void TransformPoint(float radius, gfx::Point* point);
+  void TransformPoint(float radius, gfx::Point3F* point);
 
   // Returns the center point that the ripple is drawn at in the original Canvas
   // coordinate space.
diff --git a/ui/views/animation/test/ink_drop_highlight_test_api.cc b/ui/views/animation/test/ink_drop_highlight_test_api.cc
index 77ba8fb2..4111c89 100644
--- a/ui/views/animation/test/ink_drop_highlight_test_api.cc
+++ b/ui/views/animation/test/ink_drop_highlight_test_api.cc
@@ -26,5 +26,10 @@
   return animators;
 }
 
+gfx::Transform InkDropHighlightTestApi::CalculateTransform(
+    const gfx::SizeF& size) {
+  return ink_drop_highlight()->CalculateTransform(size);
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/animation/test/ink_drop_highlight_test_api.h b/ui/views/animation/test/ink_drop_highlight_test_api.h
index e542b8a..c7bec20 100644
--- a/ui/views/animation/test/ink_drop_highlight_test_api.h
+++ b/ui/views/animation/test/ink_drop_highlight_test_api.h
@@ -10,6 +10,8 @@
 #include "base/macros.h"
 #include "ui/compositor/test/multi_layer_animator_test_controller.h"
 #include "ui/compositor/test/multi_layer_animator_test_controller_delegate.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/transform.h"
 
 namespace ui {
 class LayerAnimator;
@@ -33,6 +35,8 @@
   // MultiLayerAnimatorTestControllerDelegate:
   std::vector<ui::LayerAnimator*> GetLayerAnimators() override;
 
+  gfx::Transform CalculateTransform(const gfx::SizeF& size);
+
  protected:
   InkDropHighlight* ink_drop_highlight() {
     return static_cast<const InkDropHighlightTestApi*>(this)
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 99f1a30f6..94be6ec 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -1984,7 +1984,7 @@
   location.set_x(GetMirroredXForRect(location));
   location.set_height(
       std::min(location.height(),
-               GetVisibleBounds().height() - location.y() - location.y()));
+               GetContentsBounds().height() - location.y() - location.y()));
   cursor_view_.SetBoundsRect(location);
 }
 
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 88f29d8..a58d14f 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -3217,6 +3217,23 @@
             GetCursorBounds().height());
 }
 
+// Verify that cursor view height is independent of its parent view height.
+TEST_F(TextfieldTest, CursorViewHeightAtDiffDSF) {
+  InitTextfield();
+  textfield_->SetBounds(0, 0, 100, 100);
+  textfield_->SetCursorEnabled(true);
+  SendKeyEvent('a');
+  EXPECT_TRUE(test_api_->IsCursorVisible());
+  int height = test_api_->GetCursorViewRect().height();
+
+  // update the size of its parent view size and verify that the height of the
+  // cursor view stays the same.
+  View* parent = textfield_->parent();
+  parent->SetBounds(0, 0, 50, height - 2);
+  SendKeyEvent('b');
+  EXPECT_EQ(height, test_api_->GetCursorViewRect().height());
+}
+
 // Check if the text cursor is always at the end of the textfield after the
 // text overflows from the textfield. If the textfield size changes, check if
 // the text cursor's location is updated accordingly.
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index df8884e7..c31728ca 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -377,9 +377,8 @@
       // TODO(ccameron): Populate this based on this specific display.
       // http://crbug.com/735613
       if (!display::Display::HasForceColorProfile()) {
-        gfx::ICCProfile icc_profile = gfx::ICCProfile::FromBestMonitor();
-        icc_profile.HistogramDisplay(display.id());
-        display.set_color_space(icc_profile.GetColorSpace());
+        display.set_color_space(
+            gfx::ICCProfile::FromBestMonitor().GetColorSpace());
       }
 
       displays.push_back(display);
diff --git a/url/gurl.cc b/url/gurl.cc
index 45c45b10b..dbf09b8 100644
--- a/url/gurl.cc
+++ b/url/gurl.cc
@@ -509,14 +509,14 @@
 
 #endif  // WIN32
 
-bool GURL::DomainIs(base::StringPiece lower_ascii_domain) const {
+bool GURL::DomainIs(base::StringPiece canonical_domain) const {
   if (!is_valid_)
     return false;
 
   // FileSystem URLs have empty host_piece, so check this first.
-  if (SchemeIsFileSystem() && inner_url_)
-    return inner_url_->DomainIs(lower_ascii_domain);
-  return url::DomainIs(host_piece(), lower_ascii_domain);
+  if (inner_url_ && SchemeIsFileSystem())
+    return inner_url_->DomainIs(canonical_domain);
+  return url::DomainIs(host_piece(), canonical_domain);
 }
 
 bool GURL::EqualsIgnoringRef(const GURL& other) const {
diff --git a/url/gurl.h b/url/gurl.h
index 091ff68..2d9940b 100644
--- a/url/gurl.h
+++ b/url/gurl.h
@@ -397,11 +397,13 @@
   // "www.google.com", this will return true for "com", "google.com", and
   // "www.google.com".
   //
-  // The input domain should be lower-case ASCII to match the canonicalized
-  // scheme. This call is more efficient than getting the host and check
-  // whether host has the specific domain or not because no copies or
-  // object constructions are done.
-  bool DomainIs(base::StringPiece lower_ascii_domain) const;
+  // The input domain should match host canonicalization rules. i.e. the input
+  // show be lowercase except for escape chars.
+  //
+  // This call is more efficient than getting the host and checking whether the
+  // host has the specific domain or not because no copies or object
+  // constructions are done.
+  bool DomainIs(base::StringPiece canonical_domain) const;
 
   // Checks whether or not two URLs are differing only in the ref (the part
   // after the # character).
diff --git a/url/gurl_unittest.cc b/url/gurl_unittest.cc
index 464d08a..7545c97 100644
--- a/url/gurl_unittest.cc
+++ b/url/gurl_unittest.cc
@@ -658,6 +658,11 @@
   GURL invalid_url("google.com");
   EXPECT_FALSE(invalid_url.is_valid());
   EXPECT_FALSE(invalid_url.DomainIs("google.com"));
+
+  GURL url_with_escape_chars("https://www.,.test");
+  EXPECT_TRUE(url_with_escape_chars.is_valid());
+  EXPECT_EQ(url_with_escape_chars.host(), "www.%2C.test");
+  EXPECT_TRUE(url_with_escape_chars.DomainIs("%2C.test"));
 }
 
 TEST(GURLTest, DomainIsTerminatingDotBehavior) {
diff --git a/url/origin.cc b/url/origin.cc
index d15ba43f..81908281 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -170,8 +170,8 @@
   return GetPhysicalOrigin().IsSameOriginWith(other.GetPhysicalOrigin());
 }
 
-bool Origin::DomainIs(base::StringPiece lower_ascii_domain) const {
-  return !unique_ && url::DomainIs(tuple_.host(), lower_ascii_domain);
+bool Origin::DomainIs(base::StringPiece canonical_domain) const {
+  return !unique_ && url::DomainIs(tuple_.host(), canonical_domain);
 }
 
 bool Origin::operator<(const Origin& other) const {
diff --git a/url/origin.h b/url/origin.h
index c5bdf63..8b59b5a 100644
--- a/url/origin.h
+++ b/url/origin.h
@@ -161,7 +161,7 @@
   GURL GetURL() const;
 
   // Same as GURL::DomainIs. If |this| origin is unique, then returns false.
-  bool DomainIs(base::StringPiece lower_ascii_domain) const;
+  bool DomainIs(base::StringPiece canonical_domain) const;
 
   // Allows Origin to be used as a key in STL (for example, a std::set or
   // std::map).
diff --git a/url/url_util.cc b/url/url_util.cc
index c026453b..1942f79e 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -672,28 +672,27 @@
   return DoFindAndCompareScheme(str, str_len, compare, found_scheme);
 }
 
-bool DomainIs(base::StringPiece canonicalized_host,
-              base::StringPiece lower_ascii_domain) {
-  if (canonicalized_host.empty() || lower_ascii_domain.empty())
+bool DomainIs(base::StringPiece canonical_host,
+              base::StringPiece canonical_domain) {
+  if (canonical_host.empty() || canonical_domain.empty())
     return false;
 
   // If the host name ends with a dot but the input domain doesn't, then we
   // ignore the dot in the host name.
-  size_t host_len = canonicalized_host.length();
-  if (canonicalized_host.back() == '.' && lower_ascii_domain.back() != '.')
+  size_t host_len = canonical_host.length();
+  if (canonical_host.back() == '.' && canonical_domain.back() != '.')
     --host_len;
 
-  if (host_len < lower_ascii_domain.length())
+  if (host_len < canonical_domain.length())
     return false;
 
   // |host_first_pos| is the start of the compared part of the host name, not
   // start of the whole host name.
   const char* host_first_pos =
-      canonicalized_host.data() + host_len - lower_ascii_domain.length();
+      canonical_host.data() + host_len - canonical_domain.length();
 
-  if (!base::LowerCaseEqualsASCII(
-          base::StringPiece(host_first_pos, lower_ascii_domain.length()),
-          lower_ascii_domain)) {
+  if (base::StringPiece(host_first_pos, canonical_domain.length()) !=
+      canonical_domain) {
     return false;
   }
 
@@ -701,7 +700,7 @@
   // if the host name is longer than the input domain name, then the character
   // immediately before the compared part should be a dot. For example,
   // www.google.com has domain "google.com", but www.iamnotgoogle.com does not.
-  if (lower_ascii_domain[0] != '.' && host_len > lower_ascii_domain.length() &&
+  if (canonical_domain[0] != '.' && host_len > canonical_domain.length() &&
       *(host_first_pos - 1) != '.') {
     return false;
   }
diff --git a/url/url_util.h b/url/url_util.h
index 643c29d8..7486bf7 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -173,16 +173,16 @@
 
 // Hosts  ----------------------------------------------------------------------
 
-// Returns true if the |canonicalized_host| matches or is in the same domain as
-// the given |lower_ascii_domain| string. For example, if the canonicalized
-// hostname is "www.google.com", this will return true for "com", "google.com",
-// and "www.google.com" domains.
+// Returns true if the |canonical_host| matches or is in the same domain as the
+// given |canonical_domain| string. For example, if the canonicalized hostname
+// is "www.google.com", this will return true for "com", "google.com", and
+// "www.google.com" domains.
 //
 // If either of the input StringPieces is empty, the return value is false. The
-// input domain should be a lower-case ASCII string in order to match the
-// canonicalized host.
-URL_EXPORT bool DomainIs(base::StringPiece canonicalized_host,
-                         base::StringPiece lower_ascii_domain);
+// input domain should match host canonicalization rules. i.e. it should be
+// lowercase except for escape chars.
+URL_EXPORT bool DomainIs(base::StringPiece canonical_host,
+                         base::StringPiece canonical_domain);
 
 // Returns true if the hostname is an IP address. Note: this function isn't very
 // cheap, as it must re-parse the host to verify.