diff --git a/DEPS b/DEPS
index 75a906d..5f4c80fd 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # 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': '09e2ca1bb70f4735b300f293a8885d3be8fecfe2',
+  'v8_revision': 'b3ab49fff913807d151e289dc44b3fb91201997d',
   # 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.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '38ef7c8c97d1e28cb52816eedb823438c3057cee',
+  'angle_revision': 'a818c327fe6bb770c3a3621a985c4afd698ad0ab',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -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': 'db194cf018069b930d0e3d5fc0242e14f70e8620',
+  'pdfium_revision': '72f50f2021cd9f66016c962df8d27fc0c34748cd',
   # 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.
@@ -88,7 +88,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '25f277533941cf6c733ff70ae3e7713423ba60f2',
+  'nacl_revision': '9f8fdd668b305511b605ee229533639c29a5222b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype-android
   # 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': '71960b03052fb5fe3e75024533923e785a465a26',
+  'catapult_revision': '4ffd54d31cd52fdcc697562667985eac477a95ce',
   # 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/common/system/tray/tray_details_view.cc b/ash/common/system/tray/tray_details_view.cc
index cd84002..6e1b8739 100644
--- a/ash/common/system/tray/tray_details_view.cc
+++ b/ash/common/system/tray/tray_details_view.cc
@@ -49,8 +49,7 @@
 // above the top of the visible viewport until the next one "pushes" it up and
 // are painted above other children. To indicate that a child is a sticky header
 // row use set_id(VIEW_ID_STICKY_HEADER).
-class ScrollContentsView : public views::View,
-                           public views::ViewTargeterDelegate {
+class ScrollContentsView : public views::View {
  public:
   ScrollContentsView()
       : box_layout_(new views::BoxLayout(
@@ -58,7 +57,6 @@
             0,
             0,
             UseMd() ? 0 : kContentsBetweenChildSpacingNonMd)) {
-    SetEventTargeter(base::MakeUnique<views::ViewTargeter>(this));
     SetLayoutManager(box_layout_);
   }
   ~ScrollContentsView() override {}
@@ -70,17 +68,11 @@
   }
 
   void PaintChildren(const ui::PaintContext& context) override {
-    for (int i = 0; i < child_count(); ++i) {
-      if (child_at(i)->id() != VIEW_ID_STICKY_HEADER && !child_at(i)->layer())
-        child_at(i)->Paint(context);
-    }
+    views::View::PaintChildren(context);
     bool did_draw_shadow = false;
-    // Paint header rows above other children in Z-order.
-    for (auto& header : headers_) {
-      if (!header.view->layer())
-        header.view->Paint(context);
+    // Paint header row separators.
+    for (auto& header : headers_)
       did_draw_shadow = PaintDelineation(header, context) || did_draw_shadow;
-    }
 
     // Draw a shadow at the top of the viewport when scrolled, but only if a
     // header didn't already draw one. Overlap the shadow with the separator
@@ -101,24 +93,20 @@
     PositionHeaderRows();
   }
 
-  void ReorderChildLayers(ui::Layer* parent_layer) override {
-    views::View::ReorderChildLayers(parent_layer);
-    ui::Layer* topmost_layer = TopmostLayer(this, parent_layer);
-    if (!topmost_layer)
-      return;
-
-    // Keep the sticky headers with layers above the rest of the children's
-    // layers. Make sure to avoid changing the stacking order of the layers that
-    // are children of |parent_layer| but do not belong to the same parent view.
-    // Note: this assumes that all views that have id=VIEW_ID_STICKY_HEADER have
-    // a layer.
-    for (int i = child_count() - 1; i >= 0; --i) {
-      View* child = child_at(i);
-      if (child->id() == VIEW_ID_STICKY_HEADER &&
-          child->layer() != topmost_layer) {
-        parent_layer->StackAbove(child->layer(), topmost_layer);
-      }
+  View::Views GetChildrenInZOrder() override {
+    View::Views children;
+    // Iterate over regular children and later over the sticky headers to keep
+    // the sticky headers above in Z-order.
+    for (int i = 0; i < child_count(); ++i) {
+      if (child_at(i)->id() != VIEW_ID_STICKY_HEADER)
+        children.push_back(child_at(i));
     }
+    for (int i = 0; i < child_count(); ++i) {
+      if (child_at(i)->id() == VIEW_ID_STICKY_HEADER)
+        children.push_back(child_at(i));
+    }
+    DCHECK_EQ(child_count(), static_cast<int>(children.size()));
+    return children;
   }
 
   void ViewHierarchyChanged(
@@ -144,19 +132,6 @@
     }
   }
 
-  // views::ViewTargeterDelegate:
-  View* TargetForRect(View* root, const gfx::Rect& rect) override {
-    // Give header rows first dibs on events.
-    for (auto& header : headers_) {
-      views::View* view = header.view;
-      gfx::Rect local_to_header = rect;
-      local_to_header.Offset(-view->x(), -view->y());
-      if (ViewTargeterDelegate::DoesIntersectRect(view, local_to_header))
-        return ViewTargeterDelegate::TargetForRect(view, local_to_header);
-    }
-    return ViewTargeterDelegate::TargetForRect(root, rect);
-  }
-
  private:
   const SkColor kSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F);
   const int kShadowOffsetY = 2;
@@ -251,25 +226,6 @@
     canvas->DrawRect(shadowed_area, paint);
   }
 
-  // Recursively iterates through children to return the child layer that is
-  // stacked at the top. Only considers layers that are direct |parent_layer|'s
-  // children.
-  ui::Layer* TopmostLayer(views::View* view, ui::Layer* parent_layer) {
-    DCHECK(parent_layer);
-    // Iterate backwards through the children to find the topmost layer.
-    for (int i = view->child_count() - 1; i >= 0; --i) {
-      views::View* child = view->child_at(i);
-      ui::Layer* layer = TopmostLayer(child, parent_layer);
-      if (layer)
-        return layer;
-    }
-    if (view->layer() && view->layer() != parent_layer &&
-        view->layer()->parent() == parent_layer) {
-      return view->layer();
-    }
-    return nullptr;
-  }
-
   views::BoxLayout* box_layout_;
 
   // Header child views that stick to the top of visible viewport when scrolled.
diff --git a/ash/common/wm/window_cycle_controller.cc b/ash/common/wm/window_cycle_controller.cc
index 5a0ede9..17fb0448 100644
--- a/ash/common/wm/window_cycle_controller.cc
+++ b/ash/common/wm/window_cycle_controller.cc
@@ -78,6 +78,15 @@
                            window_list.size());
 }
 
+void WindowCycleController::CompleteCycling() {
+  window_cycle_list_->set_user_did_accept(true);
+  StopCycling();
+}
+
+void WindowCycleController::CancelCycling() {
+  StopCycling();
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // WindowCycleController, private:
 
diff --git a/ash/common/wm/window_cycle_controller.h b/ash/common/wm/window_cycle_controller.h
index 4544f49..87eedd9 100644
--- a/ash/common/wm/window_cycle_controller.h
+++ b/ash/common/wm/window_cycle_controller.h
@@ -45,10 +45,14 @@
   // listen to the alt key release.
   void StartCycling();
 
-  // Stops the current window cycle and removes the event filter.
-  void StopCycling();
+  // Both of these functions stop the current window cycle and removes the event
+  // filter. The former indicates success (i.e. the new window should be
+  // activated) and the latter indicates that the interaction was cancelled (and
+  // the originally active window should remain active).
+  void CompleteCycling();
+  void CancelCycling();
 
-  // Returns the WindowCycleList. Really only useful for testing.
+  // Returns the WindowCycleList.
   const WindowCycleList* window_cycle_list() const {
     return window_cycle_list_.get();
   }
@@ -57,6 +61,8 @@
   // Cycles to the next or previous window based on |direction|.
   void Step(Direction direction);
 
+  void StopCycling();
+
   std::unique_ptr<WindowCycleList> window_cycle_list_;
 
   // Tracks what Window was active when starting to cycle and used to determine
diff --git a/ash/common/wm/window_cycle_list.cc b/ash/common/wm/window_cycle_list.cc
index b8b1337a..6ba2e9a 100644
--- a/ash/common/wm/window_cycle_list.cc
+++ b/ash/common/wm/window_cycle_list.cc
@@ -39,17 +39,6 @@
 // Used for the highlight view and the shield (black background).
 constexpr float kBackgroundCornerRadius = 4.f;
 
-// Returns the window immediately below |window| in the current container.
-WmWindow* GetWindowBelow(WmWindow* window) {
-  WmWindow* parent = window->GetParent();
-  if (!parent)
-    return nullptr;
-  const WmWindow::Windows children = parent->GetChildren();
-  auto iter = std::find(children.begin(), children.end(), window);
-  CHECK(*iter == window);
-  return (iter != children.begin()) ? *(iter - 1) : nullptr;
-}
-
 // This background paints a |Painter| but fills the view's layer's size rather
 // than the view's size.
 class LayerFillBackgroundPainter : public views::Background {
@@ -72,36 +61,6 @@
 
 }  // namespace
 
-// This class restores and moves a window to the front of the stacking order for
-// the duration of the class's scope.
-class ScopedShowWindow : public WmWindowObserver {
- public:
-  ScopedShowWindow();
-  ~ScopedShowWindow() override;
-
-  // Show |window| at the top of the stacking order.
-  void Show(WmWindow* window);
-
-  // Cancel restoring the window on going out of scope.
-  void CancelRestore();
-
- private:
-  // WmWindowObserver:
-  void OnWindowTreeChanging(WmWindow* window,
-                            const TreeChangeParams& params) override;
-
-  // The window being shown.
-  WmWindow* window_;
-
-  // The window immediately below where window_ belongs.
-  WmWindow* stack_window_above_;
-
-  // If true, minimize window_ on going out of scope.
-  bool minimized_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow);
-};
-
 // This view represents a single WmWindow by displaying a title and a thumbnail
 // of the window's contents.
 class WindowPreviewView : public views::View, public WmWindowObserver {
@@ -392,7 +351,7 @@
   }
 
   void OnMouseCaptureLost() override {
-    WmShell::Get()->window_cycle_controller()->StopCycling();
+    WmShell::Get()->window_cycle_controller()->CancelCycling();
   }
 
   void OnPaintBackground(gfx::Canvas* canvas) override {
@@ -425,62 +384,8 @@
   DISALLOW_COPY_AND_ASSIGN(WindowCycleView);
 };
 
-ScopedShowWindow::ScopedShowWindow()
-    : window_(nullptr), stack_window_above_(nullptr), minimized_(false) {}
-
-ScopedShowWindow::~ScopedShowWindow() {
-  if (window_) {
-    window_->GetParent()->RemoveObserver(this);
-
-    // Restore window's stacking position.
-    if (stack_window_above_)
-      window_->GetParent()->StackChildAbove(window_, stack_window_above_);
-    else
-      window_->GetParent()->StackChildAtBottom(window_);
-
-    // Restore minimized state.
-    if (minimized_)
-      window_->GetWindowState()->Minimize();
-  }
-}
-
-void ScopedShowWindow::Show(WmWindow* window) {
-  DCHECK(!window_);
-  window_ = window;
-  stack_window_above_ = GetWindowBelow(window);
-  minimized_ = window->GetWindowState()->IsMinimized();
-  window_->GetParent()->AddObserver(this);
-  window_->Show();
-  window_->GetWindowState()->Activate();
-}
-
-void ScopedShowWindow::CancelRestore() {
-  if (!window_)
-    return;
-  window_->GetParent()->RemoveObserver(this);
-  window_ = stack_window_above_ = nullptr;
-}
-
-void ScopedShowWindow::OnWindowTreeChanging(WmWindow* window,
-                                            const TreeChangeParams& params) {
-  // Only interested in removal.
-  if (params.new_parent != nullptr)
-    return;
-
-  if (params.target == window_) {
-    CancelRestore();
-  } else if (params.target == stack_window_above_) {
-    // If the window this window was above is removed, use the next window down
-    // as the restore marker.
-    stack_window_above_ = GetWindowBelow(stack_window_above_);
-  }
-}
-
 WindowCycleList::WindowCycleList(const WindowList& windows)
     : windows_(windows),
-      current_index_(0),
-      cycle_view_(nullptr),
-      cycle_ui_widget_(nullptr),
       screen_observer_(this) {
   if (!ShouldShowUi())
     WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(true);
@@ -505,9 +410,7 @@
   for (WmWindow* window : windows_)
     window->RemoveObserver(this);
 
-  if (showing_window_) {
-    showing_window_->CancelRestore();
-  } else if (!windows_.empty()) {
+  if (!windows_.empty() && user_did_accept_) {
     WmWindow* target_window = windows_[current_index_];
     target_window->Show();
     target_window->GetWindowState()->Activate();
@@ -553,10 +456,6 @@
 
     if (cycle_view_)
       cycle_view_->SetTargetWindow(windows_[current_index_]);
-  } else {
-    // Make sure the next window is visible.
-    showing_window_.reset(new ScopedShowWindow);
-    showing_window_->Show(windows_[current_index_]);
   }
 }
 
@@ -584,7 +483,7 @@
     cycle_view_->HandleWindowDestruction(window, new_target_window);
     if (windows_.empty()) {
       // This deletes us.
-      WmShell::Get()->window_cycle_controller()->StopCycling();
+      WmShell::Get()->window_cycle_controller()->CancelCycling();
       return;
     }
   }
@@ -602,7 +501,7 @@
               ->GetDisplayNearestWindow(cycle_ui_widget_->GetNativeView())
               .id() &&
       (changed_metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION))) {
-    WmShell::Get()->window_cycle_controller()->StopCycling();
+    WmShell::Get()->window_cycle_controller()->CancelCycling();
     // |this| is deleted.
     return;
   }
diff --git a/ash/common/wm/window_cycle_list.h b/ash/common/wm/window_cycle_list.h
index d4bc8d06..c950a73 100644
--- a/ash/common/wm/window_cycle_list.h
+++ b/ash/common/wm/window_cycle_list.h
@@ -26,7 +26,6 @@
 
 namespace ash {
 
-class ScopedShowWindow;
 class WindowCycleView;
 
 // Tracks a set of Windows that can be stepped through. This class is used by
@@ -46,6 +45,10 @@
 
   int current_index() const { return current_index_; }
 
+  void set_user_did_accept(bool user_did_accept) {
+    user_did_accept_ = user_did_accept;
+  }
+
  private:
   friend class WindowCycleControllerTest;
 
@@ -79,19 +82,18 @@
 
   // Current position in the |windows_|. Can be used to query selection depth,
   // i.e., the position of an active window in a global MRU ordering.
-  int current_index_;
+  int current_index_ = 0;
 
-  // Wrapper for the window brought to the front.
-  // TODO(estade): remove ScopedShowWindow when we know we are happy launching
-  // the |cycle_view_| version.
-  std::unique_ptr<ScopedShowWindow> showing_window_;
+  // True if the user accepted the window switch (as opposed to cancelling or
+  // interrupting the interaction).
+  bool user_did_accept_ = false;
 
   // The top level View for the window cycle UI. May be null if the UI is not
   // showing.
-  WindowCycleView* cycle_view_;
+  WindowCycleView* cycle_view_ = nullptr;
 
   // The widget that hosts the window cycle UI.
-  views::Widget* cycle_ui_widget_;
+  views::Widget* cycle_ui_widget_ = nullptr;
 
   // The window list will dismiss if the display metrics change.
   ScopedObserver<display::Screen, display::DisplayObserver> screen_observer_;
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index 28ca6b1c..2223ec5e 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -170,13 +170,18 @@
   ASSERT_EQ(window1.get(), GetWindows(controller)[1]);
   ASSERT_EQ(window2.get(), GetWindows(controller)[2]);
 
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
 
   // Pressing and releasing Alt-tab again should cycle back to the most-
   // recently-used window in the current child order.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
+  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
+
+  // Cancelled cycling shouldn't move the active window.
+  controller->HandleCycleWindow(WindowCycleController::FORWARD);
+  controller->CancelCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
 
   // Pressing Alt-tab multiple times without releasing Alt should cycle through
@@ -190,7 +195,7 @@
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
   EXPECT_TRUE(controller->IsCycling());
 
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_FALSE(controller->IsCycling());
   EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
 
@@ -202,7 +207,7 @@
   // Likewise we can cycle backwards through the windows.
   controller->HandleCycleWindow(WindowCycleController::BACKWARD);
   controller->HandleCycleWindow(WindowCycleController::BACKWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
 
   // Reset our stacking order.
@@ -221,7 +226,7 @@
   EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
 
   // When a modal window is active, cycling window does not take effect.
@@ -259,13 +264,13 @@
   // Rotate focus, this should move focus to window0.
   WindowCycleController* controller = WmShell::Get()->window_cycle_controller();
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::GetWindowState(window0.get())->IsActive());
   EXPECT_FALSE(window1_state->IsActive());
 
   // One more time.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(window1_state->IsActive());
 }
 
@@ -284,14 +289,14 @@
   // Rotate focus, this should move focus to window1 and unminimize it.
   WindowCycleController* controller = WmShell::Get()->window_cycle_controller();
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_FALSE(window0_state->IsActive());
   EXPECT_FALSE(window1_state->IsMinimized());
   EXPECT_TRUE(window1_state->IsActive());
 
   // One more time back to w0.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(window0_state->IsActive());
 }
 
@@ -309,7 +314,7 @@
 
   WindowCycleController* controller = WmShell::Get()->window_cycle_controller();
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(window0_state->IsActive());
   EXPECT_FALSE(window0_state->IsMinimized());
   EXPECT_TRUE(window1_state->IsMinimized());
@@ -318,7 +323,7 @@
   window0_state->Minimize();
   window1_state->Minimize();
   controller->HandleCycleWindow(WindowCycleController::BACKWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(window0_state->IsMinimized());
   EXPECT_TRUE(window1_state->IsActive());
   EXPECT_FALSE(window1_state->IsMinimized());
@@ -347,7 +352,7 @@
   EXPECT_EQ(window2.get(), GetWindows(controller)[1]);
   EXPECT_EQ(window1.get(), GetWindows(controller)[2]);
 
-  controller->StopCycling();
+  controller->CompleteCycling();
 }
 
 TEST_F(WindowCycleControllerTest, AlwaysOnTopMultiWindow) {
@@ -375,7 +380,7 @@
   EXPECT_EQ(window2.get(), GetWindows(controller)[2]);
   EXPECT_EQ(window1.get(), GetWindows(controller)[3]);
 
-  controller->StopCycling();
+  controller->CompleteCycling();
 }
 
 TEST_F(WindowCycleControllerTest, AlwaysOnTopMultipleRootWindows) {
@@ -427,7 +432,7 @@
   EXPECT_EQ(window1.get(), GetWindows(controller)[2]);
   EXPECT_EQ(window0.get(), GetWindows(controller)[3]);
 
-  controller->StopCycling();
+  controller->CompleteCycling();
 }
 
 TEST_F(WindowCycleControllerTest, MostRecentlyUsed) {
@@ -453,17 +458,17 @@
 
   // Cycling through then stopping the cycling will activate a window.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
 
-  // Cycling alone (without StopCycling()) doesn't activate.
+  // Cycling alone (without CompleteCycling()) doesn't activate.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
   EXPECT_FALSE(wm::IsActiveWindow(window0.get()));
 
   // Showing the Alt+Tab UI does however deactivate the erstwhile active window.
   EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
 
-  controller->StopCycling();
+  controller->CompleteCycling();
 }
 
 // Tests that beginning window selection hides the app list.
@@ -482,7 +487,7 @@
   EXPECT_FALSE(wm::IsActiveWindow(window0.get()));
   EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
 
-  controller->StopCycling();
+  controller->CompleteCycling();
 }
 
 // Tests that cycling through windows doesn't change their minimized state.
@@ -504,7 +509,7 @@
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
   EXPECT_TRUE(IsWindowMinimized(window1.get()));
 
-  controller->StopCycling();
+  controller->CompleteCycling();
 
   EXPECT_TRUE(IsWindowMinimized(window1.get()));
 }
@@ -522,18 +527,18 @@
   EXPECT_TRUE(wm::IsActiveWindow(panel0.get()));
 
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(panel1.get()));
 
   // Cycling again should select the most recently used panel.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(panel0.get()));
 
   // Cycling twice again should select the first window.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
 }
 
@@ -560,7 +565,7 @@
   panel1.reset();
   // Cycling again should now select window2.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
 }
 
@@ -586,7 +591,7 @@
   panel0.reset();
   // Cycling again should now select panel1.
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  controller->StopCycling();
+  controller->CompleteCycling();
   EXPECT_TRUE(wm::IsActiveWindow(panel1.get()));
 }
 
@@ -637,7 +642,7 @@
   EXPECT_EQ(0, event_count.GetMouseEventCountAndReset());
 
   // Stop cycling: once again, events get through.
-  controller->StopCycling();
+  controller->CompleteCycling();
   generator.ClickLeftButton();
   EXPECT_LT(0, event_count.GetMouseEventCountAndReset());
 }
diff --git a/ash/wm/window_cycle_event_filter_aura.cc b/ash/wm/window_cycle_event_filter_aura.cc
index fbafa859..e9879b52 100644
--- a/ash/wm/window_cycle_event_filter_aura.cc
+++ b/ash/wm/window_cycle_event_filter_aura.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/window_cycle_event_filter_aura.h"
 
 #include "ash/common/wm/window_cycle_controller.h"
+#include "ash/common/wm/window_cycle_list.h"
 #include "ash/common/wm_shell.h"
 #include "ash/shell.h"
 #include "ui/events/event.h"
@@ -30,7 +31,7 @@
   // Views uses VKEY_MENU for both left and right Alt keys.
   if (event->key_code() == ui::VKEY_MENU &&
       event->type() == ui::ET_KEY_RELEASED) {
-    WmShell::Get()->window_cycle_controller()->StopCycling();
+    WmShell::Get()->window_cycle_controller()->CompleteCycling();
     // Warning: |this| will be deleted from here on.
   } else if (event->key_code() == ui::VKEY_TAB) {
     if (event->type() == ui::ET_KEY_RELEASED) {
@@ -46,7 +47,7 @@
                                    : WindowCycleController::FORWARD));
     }
   } else if (event->key_code() == ui::VKEY_ESCAPE) {
-    WmShell::Get()->window_cycle_controller()->StopCycling();
+    WmShell::Get()->window_cycle_controller()->CancelCycling();
   }
 }
 
diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h
index 32f0dfdd..f5007db3 100644
--- a/base/numerics/safe_math.h
+++ b/base/numerics/safe_math.h
@@ -204,27 +204,26 @@
   template <typename Src>
   CheckedNumeric& operator^=(const Src rhs);
 
-  CheckedNumeric operator-() const {
-    // Negation is always valid for floating point.
-    T value = 0;
-    bool is_valid = (std::is_floating_point<T>::value || IsValid()) &&
-                    CheckedNeg(state_.value(), &value);
-    return CheckedNumeric<T>(value, is_valid);
+  constexpr CheckedNumeric operator-() const {
+    return CheckedNumeric<T>(
+        NegateWrapper(state_.value()),
+        IsValid() &&
+            (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
+             NegateWrapper(state_.value()) !=
+                 std::numeric_limits<T>::lowest()));
   }
 
-  CheckedNumeric operator~() const {
-    static_assert(!std::is_signed<T>::value, "Type must be unsigned.");
-    T value = 0;
-    bool is_valid = IsValid() && CheckedInv(state_.value(), &value);
-    return CheckedNumeric<T>(value, is_valid);
+  constexpr CheckedNumeric operator~() const {
+    return CheckedNumeric<decltype(InvertWrapper(T()))>(
+        InvertWrapper(state_.value()), IsValid());
   }
 
-  CheckedNumeric Abs() const {
-    // Absolute value is always valid for floating point.
-    T value = 0;
-    bool is_valid = (std::is_floating_point<T>::value || IsValid()) &&
-                    CheckedAbs(state_.value(), &value);
-    return CheckedNumeric<T>(value, is_valid);
+  constexpr CheckedNumeric Abs() const {
+    return CheckedNumeric<T>(
+        AbsWrapper(state_.value()),
+        IsValid() &&
+            (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
+             AbsWrapper(state_.value()) != std::numeric_limits<T>::lowest()));
   }
 
   template <typename U>
diff --git a/base/numerics/safe_math_impl.h b/base/numerics/safe_math_impl.h
index 9956115..a224f69 100644
--- a/base/numerics/safe_math_impl.h
+++ b/base/numerics/safe_math_impl.h
@@ -442,54 +442,6 @@
   }
 };
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  std::is_signed<T>::value>::type* = nullptr>
-bool CheckedNeg(T value, T* result) {
-  // The negation of signed min is min, so catch that one.
-  if (value != std::numeric_limits<T>::lowest()) {
-    *result = static_cast<T>(-value);
-    return true;
-  }
-  return false;
-}
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  !std::is_signed<T>::value>::type* = nullptr>
-bool CheckedNeg(T value, T* result) {
-  if (!value) {
-    *result = static_cast<T>(0);
-    return true;
-  }
-  return false;
-}
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  !std::is_signed<T>::value>::type* = nullptr>
-bool CheckedInv(T value, T* result) {
-  *result = ~value;
-  return true;
-}
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  std::is_signed<T>::value>::type* = nullptr>
-bool CheckedAbs(T value, T* result) {
-  *result = static_cast<T>(SafeUnsignedAbs(value));
-  return *result != std::numeric_limits<T>::lowest();
-}
-
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  !std::is_signed<T>::value>::type* = nullptr>
-bool CheckedAbs(T value, T* result) {
-  // T is unsigned, so |value| must already be positive.
-  *result = value;
-  return true;
-}
-
 // This is just boilerplate that wraps the standard floating point arithmetic.
 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                                    \
@@ -514,20 +466,43 @@
 
 #undef BASE_FLOAT_ARITHMETIC_OPS
 
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-bool CheckedNeg(T value, T* result) {
-  *result = static_cast<T>(-value);
-  return true;
+// Wrap the unary operations to allow SFINAE when instantiating integrals versus
+// floating points. These don't perform any overflow checking. Rather, they
+// exhibit well-defined overflow semantics and rely on the caller to detect
+// if an overflow occured.
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+  using UnsignedT = typename std::make_unsigned<T>::type;
+  // This will compile to a NEG on Intel, and is normal negation on ARM.
+  return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
 }
 
 template <
     typename T,
     typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-bool CheckedAbs(T value, T* result) {
-  *result = static_cast<T>(std::abs(value));
-  return true;
+constexpr T NegateWrapper(T value) {
+  return -value;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
+  return ~value;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+  return static_cast<T>(SafeUnsignedAbs(value));
+}
+
+template <
+    typename T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+  return value < 0 ? -value : value;
 }
 
 // Floats carry around their validity state with them, but integers do not. So,
diff --git a/build/android/pylib/local/device/local_device_perf_test_run.py b/build/android/pylib/local/device/local_device_perf_test_run.py
index b34c360..0dbafd5 100644
--- a/build/android/pylib/local/device/local_device_perf_test_run.py
+++ b/build/android/pylib/local/device/local_device_perf_test_run.py
@@ -252,7 +252,12 @@
           self._TestTearDown()
           if result_type != base_test_result.ResultType.PASS:
             try:
-              device_recovery.RecoverDevice(self._device, self._env.blacklist)
+              # TODO(rnephew): Possible problem when restarting on N7 devices.
+              # Determine if this is true. crbug.com/667470
+              if 'Nexus 7' not in self._device.product_model:
+                device_recovery.RecoverDevice(self._device, self._env.blacklist)
+              else:
+                logging.critical('Not attempting device recovery.')
             except device_errors.CommandTimeoutError:
               logging.exception(
                   'Device failed to recover after failing %s.', test)
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index 0918a7c22..1a7332d 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <vector>
 
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
@@ -765,12 +766,7 @@
     backdrop_rect_location_ = locations[POS + 2];          \
   }
 
-FragmentShaderBase::FragmentShaderBase()
-    : backdrop_location_(-1),
-      original_backdrop_location_(-1),
-      backdrop_rect_location_(-1),
-      blend_mode_(BLEND_MODE_NONE),
-      mask_for_background_(false) {}
+FragmentShaderBase::FragmentShaderBase() {}
 
 std::string FragmentShaderBase::GetShaderString(TexCoordPrecision precision,
                                                 SamplerType sampler) const {
@@ -779,6 +775,91 @@
                      sampler, SetBlendModeFunctions(GetShaderSource())));
 }
 
+void FragmentShaderBase::Init(GLES2Interface* context,
+                              unsigned program,
+                              int* base_uniform_index) {
+  std::vector<const char*> uniforms;
+  std::vector<int> locations;
+  if (has_blend_mode()) {
+    uniforms.push_back("s_backdropTexture");
+    uniforms.push_back("s_originalBackdropTexture");
+    uniforms.push_back("backdropRect");
+  }
+  if (has_mask_sampler_) {
+    uniforms.push_back("s_mask");
+    uniforms.push_back("maskTexCoordScale");
+    uniforms.push_back("maskTexCoordOffset");
+  }
+  if (has_color_matrix_) {
+    uniforms.push_back("colorMatrix");
+    uniforms.push_back("colorOffset");
+  }
+  if (has_sampler_)
+    uniforms.push_back("s_texture");
+  if (has_uniform_alpha_)
+    uniforms.push_back("alpha");
+  if (has_background_color_)
+    uniforms.push_back("background_color");
+  if (has_fragment_tex_transform_)
+    uniforms.push_back("fragmentTexTransform");
+  if (has_uniform_color_)
+    uniforms.push_back("color");
+
+  locations.resize(uniforms.size());
+
+  GetProgramUniformLocations(context, program, uniforms.size(), uniforms.data(),
+                             locations.data(), base_uniform_index);
+
+  size_t index = 0;
+  if (has_blend_mode()) {
+    backdrop_location_ = locations[index++];
+    original_backdrop_location_ = locations[index++];
+    backdrop_rect_location_ = locations[index++];
+  }
+  if (has_mask_sampler_) {
+    mask_sampler_location_ = locations[index++];
+    mask_tex_coord_scale_location_ = locations[index++];
+    mask_tex_coord_offset_location_ = locations[index++];
+  }
+  if (has_color_matrix_) {
+    color_matrix_location_ = locations[index++];
+    color_offset_location_ = locations[index++];
+  }
+  if (has_sampler_)
+    sampler_location_ = locations[index++];
+  if (has_uniform_alpha_)
+    alpha_location_ = locations[index++];
+  if (has_background_color_)
+    background_color_location_ = locations[index++];
+  if (has_fragment_tex_transform_)
+    fragment_tex_transform_location_ = locations[index++];
+  if (has_uniform_color_)
+    color_location_ = locations[index++];
+  DCHECK_EQ(index, locations.size());
+}
+
+void FragmentShaderBase::FillLocations(ShaderLocations* locations) const {
+  if (has_blend_mode()) {
+    locations->backdrop = backdrop_location_;
+    locations->backdrop_rect = backdrop_rect_location_;
+  }
+  if (mask_for_background())
+    locations->original_backdrop = original_backdrop_location_;
+  if (has_mask_sampler_) {
+    locations->mask_sampler = mask_sampler_location_;
+    locations->mask_tex_coord_scale = mask_tex_coord_scale_location_;
+    locations->mask_tex_coord_offset = mask_tex_coord_offset_location_;
+  }
+  if (has_color_matrix_) {
+    locations->color_matrix = color_matrix_location_;
+    locations->color_offset = color_offset_location_;
+  }
+  if (has_sampler_)
+    locations->sampler = sampler_location_;
+  if (has_uniform_alpha_)
+    locations->alpha = alpha_location_;
+}
+
 std::string FragmentShaderBase::SetBlendModeFunctions(
     const std::string& shader_string) const {
   if (shader_string.find("ApplyBlendMode") == std::string::npos)
@@ -1074,77 +1155,6 @@
   return "result = vec4(1.0, 0.0, 0.0, 1.0);";
 }
 
-FragmentTexAlphaBinding::FragmentTexAlphaBinding()
-    : sampler_location_(-1), alpha_location_(-1) {
-}
-
-void FragmentTexAlphaBinding::Init(GLES2Interface* context,
-                                   unsigned program,
-                                   int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture", "alpha", BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  alpha_location_ = locations[1];
-  BLEND_MODE_SET_LOCATIONS(locations, 2);
-}
-
-FragmentTexColorMatrixAlphaBinding::FragmentTexColorMatrixAlphaBinding()
-    : sampler_location_(-1),
-      alpha_location_(-1),
-      color_matrix_location_(-1),
-      color_offset_location_(-1) {
-}
-
-void FragmentTexColorMatrixAlphaBinding::Init(GLES2Interface* context,
-                                              unsigned program,
-                                              int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture", "alpha", "colorMatrix", "colorOffset", BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  alpha_location_ = locations[1];
-  color_matrix_location_ = locations[2];
-  color_offset_location_ = locations[3];
-  BLEND_MODE_SET_LOCATIONS(locations, 4);
-}
-
-FragmentTexOpaqueBinding::FragmentTexOpaqueBinding() : sampler_location_(-1) {
-}
-
-void FragmentTexOpaqueBinding::Init(GLES2Interface* context,
-                                    unsigned program,
-                                    int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-}
-
 std::string FragmentShaderRGBATexAlpha::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1158,14 +1168,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlpha::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->alpha = alpha_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-}
-
 std::string FragmentShaderRGBATexColorMatrixAlpha::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1186,16 +1188,6 @@
   });
 }
 
-void FragmentShaderRGBATexColorMatrixAlpha::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->alpha = alpha_location();
-  locations->color_matrix = color_matrix_location();
-  locations->color_offset = color_offset_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-}
-
 std::string FragmentShaderRGBATexVaryingAlpha::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1223,32 +1215,6 @@
   });
 }
 
-FragmentTexBackgroundBinding::FragmentTexBackgroundBinding()
-    : background_color_location_(-1), sampler_location_(-1) {
-}
-
-void FragmentTexBackgroundBinding::Init(GLES2Interface* context,
-                                        unsigned program,
-                                        int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture", "background_color",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-
-  sampler_location_ = locations[0];
-  DCHECK_NE(sampler_location_, -1);
-
-  background_color_location_ = locations[1];
-  DCHECK_NE(background_color_location_, -1);
-}
-
 std::string FragmentShaderTexBackgroundVaryingAlpha::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1328,29 +1294,6 @@
   });
 }
 
-FragmentShaderRGBATexAlphaAA::FragmentShaderRGBATexAlphaAA()
-    : sampler_location_(-1), alpha_location_(-1) {
-}
-
-void FragmentShaderRGBATexAlphaAA::Init(GLES2Interface* context,
-                                        unsigned program,
-                                        int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture", "alpha", BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  alpha_location_ = locations[1];
-  BLEND_MODE_SET_LOCATIONS(locations, 2);
-}
-
 std::string FragmentShaderRGBATexAlphaAA::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1368,39 +1311,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlphaAA::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->alpha = alpha_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-}
-
-FragmentTexClampAlphaAABinding::FragmentTexClampAlphaAABinding()
-    : sampler_location_(-1),
-      alpha_location_(-1),
-      fragment_tex_transform_location_(-1) {
-}
-
-void FragmentTexClampAlphaAABinding::Init(GLES2Interface* context,
-                                          unsigned program,
-                                          int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture", "alpha", "fragmentTexTransform",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  alpha_location_ = locations[1];
-  fragment_tex_transform_location_ = locations[2];
-}
-
 std::string FragmentShaderRGBATexClampAlphaAA::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1444,40 +1354,6 @@
   });
 }
 
-FragmentShaderRGBATexAlphaMask::FragmentShaderRGBATexAlphaMask()
-    : sampler_location_(-1),
-      mask_sampler_location_(-1),
-      alpha_location_(-1),
-      mask_tex_coord_scale_location_(-1) {
-}
-
-void FragmentShaderRGBATexAlphaMask::Init(GLES2Interface* context,
-                                          unsigned program,
-                                          int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture",
-      "s_mask",
-      "alpha",
-      "maskTexCoordScale",
-      "maskTexCoordOffset",
-      BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  mask_sampler_location_ = locations[1];
-  alpha_location_ = locations[2];
-  mask_tex_coord_scale_location_ = locations[3];
-  mask_tex_coord_offset_location_ = locations[4];
-  BLEND_MODE_SET_LOCATIONS(locations, 5);
-}
-
 std::string FragmentShaderRGBATexAlphaMask::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1499,54 +1375,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlphaMask::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->mask_sampler = mask_sampler_location();
-  locations->mask_tex_coord_scale = mask_tex_coord_scale_location();
-  locations->mask_tex_coord_offset = mask_tex_coord_offset_location();
-  locations->alpha = alpha_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-  if (mask_for_background())
-    locations->original_backdrop = original_backdrop_location();
-}
-
-FragmentShaderRGBATexAlphaMaskAA::FragmentShaderRGBATexAlphaMaskAA()
-    : sampler_location_(-1),
-      mask_sampler_location_(-1),
-      alpha_location_(-1),
-      mask_tex_coord_scale_location_(-1),
-      mask_tex_coord_offset_location_(-1) {
-}
-
-void FragmentShaderRGBATexAlphaMaskAA::Init(GLES2Interface* context,
-                                            unsigned program,
-                                            int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture",
-      "s_mask",
-      "alpha",
-      "maskTexCoordScale",
-      "maskTexCoordOffset",
-      BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  mask_sampler_location_ = locations[1];
-  alpha_location_ = locations[2];
-  mask_tex_coord_scale_location_ = locations[3];
-  mask_tex_coord_offset_location_ = locations[4];
-  BLEND_MODE_SET_LOCATIONS(locations, 5);
-}
-
 std::string FragmentShaderRGBATexAlphaMaskAA::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1572,61 +1400,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlphaMaskAA::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->mask_sampler = mask_sampler_location();
-  locations->mask_tex_coord_scale = mask_tex_coord_scale_location();
-  locations->mask_tex_coord_offset = mask_tex_coord_offset_location();
-  locations->alpha = alpha_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-  if (mask_for_background())
-    locations->original_backdrop = original_backdrop_location();
-}
-
-FragmentShaderRGBATexAlphaMaskColorMatrixAA::
-    FragmentShaderRGBATexAlphaMaskColorMatrixAA()
-    : sampler_location_(-1),
-      mask_sampler_location_(-1),
-      alpha_location_(-1),
-      mask_tex_coord_scale_location_(-1),
-      color_matrix_location_(-1),
-      color_offset_location_(-1) {
-}
-
-void FragmentShaderRGBATexAlphaMaskColorMatrixAA::Init(
-    GLES2Interface* context,
-    unsigned program,
-    int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture",
-      "s_mask",
-      "alpha",
-      "maskTexCoordScale",
-      "maskTexCoordOffset",
-      "colorMatrix",
-      "colorOffset",
-      BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  mask_sampler_location_ = locations[1];
-  alpha_location_ = locations[2];
-  mask_tex_coord_scale_location_ = locations[3];
-  mask_tex_coord_offset_location_ = locations[4];
-  color_matrix_location_ = locations[5];
-  color_offset_location_ = locations[6];
-  BLEND_MODE_SET_LOCATIONS(locations, 7);
-}
-
 std::string FragmentShaderRGBATexAlphaMaskColorMatrixAA::GetShaderSource()
     const {
   return SHADER0([]() {
@@ -1660,50 +1433,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlphaMaskColorMatrixAA::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->alpha = alpha_location();
-  locations->mask_sampler = mask_sampler_location();
-  locations->mask_tex_coord_scale = mask_tex_coord_scale_location();
-  locations->mask_tex_coord_offset = mask_tex_coord_offset_location();
-  locations->color_matrix = color_matrix_location();
-  locations->color_offset = color_offset_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-  if (mask_for_background())
-    locations->original_backdrop = original_backdrop_location();
-}
-
-FragmentShaderRGBATexAlphaColorMatrixAA::
-    FragmentShaderRGBATexAlphaColorMatrixAA()
-    : sampler_location_(-1),
-      alpha_location_(-1),
-      color_matrix_location_(-1),
-      color_offset_location_(-1) {
-}
-
-void FragmentShaderRGBATexAlphaColorMatrixAA::Init(GLES2Interface* context,
-                                                   unsigned program,
-                                                   int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture", "alpha", "colorMatrix", "colorOffset", BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  alpha_location_ = locations[1];
-  color_matrix_location_ = locations[2];
-  color_offset_location_ = locations[3];
-  BLEND_MODE_SET_LOCATIONS(locations, 4);
-}
-
 std::string FragmentShaderRGBATexAlphaColorMatrixAA::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1728,55 +1457,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlphaColorMatrixAA::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->alpha = alpha_location();
-  locations->color_matrix = color_matrix_location();
-  locations->color_offset = color_offset_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-}
-
-FragmentShaderRGBATexAlphaMaskColorMatrix::
-    FragmentShaderRGBATexAlphaMaskColorMatrix()
-    : sampler_location_(-1),
-      mask_sampler_location_(-1),
-      alpha_location_(-1),
-      mask_tex_coord_scale_location_(-1) {
-}
-
-void FragmentShaderRGBATexAlphaMaskColorMatrix::Init(GLES2Interface* context,
-                                                     unsigned program,
-                                                     int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "s_texture",
-      "s_mask",
-      "alpha",
-      "maskTexCoordScale",
-      "maskTexCoordOffset",
-      "colorMatrix",
-      "colorOffset",
-      BLEND_MODE_UNIFORMS,
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS,
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  sampler_location_ = locations[0];
-  mask_sampler_location_ = locations[1];
-  alpha_location_ = locations[2];
-  mask_tex_coord_scale_location_ = locations[3];
-  mask_tex_coord_offset_location_ = locations[4];
-  color_matrix_location_ = locations[5];
-  color_offset_location_ = locations[6];
-  BLEND_MODE_SET_LOCATIONS(locations, 7);
-}
-
 std::string FragmentShaderRGBATexAlphaMaskColorMatrix::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -1805,21 +1485,6 @@
   });
 }
 
-void FragmentShaderRGBATexAlphaMaskColorMatrix::FillLocations(
-    ShaderLocations* locations) const {
-  locations->sampler = sampler_location();
-  locations->mask_sampler = mask_sampler_location();
-  locations->mask_tex_coord_scale = mask_tex_coord_scale_location();
-  locations->mask_tex_coord_offset = mask_tex_coord_offset_location();
-  locations->alpha = alpha_location();
-  locations->color_matrix = color_matrix_location();
-  locations->color_offset = color_offset_location();
-  locations->backdrop = backdrop_location();
-  locations->backdrop_rect = backdrop_rect_location();
-  if (mask_for_background())
-    locations->original_backdrop = original_backdrop_location();
-}
-
 FragmentShaderYUVVideo::FragmentShaderYUVVideo()
     : y_texture_location_(-1),
       u_texture_location_(-1),
@@ -1982,26 +1647,6 @@
   return head + functions;
 }
 
-FragmentShaderColor::FragmentShaderColor() : color_location_(-1) {
-}
-
-void FragmentShaderColor::Init(GLES2Interface* context,
-                               unsigned program,
-                               int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "color",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  color_location_ = locations[0];
-}
-
 std::string FragmentShaderColor::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
@@ -2010,26 +1655,6 @@
   });
 }
 
-FragmentShaderColorAA::FragmentShaderColorAA() : color_location_(-1) {
-}
-
-void FragmentShaderColorAA::Init(GLES2Interface* context,
-                                 unsigned program,
-                                 int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "color",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  color_location_ = locations[0];
-}
-
 std::string FragmentShaderColorAA::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
diff --git a/cc/output/shader.h b/cc/output/shader.h
index fdccb593..a74a58f 100644
--- a/cc/output/shader.h
+++ b/cc/output/shader.h
@@ -356,12 +356,12 @@
 
 class FragmentShaderBase {
  public:
+  virtual void Init(gpu::gles2::GLES2Interface* context,
+                    unsigned program,
+                    int* base_uniform_index);
   std::string GetShaderString(TexCoordPrecision precision,
                               SamplerType sampler) const;
-
-  int backdrop_location() const { return backdrop_location_; }
-  int original_backdrop_location() const { return original_backdrop_location_; }
-  int backdrop_rect_location() const { return backdrop_rect_location_; }
+  void FillLocations(ShaderLocations* locations) const;
 
   BlendMode blend_mode() const { return blend_mode_; }
   void set_blend_mode(BlendMode blend_mode) { blend_mode_ = blend_mode; }
@@ -371,19 +371,52 @@
   }
   bool mask_for_background() const { return mask_for_background_; }
 
+  int sampler_location() const { return sampler_location_; }
+  int alpha_location() const { return alpha_location_; }
+  int color_location() const { return color_location_; }
+  int background_color_location() const { return background_color_location_; }
+  int fragment_tex_transform_location() const {
+    return fragment_tex_transform_location_;
+  }
+
  protected:
   FragmentShaderBase();
   virtual std::string GetShaderSource() const = 0;
 
   std::string SetBlendModeFunctions(const std::string& shader_string) const;
 
-  int backdrop_location_;
-  int original_backdrop_location_;
-  int backdrop_rect_location_;
+  // Used only if |blend_mode_| is not BLEND_MODE_NONE.
+  int backdrop_location_ = -1;
+  int original_backdrop_location_ = -1;
+  int backdrop_rect_location_ = -1;
+
+  bool has_mask_sampler_ = false;
+  int mask_sampler_location_ = -1;
+  int mask_tex_coord_scale_location_ = -1;
+  int mask_tex_coord_offset_location_ = -1;
+
+  bool has_color_matrix_ = false;
+  int color_matrix_location_ = -1;
+  int color_offset_location_ = -1;
+
+  bool has_sampler_ = false;
+  int sampler_location_ = -1;
+
+  bool has_uniform_alpha_ = false;
+  int alpha_location_ = -1;
+
+  bool has_background_color_ = false;
+  int background_color_location_ = -1;
+
+  bool has_fragment_tex_transform_ = false;
+  int fragment_tex_transform_location_ = -1;
+
+  bool has_uniform_color_ = false;
+  int color_location_ = -1;
 
  private:
-  BlendMode blend_mode_;
-  bool mask_for_background_;
+  BlendMode blend_mode_ = BLEND_MODE_NONE;
+  bool mask_for_background_ = false;
 
   std::string GetHelperFunctions() const;
   std::string GetBlendFunction() const;
@@ -392,74 +425,42 @@
 
 class FragmentTexAlphaBinding : public FragmentShaderBase {
  public:
-  FragmentTexAlphaBinding();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int fragment_tex_transform_location() const { return -1; }
-  int sampler_location() const { return sampler_location_; }
+  FragmentTexAlphaBinding() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+  }
 
  private:
-  int sampler_location_;
-  int alpha_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentTexAlphaBinding);
 };
 
 class FragmentTexColorMatrixAlphaBinding : public FragmentShaderBase {
  public:
-    FragmentTexColorMatrixAlphaBinding();
-
-    void Init(gpu::gles2::GLES2Interface* context,
-              unsigned program,
-              int* base_uniform_index);
-    int alpha_location() const { return alpha_location_; }
-    int color_matrix_location() const { return color_matrix_location_; }
-    int color_offset_location() const { return color_offset_location_; }
-    int fragment_tex_transform_location() const { return -1; }
-    int sampler_location() const { return sampler_location_; }
+  FragmentTexColorMatrixAlphaBinding() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_color_matrix_ = true;
+  }
 
  private:
-    int sampler_location_;
-    int alpha_location_;
-    int color_matrix_location_;
-    int color_offset_location_;
 };
 
 class FragmentTexOpaqueBinding : public FragmentShaderBase {
  public:
-  FragmentTexOpaqueBinding();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return -1; }
-  int fragment_tex_transform_location() const { return -1; }
-  int background_color_location() const { return -1; }
-  int sampler_location() const { return sampler_location_; }
+  FragmentTexOpaqueBinding() { has_sampler_ = true; }
 
  private:
-  int sampler_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentTexOpaqueBinding);
 };
 
 class FragmentTexBackgroundBinding : public FragmentShaderBase {
  public:
-  FragmentTexBackgroundBinding();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int background_color_location() const { return background_color_location_; }
-  int sampler_location() const { return sampler_location_; }
+  FragmentTexBackgroundBinding() {
+    has_sampler_ = true;
+    has_background_color_ = true;
+  }
 
  private:
-  int background_color_location_;
-  int sampler_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentTexBackgroundBinding);
 };
 
@@ -486,18 +487,12 @@
 };
 
 class FragmentShaderRGBATexAlpha : public FragmentTexAlphaBinding {
- public:
-  void FillLocations(ShaderLocations* locations) const;
-
  private:
   std::string GetShaderSource() const override;
 };
 
 class FragmentShaderRGBATexColorMatrixAlpha
     : public FragmentTexColorMatrixAlphaBinding {
- public:
-  void FillLocations(ShaderLocations* locations) const;
-
  private:
   std::string GetShaderSource() const override;
 };
@@ -526,42 +521,24 @@
 
 class FragmentShaderRGBATexAlphaAA : public FragmentShaderBase {
  public:
-  FragmentShaderRGBATexAlphaAA();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  void FillLocations(ShaderLocations* locations) const;
-
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
+  FragmentShaderRGBATexAlphaAA() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+  }
 
  private:
   std::string GetShaderSource() const override;
-  int sampler_location_;
-  int alpha_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaAA);
 };
 
 class FragmentTexClampAlphaAABinding : public FragmentShaderBase {
  public:
-  FragmentTexClampAlphaAABinding();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
-  int fragment_tex_transform_location() const {
-    return fragment_tex_transform_location_;
+  FragmentTexClampAlphaAABinding() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_fragment_tex_transform_ = true;
   }
-
  private:
-  int sampler_location_;
-  int alpha_location_;
-  int fragment_tex_transform_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentTexClampAlphaAABinding);
 };
 
@@ -580,138 +557,62 @@
 
 class FragmentShaderRGBATexAlphaMask : public FragmentShaderBase {
  public:
-  FragmentShaderRGBATexAlphaMask();
-  void FillLocations(ShaderLocations* locations) const;
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
-  int mask_sampler_location() const { return mask_sampler_location_; }
-  int mask_tex_coord_scale_location() const {
-    return mask_tex_coord_scale_location_;
+  FragmentShaderRGBATexAlphaMask() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_mask_sampler_ = true;
   }
-  int mask_tex_coord_offset_location() const {
-    return mask_tex_coord_offset_location_;
-  }
-
  private:
   std::string GetShaderSource() const override;
-  int sampler_location_;
-  int mask_sampler_location_;
-  int alpha_location_;
-  int mask_tex_coord_scale_location_;
-  int mask_tex_coord_offset_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaMask);
 };
 
 class FragmentShaderRGBATexAlphaMaskAA : public FragmentShaderBase {
  public:
-  FragmentShaderRGBATexAlphaMaskAA();
-  void FillLocations(ShaderLocations* locations) const;
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
-  int mask_sampler_location() const { return mask_sampler_location_; }
-  int mask_tex_coord_scale_location() const {
-    return mask_tex_coord_scale_location_;
+  FragmentShaderRGBATexAlphaMaskAA() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_mask_sampler_ = true;
   }
-  int mask_tex_coord_offset_location() const {
-    return mask_tex_coord_offset_location_;
-  }
-
  private:
   std::string GetShaderSource() const override;
-  int sampler_location_;
-  int mask_sampler_location_;
-  int alpha_location_;
-  int mask_tex_coord_scale_location_;
-  int mask_tex_coord_offset_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaMaskAA);
 };
 
 class FragmentShaderRGBATexAlphaMaskColorMatrixAA : public FragmentShaderBase {
  public:
-  FragmentShaderRGBATexAlphaMaskColorMatrixAA();
-  void FillLocations(ShaderLocations* locations) const;
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
-  int mask_sampler_location() const { return mask_sampler_location_; }
-  int mask_tex_coord_scale_location() const {
-    return mask_tex_coord_scale_location_;
+  FragmentShaderRGBATexAlphaMaskColorMatrixAA() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_mask_sampler_ = true;
+    has_color_matrix_ = true;
   }
-  int mask_tex_coord_offset_location() const {
-    return mask_tex_coord_offset_location_;
-  }
-  int color_matrix_location() const { return color_matrix_location_; }
-  int color_offset_location() const { return color_offset_location_; }
-
  private:
   std::string GetShaderSource() const override;
-  int sampler_location_;
-  int mask_sampler_location_;
-  int alpha_location_;
-  int mask_tex_coord_scale_location_;
-  int mask_tex_coord_offset_location_;
-  int color_matrix_location_;
-  int color_offset_location_;
 };
 
 class FragmentShaderRGBATexAlphaColorMatrixAA : public FragmentShaderBase {
  public:
-  FragmentShaderRGBATexAlphaColorMatrixAA();
-  void FillLocations(ShaderLocations* locations) const;
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
-  int color_matrix_location() const { return color_matrix_location_; }
-  int color_offset_location() const { return color_offset_location_; }
+  FragmentShaderRGBATexAlphaColorMatrixAA() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_color_matrix_ = true;
+  }
 
  private:
   std::string GetShaderSource() const override;
-  int sampler_location_;
-  int alpha_location_;
-  int color_matrix_location_;
-  int color_offset_location_;
 };
 
 class FragmentShaderRGBATexAlphaMaskColorMatrix : public FragmentShaderBase {
  public:
-  FragmentShaderRGBATexAlphaMaskColorMatrix();
-  void FillLocations(ShaderLocations* locations) const;
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int alpha_location() const { return alpha_location_; }
-  int sampler_location() const { return sampler_location_; }
-  int mask_sampler_location() const { return mask_sampler_location_; }
-  int mask_tex_coord_scale_location() const {
-    return mask_tex_coord_scale_location_;
+  FragmentShaderRGBATexAlphaMaskColorMatrix() {
+    has_sampler_ = true;
+    has_uniform_alpha_ = true;
+    has_mask_sampler_ = true;
+    has_color_matrix_ = true;
   }
-  int mask_tex_coord_offset_location() const {
-    return mask_tex_coord_offset_location_;
-  }
-  int color_matrix_location() const { return color_matrix_location_; }
-  int color_offset_location() const { return color_offset_location_; }
-
  private:
   std::string GetShaderSource() const override;
-  int sampler_location_;
-  int mask_sampler_location_;
-  int alpha_location_;
-  int mask_tex_coord_scale_location_;
-  int mask_tex_coord_offset_location_;
-  int color_matrix_location_;
-  int color_offset_location_;
 };
 
 class FragmentShaderYUVVideo : public FragmentShaderBase {
@@ -721,7 +622,7 @@
 
   void Init(gpu::gles2::GLES2Interface* context,
             unsigned program,
-            int* base_uniform_index);
+            int* base_uniform_index) override;
   int y_texture_location() const { return y_texture_location_; }
   int u_texture_location() const { return u_texture_location_; }
   int v_texture_location() const { return v_texture_location_; }
@@ -741,56 +642,42 @@
  private:
   std::string GetShaderSource() const override;
 
-  bool use_alpha_texture_;
-  bool use_nv12_;
-  bool use_color_lut_;
+  bool use_alpha_texture_ = false;
+  bool use_nv12_ = false;
+  bool use_color_lut_ = false;
 
-  int y_texture_location_;
-  int u_texture_location_;
-  int v_texture_location_;
-  int uv_texture_location_;
-  int a_texture_location_;
-  int lut_texture_location_;
-  int alpha_location_;
-  int yuv_matrix_location_;
-  int yuv_adj_location_;
-  int ya_clamp_rect_location_;
-  int uv_clamp_rect_location_;
-  int resource_multiplier_location_;
-  int resource_offset_location_;
+  int y_texture_location_ = -1;
+  int u_texture_location_ = -1;
+  int v_texture_location_ = -1;
+  int uv_texture_location_ = -1;
+  int a_texture_location_ = -1;
+  int lut_texture_location_ = -1;
+  int alpha_location_ = -1;
+  int yuv_matrix_location_ = -1;
+  int yuv_adj_location_ = -1;
+  int ya_clamp_rect_location_ = -1;
+  int uv_clamp_rect_location_ = -1;
+  int resource_multiplier_location_ = -1;
+  int resource_offset_location_ = -1;
 
   DISALLOW_COPY_AND_ASSIGN(FragmentShaderYUVVideo);
 };
 
 class FragmentShaderColor : public FragmentShaderBase {
  public:
-  FragmentShaderColor();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int color_location() const { return color_location_; }
+  FragmentShaderColor() { has_uniform_color_ = true; }
 
  private:
   std::string GetShaderSource() const override;
-  int color_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentShaderColor);
 };
 
 class FragmentShaderColorAA : public FragmentShaderBase {
  public:
-  FragmentShaderColorAA();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  int color_location() const { return color_location_; }
+  FragmentShaderColorAA() { has_uniform_color_ = true; }
 
  private:
   std::string GetShaderSource() const override;
-  int color_location_;
-
   DISALLOW_COPY_AND_ASSIGN(FragmentShaderColorAA);
 };
 
diff --git a/chrome/android/java/res/layout/download_item_view.xml b/chrome/android/java/res/layout/download_item_view.xml
index 1210f0f4..9d70beaa 100644
--- a/chrome/android/java/res/layout/download_item_view.xml
+++ b/chrome/android/java/res/layout/download_item_view.xml
@@ -88,10 +88,11 @@
             <TextView
                     android:id="@+id/filename_progress_view"
                     android:layout_width="wrap_content"
-                    android:layout_height="18dp"
+                    android:layout_height="wrap_content"
                     android:layout_alignParentTop="true"
                     android:layout_alignParentStart="true"
                     android:layout_toStartOf="@+id/pause_button"
+                    android:minHeight="18dp"
                     android:singleLine="true"
                     android:textColor="@color/default_text_color"
                     android:textSize="14sp" />
@@ -112,13 +113,14 @@
             <TextView
                     android:id="@+id/status_view"
                     android:layout_width="wrap_content"
-                    android:layout_height="18dp"
+                    android:layout_height="wrap_content"
                     android:layout_marginEnd="16dp"
                     android:layout_marginTop="0dp"
                     android:layout_alignParentBottom="true"
                     android:layout_alignParentStart="true"
                     android:layout_toStartOf="@+id/pause_button"
                     android:layout_below="@+id/download_progress_view"
+                    android:minHeight="18dp"
                     android:textAlignment="viewStart"
                     android:textColor="@color/google_grey_600"
                     android:textSize="12sp"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
index f4a3c9e..437c88d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
@@ -318,10 +318,8 @@
      */
     @CalledByNative
     private void onDownloadStarted(String filename) {
-        if (!isDangerousFile(filename)) {
-            DownloadUtils.showDownloadStartToast(mContext);
-            closeBlankTab();
-        }
+        DownloadUtils.showDownloadStartToast(mContext);
+        closeBlankTab();
     }
 
     /**
@@ -378,16 +376,6 @@
     }
 
     /**
-     * Check whether a file is dangerous.
-     *
-     * @param filename Name of the file.
-     * @return true if the file is dangerous, or false otherwise.
-     */
-    protected boolean isDangerousFile(String filename) {
-        return nativeIsDownloadDangerous(filename);
-    }
-
-    /**
      * Discards a downloaded file.
      *
      * @param filepath File to be discarded.
@@ -475,7 +463,6 @@
 
     private native void nativeInit(WebContents webContents);
     private static native String nativeGetDownloadWarningText(String filename);
-    private static native boolean nativeIsDownloadDangerous(String filename);
     private static native void nativeLaunchDuplicateDownloadInfoBar(ChromeDownloadDelegate delegate,
             Tab tab, DownloadInfo downloadInfo, String filePath, boolean isIncognito);
     private static native void nativeLaunchPermissionUpdateInfoBar(
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index cc98c5c..c2e46f6 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1067,8 +1067,8 @@
     <message name="IDS_SETTINGS_INTERNET_NETWORK_SECTION_EXPAND_ACCESSIBILITY_LABEL" desc="Label for the button that toggles showing the advanced network options. Only visible by screen reader software.">
       Show advanced network options
     </message>
-    <message name="IDS_SETTINGS_INTERNET_NETWORK_SECTION_NAMESERVERS" desc="Settings > Internet > Network details: Nameservers section label.">
-      Nameservers
+    <message name="IDS_SETTINGS_INTERNET_NETWORK_SECTION_NAMESERVERS" desc="Settings > Internet > Network details: Name servers section label.">
+      Name servers
     </message>
     <message name="IDS_SETTINGS_INTERNET_NETWORK_SECTION_NETWORK" desc="Settings > Internet > Network details: Network section label.">
       Network
@@ -2123,8 +2123,11 @@
     <message name="IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN" desc="Message shown below the title that warns the user they have entered a PIN that is easy to guess.">
       PIN may be easy to guess
     </message>
-    <message name="IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT" desc="Message shown below the title that tells the user that the PIN they entered needs to be at least four digits long.">
-      PIN must be at least 4 digits
+    <message name="IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT" desc="Message shown below the title that tells the user that the PIN they entered needs to be at least minimum digits long.">
+      PIN must be at least <ph name="MINIMUM">$1<ex>4</ex></ph> digits
+    </message>
+    <message name="IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG" desc="Message shown below the title that tells the user that the PIN they entered needs to be less than maximum digits long.">
+      PIN must be less than <ph name="MAXIMUM">$1<ex>0</ex></ph> digits
     </message>
     <message name="IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED" desc="Message shown below the title that tells the user they have entered two different PIN values.">
       PINs do not match
diff --git a/chrome/browser/android/download/chrome_download_delegate.cc b/chrome/browser/android/download/chrome_download_delegate.cc
index a9eb13f..0bbb8362 100644
--- a/chrome/browser/android/download/chrome_download_delegate.cc
+++ b/chrome/browser/android/download/chrome_download_delegate.cc
@@ -42,15 +42,6 @@
                base::android::ConvertJavaStringToUTF16(env, filename)));
 }
 
-// Returns true if a file name is dangerous, or false otherwise.
-static jboolean IsDownloadDangerous(JNIEnv* env,
-                                    const JavaParamRef<jclass>& clazz,
-                                    const JavaParamRef<jstring>& filename) {
-  base::FilePath path(base::android::ConvertJavaStringToUTF8(env, filename));
-  return safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
-             path) != safe_browsing::DownloadFileType::NOT_DANGEROUS;
-}
-
 // static
 bool ChromeDownloadDelegate::EnqueueDownloadManagerRequest(
     jobject chrome_download_delegate,
diff --git a/chrome/browser/android/download/download_controller.cc b/chrome/browser/android/download/download_controller.cc
index 908b121..c77b0f28 100644
--- a/chrome/browser/android/download/download_controller.cc
+++ b/chrome/browser/android/download/download_controller.cc
@@ -243,7 +243,9 @@
 
   ChromeDownloadDelegate* delegate =
       ChromeDownloadDelegate::FromWebContents(web_contents);
-  if (delegate) {
+  // For dangerous item, we need to show the dangerous infobar before the
+  // download can start.
+  if (!download_item->IsDangerous() && delegate) {
     delegate->OnDownloadStarted(
         download_item->GetTargetFilePath().BaseName().value());
   }
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
index 0600f24a..e897f06 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
@@ -8,12 +8,10 @@
 #include "base/bind_helpers.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/guid.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "components/security_state/core/security_state.h"
@@ -25,13 +23,6 @@
 namespace offline_pages {
 namespace {
 const base::FilePath::CharType kMHTMLExtension[] = FILE_PATH_LITERAL("mhtml");
-const base::FilePath::CharType kDefaultFileName[] =
-    FILE_PATH_LITERAL("offline_page");
-const int kTitleLengthMax = 80;
-const char kMHTMLFileNameExtension[] = ".mhtml";
-const char kFileNameComponentsSeparator[] = "-";
-const char kReplaceChars[] = " ";
-const char kReplaceWith[] = "_";
 
 void DeleteFileOnFileThread(const base::FilePath& file_path,
                             const base::Closure& callback) {
@@ -44,34 +35,6 @@
 }  // namespace
 
 // static
-std::string OfflinePageMHTMLArchiver::GetFileNameExtension() {
-    return kMHTMLFileNameExtension;
-}
-
-// static
-base::FilePath OfflinePageMHTMLArchiver::GenerateFileName(
-    const GURL& url,
-    const std::string& title,
-    int64_t archive_id) {
-  std::string title_part(title.substr(0, kTitleLengthMax));
-  std::string suggested_name(
-      url.host() + kFileNameComponentsSeparator +
-      title_part + kFileNameComponentsSeparator +
-      base::Int64ToString(archive_id));
-
-  // Substitute spaces out from title.
-  base::ReplaceChars(suggested_name, kReplaceChars, kReplaceWith,
-                     &suggested_name);
-
-  return net::GenerateFileName(url,
-                               std::string(),  // content disposition
-                               std::string(),  // charset
-                               suggested_name,
-                               std::string(),  // mime-type
-                               kDefaultFileName)
-      .AddExtension(kMHTMLExtension);
-}
-
 OfflinePageMHTMLArchiver::OfflinePageMHTMLArchiver(
     content::WebContents* web_contents)
     : web_contents_(web_contents),
@@ -129,9 +92,7 @@
   GURL url(web_contents_->GetLastCommittedURL());
   base::string16 title(web_contents_->GetTitle());
   base::FilePath file_path(
-      archives_dir.Append(
-          GenerateFileName(url, base::UTF16ToUTF8(title), archive_id)));
-
+      archives_dir.Append(base::GenerateGUID()).AddExtension(kMHTMLExtension));
   content::MHTMLGenerationParams params(file_path);
   params.use_binary_encoding = true;
 
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
index 9dedbdd..e1c78430 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
@@ -42,13 +42,6 @@
 //   }
 class OfflinePageMHTMLArchiver : public OfflinePageArchiver {
  public:
-  // Returns the extension name of the offline page file.
-  static std::string GetFileNameExtension();
-  // Creates a file name for the archive file based on url and title. Public for
-  // testing.
-  static base::FilePath GenerateFileName(const GURL& url,
-                                         const std::string& title,
-                                         int64_t archive_id);
 
   explicit OfflinePageMHTMLArchiver(content::WebContents* web_contents);
   ~OfflinePageMHTMLArchiver() override;
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc
index b6539b51..72845ca 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc
@@ -233,37 +233,4 @@
   EXPECT_EQ(kTestFileSize, last_file_size());
 }
 
-TEST_F(OfflinePageMHTMLArchiverTest, GenerateFileName) {
-  GURL url_1("http://news.google.com/page1");
-  std::string title_1("Google News Page");
-  base::FilePath expected_1(FILE_PATH_LITERAL(
-      "news.google.com-Google_News_Page-1234.mhtml"));
-  base::FilePath actual_1(
-      OfflinePageMHTMLArchiver::GenerateFileName(url_1, title_1, 1234LL));
-  EXPECT_EQ(expected_1, actual_1);
-
-  GURL url_2("https://en.m.wikipedia.org/Sample_page_about_stuff");
-  std::string title_2("Some Wiki Page");
-  base::FilePath expected_2(FILE_PATH_LITERAL(
-      "en.m.wikipedia.org-Some_Wiki_Page-56789.mhtml"));
-  base::FilePath actual_2(
-      OfflinePageMHTMLArchiver::GenerateFileName(url_2, title_2, 56789LL));
-  EXPECT_EQ(expected_2, actual_2);
-
-  GURL url_3("https://www.google.com/search");
-  std::string title_3 =
-      "A really really really really really long title "
-      "that is over 80 chars long here^ - TRUNCATE THIS PART";
-  std::string expected_title_3_part =
-      "A_really_really_really_really_really_long_title_"
-      "that_is_over_80_chars_long_here^";
-  base::FilePath expected_3(
-      FILE_PATH_LITERAL("www.google.com-" +
-                        expected_title_3_part +
-                        "-123456789.mhtml"));
-  base::FilePath actual_3(
-      OfflinePageMHTMLArchiver::GenerateFileName(url_3, title_3, 123456789LL));
-  EXPECT_EQ(expected_3, actual_3);
-}
-
 }  // namespace offline_pages
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 4c2841a8..332add5 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -514,6 +514,7 @@
 };
 
 class WebViewNewWindowInteractiveTest : public WebViewInteractiveTest {};
+class WebViewPointerLockInteractiveTest : public WebViewInteractiveTest {};
 
 // The tests below aren't needed in --use-cross-process-frames-for-guests.
 class WebViewContextMenuInteractiveTest : public WebViewInteractiveTestBase {};
@@ -523,7 +524,6 @@
 // with WebViewInteractiveTest (see crbug.com/582562).
 class WebViewFocusInteractiveTest : public WebViewInteractiveTestBase {};
 class WebViewPopupInteractiveTest : public WebViewInteractiveTestBase {};
-class WebViewPointerLockInteractiveTest : public WebViewInteractiveTestBase {};
 class WebViewDragDropInteractiveTest : public WebViewInteractiveTestBase {};
 
 INSTANTIATE_TEST_CASE_P(WebViewInteractiveTests,
@@ -534,6 +534,10 @@
                         WebViewNewWindowInteractiveTest,
                         testing::Bool());
 
+INSTANTIATE_TEST_CASE_P(WebViewInteractiveTests,
+                        WebViewPointerLockInteractiveTest,
+                        testing::Bool());
+
 // ui_test_utils::SendMouseMoveSync doesn't seem to work on OS_MACOSX, and
 // likely won't work on many other platforms as well, so for now this test
 // is for Windows and Linux only. As of Sept 17th, 2013 this test is disabled
@@ -544,7 +548,7 @@
 
 #if defined(OS_LINUX)
 // flaky http://crbug.com/412086
-IN_PROC_BROWSER_TEST_F(WebViewPointerLockInteractiveTest,
+IN_PROC_BROWSER_TEST_P(WebViewPointerLockInteractiveTest,
                        DISABLED_PointerLock) {
   SetupTest("web_view/pointer_lock",
             "/extensions/platform_apps/web_view/pointer_lock/guest.html");
@@ -618,7 +622,7 @@
 }
 
 // flaky http://crbug.com/412086
-IN_PROC_BROWSER_TEST_F(WebViewPointerLockInteractiveTest,
+IN_PROC_BROWSER_TEST_P(WebViewPointerLockInteractiveTest,
                        DISABLED_PointerLockFocus) {
   SetupTest("web_view/pointer_lock_focus",
             "/extensions/platform_apps/web_view/pointer_lock_focus/guest.html");
@@ -1106,7 +1110,7 @@
   ASSERT_TRUE(done_listener.WaitUntilSatisfied());
 }
 
-IN_PROC_BROWSER_TEST_F(WebViewPointerLockInteractiveTest,
+IN_PROC_BROWSER_TEST_P(WebViewPointerLockInteractiveTest,
                        PointerLock_PointerLockLostWithFocus) {
   TestHelper("testPointerLockLostWithFocus",
              "web_view/pointerlock",
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index 40f4f5c4..e4be8323 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -311,12 +311,11 @@
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
-// Flaky: http://crbug.com/675851
 // Tests that fullscreen transitions during a tab capture session dispatch
 // events to the onStatusChange listener.  The test loads a page that toggles
 // fullscreen mode, using the Fullscreen Javascript API, in response to mouse
 // clicks.
-IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, DISABLED_FullscreenEvents) {
+IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, FullscreenEvents) {
   AddExtensionToCommandLineWhitelist();
 
   ExtensionTestMessageListener capture_started("tab_capture_started", false);
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 757222b..f5acaf6 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -1190,7 +1190,7 @@
 #endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
 
 #if BUILDFLAG(DEBUG_DEVTOOLS)
-  request_interceptors.push_back(new DebugDevToolsInterceptor);
+  request_interceptors.push_back(base::MakeUnique<DebugDevToolsInterceptor>());
 #endif
 
   // Set up interceptors in the reverse order.
diff --git a/chrome/browser/resources/hotword/always_on_manager.js b/chrome/browser/resources/hotword/always_on_manager.js
index 042a8d2..f34bc6d 100644
--- a/chrome/browser/resources/hotword/always_on_manager.js
+++ b/chrome/browser/resources/hotword/always_on_manager.js
@@ -14,18 +14,17 @@
    * @extends {hotword.BaseSessionManager}
    */
   function AlwaysOnManager(stateManager) {
-    hotword.BaseSessionManager.call(this,
-                                    stateManager,
-                                    hotword.constants.SessionSource.ALWAYS);
+    hotword.BaseSessionManager.call(
+        this, stateManager, hotword.constants.SessionSource.ALWAYS);
   }
 
   AlwaysOnManager.prototype = {
     __proto__: hotword.BaseSessionManager.prototype,
 
     /** @override */
-     enabled: function() {
-       return this.stateManager.isAlwaysOnEnabled();
-     },
+    enabled: function() {
+      return this.stateManager.isAlwaysOnEnabled();
+    },
 
     /** @override */
     updateListeners: function() {
@@ -35,7 +34,5 @@
     }
   };
 
-  return {
-    AlwaysOnManager: AlwaysOnManager
-  };
+  return {AlwaysOnManager: AlwaysOnManager};
 });
diff --git a/chrome/browser/resources/hotword/audio_client.js b/chrome/browser/resources/hotword/audio_client.js
index f74f84b..b3343b43 100644
--- a/chrome/browser/resources/hotword/audio_client.js
+++ b/chrome/browser/resources/hotword/audio_client.js
@@ -133,8 +133,9 @@
    */
   AudioClient.prototype.checkSpeechOverlayUi_ = function() {
     if (!this.speechOverlay_) {
-      window.setTimeout(this.delayedCheckSpeechOverlayUi_.bind(this),
-                        AudioClient.RETRY_TIME_MS_);
+      window.setTimeout(
+          this.delayedCheckSpeechOverlayUi_.bind(this),
+          AudioClient.RETRY_TIME_MS_);
     } else {
       this.checkSpeechUiRetries_ = 0;
     }
@@ -147,8 +148,8 @@
    * @private
    */
   AudioClient.prototype.delayedCheckSpeechOverlayUi_ = function() {
-    this.speechOverlay_ = document.getElementById(
-        AudioClient.SPEECH_UI_OVERLAY_ID_);
+    this.speechOverlay_ =
+        document.getElementById(AudioClient.SPEECH_UI_OVERLAY_ID_);
     if (!this.speechOverlay_) {
       if (this.checkSpeechUiRetries_++ < AudioClient.MAX_RETRIES) {
         this.sendCommandToPage_(AudioClient.CommandToPage.VOICE_TRIGGER);
@@ -167,9 +168,8 @@
    * @private
    */
   AudioClient.prototype.checkUi_ = function(command) {
-    this.uiStatus_[command].timeoutId =
-        window.setTimeout(this.failedCheckUi_.bind(this, command),
-                          AudioClient.RETRY_TIME_MS_);
+    this.uiStatus_[command].timeoutId = window.setTimeout(
+        this.failedCheckUi_.bind(this, command), AudioClient.RETRY_TIME_MS_);
   };
 
   /**
diff --git a/chrome/browser/resources/hotword/base_session_manager.js b/chrome/browser/resources/hotword/base_session_manager.js
index d1ee2bf..6891199 100644
--- a/chrome/browser/resources/hotword/base_session_manager.js
+++ b/chrome/browser/resources/hotword/base_session_manager.js
@@ -56,8 +56,7 @@
      * Called when the hotwording session is stopped.
      * @protected
      */
-    onSessionStop: function() {
-    },
+    onSessionStop: function() {},
 
     /**
      * Starts a launcher hotwording session.
@@ -65,13 +64,9 @@
      *     recognizer in.
      */
     startSession: function(opt_mode) {
-      this.stateManager.startSession(
-          this.sessionSource_,
-          function() {
-            chrome.hotwordPrivate.setHotwordSessionState(true, function() {});
-          },
-          this.handleHotwordTrigger.bind(this),
-          opt_mode);
+      this.stateManager.startSession(this.sessionSource_, function() {
+        chrome.hotwordPrivate.setHotwordSessionState(true, function() {});
+      }, this.handleHotwordTrigger.bind(this), opt_mode);
     },
 
     /**
@@ -90,9 +85,8 @@
      */
     handleHotwordTrigger: function(log) {
       hotword.debug('Hotword triggered: ' + this.sessionSource_, log);
-      chrome.hotwordPrivate.notifyHotwordRecognition('search',
-                                                     log,
-                                                     function() {});
+      chrome.hotwordPrivate.notifyHotwordRecognition(
+          'search', log, function() {});
     },
 
     /**
@@ -154,7 +148,5 @@
     }
   };
 
-  return {
-    BaseSessionManager: BaseSessionManager
-  };
+  return {BaseSessionManager: BaseSessionManager};
 });
diff --git a/chrome/browser/resources/hotword/constants.js b/chrome/browser/resources/hotword/constants.js
index 988f9b8..0fbfa0e 100644
--- a/chrome/browser/resources/hotword/constants.js
+++ b/chrome/browser/resources/hotword/constants.js
@@ -3,308 +3,297 @@
 // found in the LICENSE file.
 
 cr.define('hotword.constants', function() {
-'use strict';
+  'use strict';
 
-/**
- * Number of seconds of audio to record when logging is enabled.
- * @const {number}
- */
-var AUDIO_LOG_SECONDS = 2;
+  /**
+   * Number of seconds of audio to record when logging is enabled.
+   * @const {number}
+   */
+  var AUDIO_LOG_SECONDS = 2;
 
-/**
- * Timeout in seconds, for detecting false positives with a hotword stream.
- * @const {number}
- */
-var HOTWORD_STREAM_TIMEOUT_SECONDS = 2;
+  /**
+   * Timeout in seconds, for detecting false positives with a hotword stream.
+   * @const {number}
+   */
+  var HOTWORD_STREAM_TIMEOUT_SECONDS = 2;
 
-/**
- * Hotword data shared module extension's ID.
- * @const {string}
- */
-var SHARED_MODULE_ID = 'lccekmodgklaepjeofjdjpbminllajkg';
+  /**
+   * Hotword data shared module extension's ID.
+   * @const {string}
+   */
+  var SHARED_MODULE_ID = 'lccekmodgklaepjeofjdjpbminllajkg';
 
-/**
- * Path to shared module data.
- * @const {string}
- */
-var SHARED_MODULE_ROOT = '_modules/' + SHARED_MODULE_ID;
+  /**
+   * Path to shared module data.
+   * @const {string}
+   */
+  var SHARED_MODULE_ROOT = '_modules/' + SHARED_MODULE_ID;
 
-/**
- * Name used by the content scripts to create communications Ports.
- * @const {string}
- */
-var CLIENT_PORT_NAME = 'chwcpn';
+  /**
+   * Name used by the content scripts to create communications Ports.
+   * @const {string}
+   */
+  var CLIENT_PORT_NAME = 'chwcpn';
 
-/**
- * The field name to specify the command among pages.
- * @const {string}
- */
-var COMMAND_FIELD_NAME = 'cmd';
+  /**
+   * The field name to specify the command among pages.
+   * @const {string}
+   */
+  var COMMAND_FIELD_NAME = 'cmd';
 
-/**
- * The speaker model file name.
- * @const {string}
- */
-var SPEAKER_MODEL_FILE_NAME = 'speaker_model.data';
+  /**
+   * The speaker model file name.
+   * @const {string}
+   */
+  var SPEAKER_MODEL_FILE_NAME = 'speaker_model.data';
 
-/**
- * The training utterance file name prefix.
- * @const {string}
- */
-var UTTERANCE_FILE_PREFIX = 'utterance-';
+  /**
+   * The training utterance file name prefix.
+   * @const {string}
+   */
+  var UTTERANCE_FILE_PREFIX = 'utterance-';
 
-/**
- * The training utterance file extension.
- * @const {string}
- */
-var UTTERANCE_FILE_EXTENSION = '.raw';
+  /**
+   * The training utterance file extension.
+   * @const {string}
+   */
+  var UTTERANCE_FILE_EXTENSION = '.raw';
 
-/**
- * The number of training utterances required to train the speaker model.
- * @const {number}
- */
-var NUM_TRAINING_UTTERANCES = 3;
+  /**
+   * The number of training utterances required to train the speaker model.
+   * @const {number}
+   */
+  var NUM_TRAINING_UTTERANCES = 3;
 
-/**
- * The size of the file system requested for reading the speaker model and
- * utterances. This number should always be larger than the combined file size,
- * currently 576338 bytes as of February 2015.
- * @const {number}
- */
-var FILE_SYSTEM_SIZE_BYTES = 1048576;
+  /**
+   * The size of the file system requested for reading the speaker model and
+   * utterances. This number should always be larger than the combined file
+   * size,
+   * currently 576338 bytes as of February 2015.
+   * @const {number}
+   */
+  var FILE_SYSTEM_SIZE_BYTES = 1048576;
 
-/**
- * Time to wait for expected messages, in milliseconds.
- * @enum {number}
- */
-var TimeoutMs = {
-  SHORT: 200,
-  NORMAL: 500,
-  LONG: 2000
-};
+  /**
+   * Time to wait for expected messages, in milliseconds.
+   * @enum {number}
+   */
+  var TimeoutMs = {SHORT: 200, NORMAL: 500, LONG: 2000};
 
-/**
- * The URL of the files used by the plugin.
- * @enum {string}
- */
-var File = {
-  RECOGNIZER_CONFIG: 'hotword.data',
-};
+  /**
+   * The URL of the files used by the plugin.
+   * @enum {string}
+   */
+  var File = {
+    RECOGNIZER_CONFIG: 'hotword.data',
+  };
 
-/**
- * Errors emitted by the NaClManager.
- * @enum {string}
- */
-var Error = {
-  NACL_CRASH: 'nacl_crash',
-  TIMEOUT: 'timeout',
-};
+  /**
+   * Errors emitted by the NaClManager.
+   * @enum {string}
+   */
+  var Error = {
+    NACL_CRASH: 'nacl_crash',
+    TIMEOUT: 'timeout',
+  };
 
-/**
- * Event types supported by NaClManager.
- * @enum {string}
- */
-var Event = {
-  READY: 'ready',
-  TRIGGER: 'trigger',
-  SPEAKER_MODEL_SAVED: 'speaker model saved',
-  ERROR: 'error',
-  TIMEOUT: 'timeout',
-};
+  /**
+   * Event types supported by NaClManager.
+   * @enum {string}
+   */
+  var Event = {
+    READY: 'ready',
+    TRIGGER: 'trigger',
+    SPEAKER_MODEL_SAVED: 'speaker model saved',
+    ERROR: 'error',
+    TIMEOUT: 'timeout',
+  };
 
-/**
- * Messages for communicating with the NaCl recognizer plugin. These must match
- * constants in <google3>/hotword_plugin.c
- * @enum {string}
- */
-var NaClPlugin = {
-  RESTART: 'r',
-  SAMPLE_RATE_PREFIX: 'h',
-  MODEL_PREFIX: 'm',
-  STOP: 's',
-  LOG: 'l',
-  DSP: 'd',
-  BEGIN_SPEAKER_MODEL: 'b',
-  ADAPT_SPEAKER_MODEL: 'a',
-  FINISH_SPEAKER_MODEL: 'f',
-  SPEAKER_MODEL_SAVED: 'sm_saved',
-  REQUEST_MODEL: 'model',
-  MODEL_LOADED: 'model_loaded',
-  READY_FOR_AUDIO: 'audio',
-  STOPPED: 'stopped',
-  HOTWORD_DETECTED: 'hotword',
-  MS_CONFIGURED: 'ms_configured',
-  TIMEOUT: 'timeout'
-};
+  /**
+   * Messages for communicating with the NaCl recognizer plugin. These must
+   * match
+   * constants in <google3>/hotword_plugin.c
+   * @enum {string}
+   */
+  var NaClPlugin = {
+    RESTART: 'r',
+    SAMPLE_RATE_PREFIX: 'h',
+    MODEL_PREFIX: 'm',
+    STOP: 's',
+    LOG: 'l',
+    DSP: 'd',
+    BEGIN_SPEAKER_MODEL: 'b',
+    ADAPT_SPEAKER_MODEL: 'a',
+    FINISH_SPEAKER_MODEL: 'f',
+    SPEAKER_MODEL_SAVED: 'sm_saved',
+    REQUEST_MODEL: 'model',
+    MODEL_LOADED: 'model_loaded',
+    READY_FOR_AUDIO: 'audio',
+    STOPPED: 'stopped',
+    HOTWORD_DETECTED: 'hotword',
+    MS_CONFIGURED: 'ms_configured',
+    TIMEOUT: 'timeout'
+  };
 
-/**
- * Messages sent from the injected scripts to the Google page.
- * @enum {string}
- */
-var CommandToPage = {
-  HOTWORD_VOICE_TRIGGER: 'vt',
-  HOTWORD_STARTED: 'hs',
-  HOTWORD_ENDED: 'hd',
-  HOTWORD_TIMEOUT: 'ht',
-  HOTWORD_ERROR: 'he'
-};
+  /**
+   * Messages sent from the injected scripts to the Google page.
+   * @enum {string}
+   */
+  var CommandToPage = {
+    HOTWORD_VOICE_TRIGGER: 'vt',
+    HOTWORD_STARTED: 'hs',
+    HOTWORD_ENDED: 'hd',
+    HOTWORD_TIMEOUT: 'ht',
+    HOTWORD_ERROR: 'he'
+  };
 
-/**
- * Messages sent from the Google page to the extension or to the
- * injected script and then passed to the extension.
- * @enum {string}
- */
-var CommandFromPage = {
-  SPEECH_START: 'ss',
-  SPEECH_END: 'se',
-  SPEECH_RESET: 'sr',
-  SHOWING_HOTWORD_START: 'shs',
-  SHOWING_ERROR_MESSAGE: 'sem',
-  SHOWING_TIMEOUT_MESSAGE: 'stm',
-  CLICKED_RESUME: 'hcc',
-  CLICKED_RESTART: 'hcr',
-  CLICKED_DEBUG: 'hcd',
-  WAKE_UP_HELPER: 'wuh',
-  // Command specifically for the opt-in promo below this line.
-  // User has explicitly clicked 'no'.
-  CLICKED_NO_OPTIN: 'hcno',
-  // User has opted in.
-  CLICKED_OPTIN: 'hco',
-  // User clicked on the microphone.
-  PAGE_WAKEUP: 'wu'
-};
+  /**
+   * Messages sent from the Google page to the extension or to the
+   * injected script and then passed to the extension.
+   * @enum {string}
+   */
+  var CommandFromPage = {
+    SPEECH_START: 'ss',
+    SPEECH_END: 'se',
+    SPEECH_RESET: 'sr',
+    SHOWING_HOTWORD_START: 'shs',
+    SHOWING_ERROR_MESSAGE: 'sem',
+    SHOWING_TIMEOUT_MESSAGE: 'stm',
+    CLICKED_RESUME: 'hcc',
+    CLICKED_RESTART: 'hcr',
+    CLICKED_DEBUG: 'hcd',
+    WAKE_UP_HELPER: 'wuh',
+    // Command specifically for the opt-in promo below this line.
+    // User has explicitly clicked 'no'.
+    CLICKED_NO_OPTIN: 'hcno',
+    // User has opted in.
+    CLICKED_OPTIN: 'hco',
+    // User clicked on the microphone.
+    PAGE_WAKEUP: 'wu'
+  };
 
-/**
- * Source of a hotwording session request.
- * @enum {string}
- */
-var SessionSource = {
-  LAUNCHER: 'launcher',
-  NTP: 'ntp',
-  ALWAYS: 'always',
-  TRAINING: 'training'
-};
+  /**
+   * Source of a hotwording session request.
+   * @enum {string}
+   */
+  var SessionSource = {
+    LAUNCHER: 'launcher',
+    NTP: 'ntp',
+    ALWAYS: 'always',
+    TRAINING: 'training'
+  };
 
-/**
- * The mode to start the hotword recognizer in.
- * @enum {string}
- */
-var RecognizerStartMode = {
-  NORMAL: 'normal',
-  NEW_MODEL: 'new model',
-  ADAPT_MODEL: 'adapt model'
-};
+  /**
+   * The mode to start the hotword recognizer in.
+   * @enum {string}
+   */
+  var RecognizerStartMode = {
+    NORMAL: 'normal',
+    NEW_MODEL: 'new model',
+    ADAPT_MODEL: 'adapt model'
+  };
 
-/**
- * MediaStream open success/errors to be reported via UMA.
- * DO NOT remove or renumber values in this enum. Only add new ones.
- * @enum {number}
- */
-var UmaMediaStreamOpenResult = {
-  SUCCESS: 0,
-  UNKNOWN: 1,
-  NOT_SUPPORTED: 2,
-  PERMISSION_DENIED: 3,
-  CONSTRAINT_NOT_SATISFIED: 4,
-  OVERCONSTRAINED: 5,
-  NOT_FOUND: 6,
-  ABORT: 7,
-  SOURCE_UNAVAILABLE: 8,
-  PERMISSION_DISMISSED: 9,
-  INVALID_STATE: 10,
-  DEVICES_NOT_FOUND: 11,
-  INVALID_SECURITY_ORIGIN: 12,
-  MAX: 12
-};
+  /**
+   * MediaStream open success/errors to be reported via UMA.
+   * DO NOT remove or renumber values in this enum. Only add new ones.
+   * @enum {number}
+   */
+  var UmaMediaStreamOpenResult = {
+    SUCCESS: 0,
+    UNKNOWN: 1,
+    NOT_SUPPORTED: 2,
+    PERMISSION_DENIED: 3,
+    CONSTRAINT_NOT_SATISFIED: 4,
+    OVERCONSTRAINED: 5,
+    NOT_FOUND: 6,
+    ABORT: 7,
+    SOURCE_UNAVAILABLE: 8,
+    PERMISSION_DISMISSED: 9,
+    INVALID_STATE: 10,
+    DEVICES_NOT_FOUND: 11,
+    INVALID_SECURITY_ORIGIN: 12,
+    MAX: 12
+  };
 
-/**
- * UMA metrics.
- * DO NOT change these enum values.
- * @enum {string}
- */
-var UmaMetrics = {
-  TRIGGER: 'Hotword.HotwordTrigger',
-  MEDIA_STREAM_RESULT: 'Hotword.HotwordMediaStreamResult',
-  NACL_PLUGIN_LOAD_RESULT: 'Hotword.HotwordNaClPluginLoadResult',
-  NACL_MESSAGE_TIMEOUT: 'Hotword.HotwordNaClMessageTimeout',
-  TRIGGER_SOURCE: 'Hotword.HotwordTriggerSource'
-};
+  /**
+   * UMA metrics.
+   * DO NOT change these enum values.
+   * @enum {string}
+   */
+  var UmaMetrics = {
+    TRIGGER: 'Hotword.HotwordTrigger',
+    MEDIA_STREAM_RESULT: 'Hotword.HotwordMediaStreamResult',
+    NACL_PLUGIN_LOAD_RESULT: 'Hotword.HotwordNaClPluginLoadResult',
+    NACL_MESSAGE_TIMEOUT: 'Hotword.HotwordNaClMessageTimeout',
+    TRIGGER_SOURCE: 'Hotword.HotwordTriggerSource'
+  };
 
-/**
- * Message waited for by NaCl plugin, to be reported via UMA.
- * DO NOT remove or renumber values in this enum. Only add new ones.
- * @enum {number}
- */
-var UmaNaClMessageTimeout = {
-  REQUEST_MODEL: 0,
-  MODEL_LOADED: 1,
-  READY_FOR_AUDIO: 2,
-  STOPPED: 3,
-  HOTWORD_DETECTED: 4,
-  MS_CONFIGURED: 5,
-  MAX: 5
-};
+  /**
+   * Message waited for by NaCl plugin, to be reported via UMA.
+   * DO NOT remove or renumber values in this enum. Only add new ones.
+   * @enum {number}
+   */
+  var UmaNaClMessageTimeout = {
+    REQUEST_MODEL: 0,
+    MODEL_LOADED: 1,
+    READY_FOR_AUDIO: 2,
+    STOPPED: 3,
+    HOTWORD_DETECTED: 4,
+    MS_CONFIGURED: 5,
+    MAX: 5
+  };
 
-/**
- * NaCl plugin load success/errors to be reported via UMA.
- * DO NOT remove or renumber values in this enum. Only add new ones.
- * @enum {number}
- */
-var UmaNaClPluginLoadResult = {
-  SUCCESS: 0,
-  UNKNOWN: 1,
-  CRASH: 2,
-  NO_MODULE_FOUND: 3,
-  MAX: 3
-};
+  /**
+   * NaCl plugin load success/errors to be reported via UMA.
+   * DO NOT remove or renumber values in this enum. Only add new ones.
+   * @enum {number}
+   */
+  var UmaNaClPluginLoadResult =
+      {SUCCESS: 0, UNKNOWN: 1, CRASH: 2, NO_MODULE_FOUND: 3, MAX: 3};
 
-/**
- * Source of hotword triggering, to be reported via UMA.
- * DO NOT remove or renumber values in this enum. Only add new ones.
- * @enum {number}
- */
-var UmaTriggerSource = {
-  LAUNCHER: 0,
-  NTP_GOOGLE_COM: 1,
-  ALWAYS_ON: 2,
-  TRAINING: 3,
-  MAX: 3
-};
+  /**
+   * Source of hotword triggering, to be reported via UMA.
+   * DO NOT remove or renumber values in this enum. Only add new ones.
+   * @enum {number}
+   */
+  var UmaTriggerSource =
+      {LAUNCHER: 0, NTP_GOOGLE_COM: 1, ALWAYS_ON: 2, TRAINING: 3, MAX: 3};
 
-/**
- * The browser UI language.
- * @const {string}
- */
-var UI_LANGUAGE = (chrome.i18n && chrome.i18n.getUILanguage) ?
-      chrome.i18n.getUILanguage() : '';
+  /**
+   * The browser UI language.
+   * @const {string}
+   */
+  var UI_LANGUAGE = (chrome.i18n && chrome.i18n.getUILanguage) ?
+      chrome.i18n.getUILanguage() :
+      '';
 
-return {
-  AUDIO_LOG_SECONDS: AUDIO_LOG_SECONDS,
-  CLIENT_PORT_NAME: CLIENT_PORT_NAME,
-  COMMAND_FIELD_NAME: COMMAND_FIELD_NAME,
-  FILE_SYSTEM_SIZE_BYTES: FILE_SYSTEM_SIZE_BYTES,
-  HOTWORD_STREAM_TIMEOUT_SECONDS: HOTWORD_STREAM_TIMEOUT_SECONDS,
-  NUM_TRAINING_UTTERANCES: NUM_TRAINING_UTTERANCES,
-  SHARED_MODULE_ID: SHARED_MODULE_ID,
-  SHARED_MODULE_ROOT: SHARED_MODULE_ROOT,
-  SPEAKER_MODEL_FILE_NAME: SPEAKER_MODEL_FILE_NAME,
-  UI_LANGUAGE: UI_LANGUAGE,
-  UTTERANCE_FILE_EXTENSION: UTTERANCE_FILE_EXTENSION,
-  UTTERANCE_FILE_PREFIX: UTTERANCE_FILE_PREFIX,
-  CommandToPage: CommandToPage,
-  CommandFromPage: CommandFromPage,
-  Error: Error,
-  Event: Event,
-  File: File,
-  NaClPlugin: NaClPlugin,
-  RecognizerStartMode: RecognizerStartMode,
-  SessionSource: SessionSource,
-  TimeoutMs: TimeoutMs,
-  UmaMediaStreamOpenResult: UmaMediaStreamOpenResult,
-  UmaMetrics: UmaMetrics,
-  UmaNaClMessageTimeout: UmaNaClMessageTimeout,
-  UmaNaClPluginLoadResult: UmaNaClPluginLoadResult,
-  UmaTriggerSource: UmaTriggerSource
-};
+  return {
+    AUDIO_LOG_SECONDS: AUDIO_LOG_SECONDS,
+    CLIENT_PORT_NAME: CLIENT_PORT_NAME,
+    COMMAND_FIELD_NAME: COMMAND_FIELD_NAME,
+    FILE_SYSTEM_SIZE_BYTES: FILE_SYSTEM_SIZE_BYTES,
+    HOTWORD_STREAM_TIMEOUT_SECONDS: HOTWORD_STREAM_TIMEOUT_SECONDS,
+    NUM_TRAINING_UTTERANCES: NUM_TRAINING_UTTERANCES,
+    SHARED_MODULE_ID: SHARED_MODULE_ID,
+    SHARED_MODULE_ROOT: SHARED_MODULE_ROOT,
+    SPEAKER_MODEL_FILE_NAME: SPEAKER_MODEL_FILE_NAME,
+    UI_LANGUAGE: UI_LANGUAGE,
+    UTTERANCE_FILE_EXTENSION: UTTERANCE_FILE_EXTENSION,
+    UTTERANCE_FILE_PREFIX: UTTERANCE_FILE_PREFIX,
+    CommandToPage: CommandToPage,
+    CommandFromPage: CommandFromPage,
+    Error: Error,
+    Event: Event,
+    File: File,
+    NaClPlugin: NaClPlugin,
+    RecognizerStartMode: RecognizerStartMode,
+    SessionSource: SessionSource,
+    TimeoutMs: TimeoutMs,
+    UmaMediaStreamOpenResult: UmaMediaStreamOpenResult,
+    UmaMetrics: UmaMetrics,
+    UmaNaClMessageTimeout: UmaNaClMessageTimeout,
+    UmaNaClPluginLoadResult: UmaNaClPluginLoadResult,
+    UmaTriggerSource: UmaTriggerSource
+  };
 
 });
diff --git a/chrome/browser/resources/hotword/keep_alive.js b/chrome/browser/resources/hotword/keep_alive.js
index 06ddf23..20ff2ea 100644
--- a/chrome/browser/resources/hotword/keep_alive.js
+++ b/chrome/browser/resources/hotword/keep_alive.js
@@ -48,7 +48,5 @@
     }
   };
 
-  return {
-    KeepAlive: KeepAlive
-  };
+  return {KeepAlive: KeepAlive};
 });
diff --git a/chrome/browser/resources/hotword/launcher_manager.js b/chrome/browser/resources/hotword/launcher_manager.js
index 59646ea..144c247 100644
--- a/chrome/browser/resources/hotword/launcher_manager.js
+++ b/chrome/browser/resources/hotword/launcher_manager.js
@@ -13,9 +13,8 @@
    * @extends {hotword.BaseSessionManager}
    */
   function LauncherManager(stateManager) {
-    hotword.BaseSessionManager.call(this,
-                                    stateManager,
-                                    hotword.constants.SessionSource.LAUNCHER);
+    hotword.BaseSessionManager.call(
+        this, stateManager, hotword.constants.SessionSource.LAUNCHER);
   }
 
   LauncherManager.prototype = {
@@ -32,7 +31,5 @@
     }
   };
 
-  return {
-    LauncherManager: LauncherManager
-  };
+  return {LauncherManager: LauncherManager};
 });
diff --git a/chrome/browser/resources/hotword/logging.js b/chrome/browser/resources/hotword/logging.js
index 307234d..e0744e0 100644
--- a/chrome/browser/resources/hotword/logging.js
+++ b/chrome/browser/resources/hotword/logging.js
@@ -15,8 +15,5 @@
       console.log.apply(console, arguments);
   }
 
-  return {
-    DEBUG: false,
-    debug: debug
-  };
+  return {DEBUG: false, debug: debug};
 });
diff --git a/chrome/browser/resources/hotword/metrics.js b/chrome/browser/resources/hotword/metrics.js
index b17a5d7..7d4001bf 100644
--- a/chrome/browser/resources/hotword/metrics.js
+++ b/chrome/browser/resources/hotword/metrics.js
@@ -22,7 +22,5 @@
     chrome.metricsPrivate.recordValue(metricDesc, value);
   }
 
-  return {
-    recordEnum: recordEnum
-  };
+  return {recordEnum: recordEnum};
 });
diff --git a/chrome/browser/resources/hotword/nacl_manager.js b/chrome/browser/resources/hotword/nacl_manager.js
index 5a1dd55..ad4a889 100644
--- a/chrome/browser/resources/hotword/nacl_manager.js
+++ b/chrome/browser/resources/hotword/nacl_manager.js
@@ -3,599 +3,609 @@
 // found in the LICENSE file.
 
 cr.define('hotword', function() {
-'use strict';
-
-/**
- * Class used to manage the state of the NaCl recognizer plugin. Handles all
- * control of the NaCl plugin, including creation, start, stop, trigger, and
- * shutdown.
- *
- * @param {boolean} loggingEnabled Whether audio logging is enabled.
- * @param {boolean} hotwordStream Whether the audio input stream is from a
- *     hotword stream.
- * @constructor
- * @extends {cr.EventTarget}
- */
-function NaClManager(loggingEnabled, hotwordStream) {
-  /**
-   * Current state of this manager.
-   * @private {hotword.NaClManager.ManagerState_}
-   */
-  this.recognizerState_ = ManagerState_.UNINITIALIZED;
+  'use strict';
 
   /**
-   * The window.timeout ID associated with a pending message.
-   * @private {?number}
+   * Class used to manage the state of the NaCl recognizer plugin. Handles all
+   * control of the NaCl plugin, including creation, start, stop, trigger, and
+   * shutdown.
+   *
+   * @param {boolean} loggingEnabled Whether audio logging is enabled.
+   * @param {boolean} hotwordStream Whether the audio input stream is from a
+   *     hotword stream.
+   * @constructor
+   * @extends {cr.EventTarget}
    */
-  this.naclTimeoutId_ = null;
+  function NaClManager(loggingEnabled, hotwordStream) {
+    /**
+     * Current state of this manager.
+     * @private {hotword.NaClManager.ManagerState_}
+     */
+    this.recognizerState_ = ManagerState_.UNINITIALIZED;
 
-  /**
-   * The expected message that will cancel the current timeout.
-   * @private {?string}
-   */
-  this.expectingMessage_ = null;
+    /**
+     * The window.timeout ID associated with a pending message.
+     * @private {?number}
+     */
+    this.naclTimeoutId_ = null;
 
-  /**
-   * Whether the plugin will be started as soon as it stops.
-   * @private {boolean}
-   */
-  this.restartOnStop_ = false;
-
-  /**
-   * NaCl plugin element on extension background page.
-   * @private {?HTMLEmbedElement}
-   */
-  this.plugin_ = null;
-
-  /**
-   * URL containing hotword-model data file.
-   * @private {string}
-   */
-  this.modelUrl_ = '';
-
-  /**
-   * Media stream containing an audio input track.
-   * @private {?MediaStream}
-   */
-  this.stream_ = null;
-
-  /**
-   * The mode to start the recognizer in.
-   * @private {?chrome.hotwordPrivate.RecognizerStartMode}
-   */
-  this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL;
-
-  /**
-   * Whether audio logging is enabled.
-   * @private {boolean}
-   */
-  this.loggingEnabled_ = loggingEnabled;
-
-  /**
-   * Whether the audio input stream is from a hotword stream.
-   * @private {boolean}
-   */
-  this.hotwordStream_ = hotwordStream;
-
-  /**
-   * Audio log of X seconds before hotword triggered.
-   * @private {?Object}
-   */
-  this.preambleLog_ = null;
-};
-
-/**
- * States this manager can be in. Since messages to/from the plugin are
- * asynchronous (and potentially queued), it's not possible to know what state
- * the plugin is in. However, track a state machine for NaClManager based on
- * what messages are sent/received.
- * @enum {number}
- * @private
- */
-NaClManager.ManagerState_ = {
-  UNINITIALIZED: 0,
-  LOADING: 1,
-  STOPPING: 2,
-  STOPPED: 3,
-  STARTING: 4,
-  RUNNING: 5,
-  ERROR: 6,
-  SHUTDOWN: 7,
-};
-var ManagerState_ = NaClManager.ManagerState_;
-var Error_ = hotword.constants.Error;
-var UmaNaClMessageTimeout_ = hotword.constants.UmaNaClMessageTimeout;
-var UmaNaClPluginLoadResult_ = hotword.constants.UmaNaClPluginLoadResult;
-
-NaClManager.prototype.__proto__ = cr.EventTarget.prototype;
-
-/**
- * Called when an error occurs. Dispatches an event.
- * @param {!hotword.constants.Error} error
- * @private
- */
-NaClManager.prototype.handleError_ = function(error) {
-  var event = new Event(hotword.constants.Event.ERROR);
-  event.data = error;
-  this.dispatchEvent(event);
-};
-
-/**
- * Record the result of loading the NaCl plugin to UMA.
- * @param {!hotword.constants.UmaNaClPluginLoadResult} error
- * @private
- */
-NaClManager.prototype.logPluginLoadResult_ = function(error) {
-  hotword.metrics.recordEnum(
-      hotword.constants.UmaMetrics.NACL_PLUGIN_LOAD_RESULT,
-      error,
-      UmaNaClPluginLoadResult_.MAX);
-};
-
-/**
- * Set a timeout. Only allow one timeout to exist at any given time.
- * @param {!function()} func
- * @param {number} timeout
- * @private
- */
-NaClManager.prototype.setTimeout_ = function(func, timeout) {
-  assert(!this.naclTimeoutId_, 'Timeout already exists');
-  this.naclTimeoutId_ = window.setTimeout(
-      function() {
-        this.naclTimeoutId_ = null;
-        func();
-      }.bind(this), timeout);
-};
-
-/**
- * Clears the current timeout.
- * @private
- */
-NaClManager.prototype.clearTimeout_ = function() {
-  window.clearTimeout(this.naclTimeoutId_);
-  this.naclTimeoutId_ = null;
-};
-
-/**
- * Starts a stopped or stopping hotword recognizer (NaCl plugin).
- * @param {hotword.constants.RecognizerStartMode} mode The mode to start the
- *     recognizer in.
- */
-NaClManager.prototype.startRecognizer = function(mode) {
-  this.startMode_ = mode;
-  if (this.recognizerState_ == ManagerState_.STOPPED) {
-    this.preambleLog_ = null;
-    this.recognizerState_ = ManagerState_.STARTING;
-    if (mode == hotword.constants.RecognizerStartMode.NEW_MODEL) {
-      hotword.debug('Starting Recognizer in START training mode');
-      this.sendDataToPlugin_(hotword.constants.NaClPlugin.BEGIN_SPEAKER_MODEL);
-    } else if (mode == hotword.constants.RecognizerStartMode.ADAPT_MODEL) {
-      hotword.debug('Starting Recognizer in ADAPT training mode');
-      this.sendDataToPlugin_(hotword.constants.NaClPlugin.ADAPT_SPEAKER_MODEL);
-    } else {
-      hotword.debug('Starting Recognizer in NORMAL mode');
-      this.sendDataToPlugin_(hotword.constants.NaClPlugin.RESTART);
-    }
-    // Normally, there would be a waitForMessage_(READY_FOR_AUDIO) here.
-    // However, this message is sent the first time audio data is read and in
-    // some cases (ie. using the hotword stream), this won't happen until a
-    // potential hotword trigger is seen. Having a waitForMessage_() would time
-    // out in this case, so just leave it out. This ends up sacrificing a bit of
-    // error detection in the non-hotword-stream case, but I think we can live
-    // with that.
-  } else if (this.recognizerState_ == ManagerState_.STOPPING) {
-    // Wait until the plugin is stopped before trying to start it.
-    this.restartOnStop_ = true;
-  } else {
-    throw 'Attempting to start NaCl recogniser not in STOPPED or STOPPING ' +
-        'state';
-  }
-};
-
-/**
- * Stops the hotword recognizer.
- */
-NaClManager.prototype.stopRecognizer = function() {
-  if (this.recognizerState_ == ManagerState_.STARTING) {
-    // If the recognizer is stopped before it finishes starting, it causes an
-    // assertion to be raised in waitForMessage_() since we're waiting for the
-    // READY_FOR_AUDIO message. Clear the current timeout and expecting message
-    // since we no longer expect it and may never receive it.
-    this.clearTimeout_();
+    /**
+     * The expected message that will cancel the current timeout.
+     * @private {?string}
+     */
     this.expectingMessage_ = null;
-  }
-  this.sendDataToPlugin_(hotword.constants.NaClPlugin.STOP);
-  this.recognizerState_ = ManagerState_.STOPPING;
-  this.waitForMessage_(hotword.constants.TimeoutMs.NORMAL,
-                       hotword.constants.NaClPlugin.STOPPED);
-};
 
-/**
- * Saves the speaker model.
- */
-NaClManager.prototype.finalizeSpeakerModel = function() {
-  if (this.recognizerState_ == ManagerState_.UNINITIALIZED ||
-      this.recognizerState_ == ManagerState_.ERROR ||
-      this.recognizerState_ == ManagerState_.SHUTDOWN ||
-      this.recognizerState_ == ManagerState_.LOADING) {
-    return;
-  }
-  this.sendDataToPlugin_(hotword.constants.NaClPlugin.FINISH_SPEAKER_MODEL);
-};
+    /**
+     * Whether the plugin will be started as soon as it stops.
+     * @private {boolean}
+     */
+    this.restartOnStop_ = false;
 
-/**
+    /**
+     * NaCl plugin element on extension background page.
+     * @private {?HTMLEmbedElement}
+     */
+    this.plugin_ = null;
+
+    /**
+     * URL containing hotword-model data file.
+     * @private {string}
+     */
+    this.modelUrl_ = '';
+
+    /**
+     * Media stream containing an audio input track.
+     * @private {?MediaStream}
+     */
+    this.stream_ = null;
+
+    /**
+     * The mode to start the recognizer in.
+     * @private {?chrome.hotwordPrivate.RecognizerStartMode}
+     */
+    this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL;
+
+    /**
+     * Whether audio logging is enabled.
+     * @private {boolean}
+     */
+    this.loggingEnabled_ = loggingEnabled;
+
+    /**
+     * Whether the audio input stream is from a hotword stream.
+     * @private {boolean}
+     */
+    this.hotwordStream_ = hotwordStream;
+
+    /**
+     * Audio log of X seconds before hotword triggered.
+     * @private {?Object}
+     */
+    this.preambleLog_ = null;
+  };
+
+  /**
+   * States this manager can be in. Since messages to/from the plugin are
+   * asynchronous (and potentially queued), it's not possible to know what state
+   * the plugin is in. However, track a state machine for NaClManager based on
+   * what messages are sent/received.
+   * @enum {number}
+   * @private
+   */
+  NaClManager.ManagerState_ = {
+    UNINITIALIZED: 0,
+    LOADING: 1,
+    STOPPING: 2,
+    STOPPED: 3,
+    STARTING: 4,
+    RUNNING: 5,
+    ERROR: 6,
+    SHUTDOWN: 7,
+  };
+  var ManagerState_ = NaClManager.ManagerState_;
+  var Error_ = hotword.constants.Error;
+  var UmaNaClMessageTimeout_ = hotword.constants.UmaNaClMessageTimeout;
+  var UmaNaClPluginLoadResult_ = hotword.constants.UmaNaClPluginLoadResult;
+
+  NaClManager.prototype.__proto__ = cr.EventTarget.prototype;
+
+  /**
+   * Called when an error occurs. Dispatches an event.
+   * @param {!hotword.constants.Error} error
+   * @private
+   */
+  NaClManager.prototype.handleError_ = function(error) {
+    var event = new Event(hotword.constants.Event.ERROR);
+    event.data = error;
+    this.dispatchEvent(event);
+  };
+
+  /**
+   * Record the result of loading the NaCl plugin to UMA.
+   * @param {!hotword.constants.UmaNaClPluginLoadResult} error
+   * @private
+   */
+  NaClManager.prototype.logPluginLoadResult_ = function(error) {
+    hotword.metrics.recordEnum(
+        hotword.constants.UmaMetrics.NACL_PLUGIN_LOAD_RESULT, error,
+        UmaNaClPluginLoadResult_.MAX);
+  };
+
+  /**
+   * Set a timeout. Only allow one timeout to exist at any given time.
+   * @param {!function()} func
+   * @param {number} timeout
+   * @private
+   */
+  NaClManager.prototype.setTimeout_ = function(func, timeout) {
+    assert(!this.naclTimeoutId_, 'Timeout already exists');
+    this.naclTimeoutId_ = window.setTimeout(function() {
+      this.naclTimeoutId_ = null;
+      func();
+    }.bind(this), timeout);
+  };
+
+  /**
+   * Clears the current timeout.
+   * @private
+   */
+  NaClManager.prototype.clearTimeout_ = function() {
+    window.clearTimeout(this.naclTimeoutId_);
+    this.naclTimeoutId_ = null;
+  };
+
+  /**
+   * Starts a stopped or stopping hotword recognizer (NaCl plugin).
+   * @param {hotword.constants.RecognizerStartMode} mode The mode to start the
+   *     recognizer in.
+   */
+  NaClManager.prototype.startRecognizer = function(mode) {
+    this.startMode_ = mode;
+    if (this.recognizerState_ == ManagerState_.STOPPED) {
+      this.preambleLog_ = null;
+      this.recognizerState_ = ManagerState_.STARTING;
+      if (mode == hotword.constants.RecognizerStartMode.NEW_MODEL) {
+        hotword.debug('Starting Recognizer in START training mode');
+        this.sendDataToPlugin_(
+            hotword.constants.NaClPlugin.BEGIN_SPEAKER_MODEL);
+      } else if (mode == hotword.constants.RecognizerStartMode.ADAPT_MODEL) {
+        hotword.debug('Starting Recognizer in ADAPT training mode');
+        this.sendDataToPlugin_(
+            hotword.constants.NaClPlugin.ADAPT_SPEAKER_MODEL);
+      } else {
+        hotword.debug('Starting Recognizer in NORMAL mode');
+        this.sendDataToPlugin_(hotword.constants.NaClPlugin.RESTART);
+      }
+      // Normally, there would be a waitForMessage_(READY_FOR_AUDIO) here.
+      // However, this message is sent the first time audio data is read and in
+      // some cases (ie. using the hotword stream), this won't happen until a
+      // potential hotword trigger is seen. Having a waitForMessage_() would
+      // time
+      // out in this case, so just leave it out. This ends up sacrificing a bit
+      // of
+      // error detection in the non-hotword-stream case, but I think we can live
+      // with that.
+    } else if (this.recognizerState_ == ManagerState_.STOPPING) {
+      // Wait until the plugin is stopped before trying to start it.
+      this.restartOnStop_ = true;
+    } else {
+      throw 'Attempting to start NaCl recogniser not in STOPPED or STOPPING ' +
+          'state';
+    }
+  };
+
+  /**
+   * Stops the hotword recognizer.
+   */
+  NaClManager.prototype.stopRecognizer = function() {
+    if (this.recognizerState_ == ManagerState_.STARTING) {
+      // If the recognizer is stopped before it finishes starting, it causes an
+      // assertion to be raised in waitForMessage_() since we're waiting for the
+      // READY_FOR_AUDIO message. Clear the current timeout and expecting
+      // message
+      // since we no longer expect it and may never receive it.
+      this.clearTimeout_();
+      this.expectingMessage_ = null;
+    }
+    this.sendDataToPlugin_(hotword.constants.NaClPlugin.STOP);
+    this.recognizerState_ = ManagerState_.STOPPING;
+    this.waitForMessage_(
+        hotword.constants.TimeoutMs.NORMAL,
+        hotword.constants.NaClPlugin.STOPPED);
+  };
+
+  /**
+   * Saves the speaker model.
+   */
+  NaClManager.prototype.finalizeSpeakerModel = function() {
+    if (this.recognizerState_ == ManagerState_.UNINITIALIZED ||
+        this.recognizerState_ == ManagerState_.ERROR ||
+        this.recognizerState_ == ManagerState_.SHUTDOWN ||
+        this.recognizerState_ == ManagerState_.LOADING) {
+      return;
+    }
+    this.sendDataToPlugin_(hotword.constants.NaClPlugin.FINISH_SPEAKER_MODEL);
+  };
+
+  /**
  * Checks whether the file at the given path exists.
  * @param {!string} path Path to a file. Can be any valid URL.
  * @return {boolean} True if the patch exists.
  * @private
  */
-NaClManager.prototype.fileExists_ = function(path) {
-  var xhr = new XMLHttpRequest();
-  xhr.open('HEAD', path, false);
-  try {
-    xhr.send();
-  } catch (err) {
-    return false;
-  }
-  if (xhr.readyState != xhr.DONE || xhr.status != 200) {
-    return false;
-  }
-  return true;
-};
+  NaClManager.prototype.fileExists_ = function(path) {
+    var xhr = new XMLHttpRequest();
+    xhr.open('HEAD', path, false);
+    try {
+      xhr.send();
+    } catch (err) {
+      return false;
+    }
+    if (xhr.readyState != xhr.DONE || xhr.status != 200) {
+      return false;
+    }
+    return true;
+  };
 
-/**
+  /**
  * Creates and returns a list of possible languages to check for hotword
  * support.
  * @return {!Array<string>} Array of languages.
  * @private
  */
-NaClManager.prototype.getPossibleLanguages_ = function() {
-  // Create array used to search first for language-country, if not found then
-  // search for language, if not found then no language (empty string).
-  // For example, search for 'en-us', then 'en', then ''.
-  var langs = new Array();
-  if (hotword.constants.UI_LANGUAGE) {
-    // Chrome webstore doesn't support uppercase path: crbug.com/353407
-    var language = hotword.constants.UI_LANGUAGE.toLowerCase();
-    langs.push(language);  // Example: 'en-us'.
-    // Remove country to add just the language to array.
-    var hyphen = language.lastIndexOf('-');
-    if (hyphen >= 0) {
-      langs.push(language.substr(0, hyphen));  // Example: 'en'.
+  NaClManager.prototype.getPossibleLanguages_ = function() {
+    // Create array used to search first for language-country, if not found then
+    // search for language, if not found then no language (empty string).
+    // For example, search for 'en-us', then 'en', then ''.
+    var langs = new Array();
+    if (hotword.constants.UI_LANGUAGE) {
+      // Chrome webstore doesn't support uppercase path: crbug.com/353407
+      var language = hotword.constants.UI_LANGUAGE.toLowerCase();
+      langs.push(language);  // Example: 'en-us'.
+      // Remove country to add just the language to array.
+      var hyphen = language.lastIndexOf('-');
+      if (hyphen >= 0) {
+        langs.push(language.substr(0, hyphen));  // Example: 'en'.
+      }
     }
-  }
-  langs.push('');
-  return langs;
-};
+    langs.push('');
+    return langs;
+  };
 
-/**
+  /**
  * Creates a NaCl plugin object and attaches it to the page.
  * @param {!string} src Location of the plugin.
  * @return {!HTMLEmbedElement} NaCl plugin DOM object.
  * @private
  */
-NaClManager.prototype.createPlugin_ = function(src) {
-  var plugin = /** @type {HTMLEmbedElement} */(document.createElement('embed'));
-  plugin.src = src;
-  plugin.type = 'application/x-nacl';
-  document.body.appendChild(plugin);
-  return plugin;
-};
+  NaClManager.prototype.createPlugin_ = function(src) {
+    var plugin =
+        /** @type {HTMLEmbedElement} */ (document.createElement('embed'));
+    plugin.src = src;
+    plugin.type = 'application/x-nacl';
+    document.body.appendChild(plugin);
+    return plugin;
+  };
 
-/**
+  /**
  * Initializes the NaCl manager.
  * @param {!string} naclArch Either 'arm', 'x86-32' or 'x86-64'.
  * @param {!MediaStream} stream A stream containing an audio source track.
  * @return {boolean} True if the successful.
  */
-NaClManager.prototype.initialize = function(naclArch, stream) {
-  assert(this.recognizerState_ == ManagerState_.UNINITIALIZED,
-         'Recognizer not in uninitialized state. State: ' +
-         this.recognizerState_);
-  assert(this.plugin_ == null);
-  var langs = this.getPossibleLanguages_();
-  var i, j;
-  // For country-lang variations. For example, when combined with path it will
-  // attempt to find: '/x86-32_en-gb/', else '/x86-32_en/', else '/x86-32_/'.
-  for (i = 0; i < langs.length; i++) {
-    var folder = hotword.constants.SHARED_MODULE_ROOT + '/_platform_specific/' +
-        naclArch + '_' + langs[i] + '/';
-    var dataSrc = folder + hotword.constants.File.RECOGNIZER_CONFIG;
-    var pluginSrc = hotword.constants.SHARED_MODULE_ROOT + '/hotword_' +
-        langs[i] + '.nmf';
-    var dataExists = this.fileExists_(dataSrc) && this.fileExists_(pluginSrc);
-    if (!dataExists) {
-      continue;
-    }
+  NaClManager.prototype.initialize = function(naclArch, stream) {
+    assert(
+        this.recognizerState_ == ManagerState_.UNINITIALIZED,
+        'Recognizer not in uninitialized state. State: ' +
+            this.recognizerState_);
+    assert(this.plugin_ == null);
+    var langs = this.getPossibleLanguages_();
+    var i, j;
+    // For country-lang variations. For example, when combined with path it will
+    // attempt to find: '/x86-32_en-gb/', else '/x86-32_en/', else '/x86-32_/'.
+    for (i = 0; i < langs.length; i++) {
+      var folder = hotword.constants.SHARED_MODULE_ROOT +
+          '/_platform_specific/' + naclArch + '_' + langs[i] + '/';
+      var dataSrc = folder + hotword.constants.File.RECOGNIZER_CONFIG;
+      var pluginSrc = hotword.constants.SHARED_MODULE_ROOT + '/hotword_' +
+          langs[i] + '.nmf';
+      var dataExists = this.fileExists_(dataSrc) && this.fileExists_(pluginSrc);
+      if (!dataExists) {
+        continue;
+      }
 
-    var plugin = this.createPlugin_(pluginSrc);
-    if (!plugin || !plugin.postMessage) {
-      document.body.removeChild(plugin);
-      this.recognizerState_ = ManagerState_.ERROR;
-      return false;
-    }
-    this.plugin_ = plugin;
-    this.modelUrl_ = chrome.extension.getURL(dataSrc);
-    this.stream_ = stream;
-    this.recognizerState_ = ManagerState_.LOADING;
-
-    plugin.addEventListener('message',
-                            this.handlePluginMessage_.bind(this),
-                            false);
-
-    plugin.addEventListener('crash',
-                            function() {
-                              this.handleError_(Error_.NACL_CRASH);
-                              this.logPluginLoadResult_(
-                                  UmaNaClPluginLoadResult_.CRASH);
-                            }.bind(this),
-                            false);
-    return true;
-  }
-  this.recognizerState_ = ManagerState_.ERROR;
-  this.logPluginLoadResult_(UmaNaClPluginLoadResult_.NO_MODULE_FOUND);
-  return false;
-};
-
-/**
- * Shuts down the NaCl plugin and frees all resources.
- */
-NaClManager.prototype.shutdown = function() {
-  if (this.plugin_ != null) {
-    document.body.removeChild(this.plugin_);
-    this.plugin_ = null;
-  }
-  this.clearTimeout_();
-  this.recognizerState_ = ManagerState_.SHUTDOWN;
-  if (this.stream_)
-    this.stream_.getAudioTracks()[0].stop();
-  this.stream_ = null;
-};
-
-/**
- * Sends data to the NaCl plugin.
- * @param {!string|!MediaStreamTrack} data Command to be sent to NaCl plugin.
- * @private
- */
-NaClManager.prototype.sendDataToPlugin_ = function(data) {
-  assert(this.recognizerState_ != ManagerState_.UNINITIALIZED,
-         'Recognizer in uninitialized state');
-  this.plugin_.postMessage(data);
-};
-
-/**
- * Waits, with a timeout, for a message to be received from the plugin. If the
- * message is not seen within the timeout, dispatch an 'error' event and go into
- * the ERROR state.
- * @param {number} timeout Timeout, in milliseconds, to wait for the message.
- * @param {!string} message Message to wait for.
- * @private
- */
-NaClManager.prototype.waitForMessage_ = function(timeout, message) {
-  assert(this.expectingMessage_ == null, 'Cannot wait for message: ' +
-      message + ', already waiting for message ' + this.expectingMessage_);
-  this.setTimeout_(
-      function() {
+      var plugin = this.createPlugin_(pluginSrc);
+      if (!plugin || !plugin.postMessage) {
+        document.body.removeChild(plugin);
         this.recognizerState_ = ManagerState_.ERROR;
-        this.handleError_(Error_.TIMEOUT);
-        switch (this.expectingMessage_) {
-          case hotword.constants.NaClPlugin.REQUEST_MODEL:
-            var metricValue = UmaNaClMessageTimeout_.REQUEST_MODEL;
-            break;
-          case hotword.constants.NaClPlugin.MODEL_LOADED:
-            var metricValue = UmaNaClMessageTimeout_.MODEL_LOADED;
-            break;
-          case hotword.constants.NaClPlugin.READY_FOR_AUDIO:
-            var metricValue = UmaNaClMessageTimeout_.READY_FOR_AUDIO;
-            break;
-          case hotword.constants.NaClPlugin.STOPPED:
-            var metricValue = UmaNaClMessageTimeout_.STOPPED;
-            break;
-          case hotword.constants.NaClPlugin.HOTWORD_DETECTED:
-            var metricValue = UmaNaClMessageTimeout_.HOTWORD_DETECTED;
-            break;
-          case hotword.constants.NaClPlugin.MS_CONFIGURED:
-            var metricValue = UmaNaClMessageTimeout_.MS_CONFIGURED;
-            break;
-        }
-        hotword.metrics.recordEnum(
-            hotword.constants.UmaMetrics.NACL_MESSAGE_TIMEOUT,
-            metricValue,
-            UmaNaClMessageTimeout_.MAX);
-      }.bind(this), timeout);
-  this.expectingMessage_ = message;
-};
+        return false;
+      }
+      this.plugin_ = plugin;
+      this.modelUrl_ = chrome.extension.getURL(dataSrc);
+      this.stream_ = stream;
+      this.recognizerState_ = ManagerState_.LOADING;
 
-/**
- * Called when a message is received from the plugin. If we're waiting for that
- * message, cancel the pending timeout.
- * @param {string} message Message received.
- * @private
- */
-NaClManager.prototype.receivedMessage_ = function(message) {
-  if (message == this.expectingMessage_) {
+      plugin.addEventListener(
+          'message', this.handlePluginMessage_.bind(this), false);
+
+      plugin.addEventListener('crash', function() {
+        this.handleError_(Error_.NACL_CRASH);
+        this.logPluginLoadResult_(UmaNaClPluginLoadResult_.CRASH);
+      }.bind(this), false);
+      return true;
+    }
+    this.recognizerState_ = ManagerState_.ERROR;
+    this.logPluginLoadResult_(UmaNaClPluginLoadResult_.NO_MODULE_FOUND);
+    return false;
+  };
+
+  /**
+   * Shuts down the NaCl plugin and frees all resources.
+   */
+  NaClManager.prototype.shutdown = function() {
+    if (this.plugin_ != null) {
+      document.body.removeChild(this.plugin_);
+      this.plugin_ = null;
+    }
     this.clearTimeout_();
-    this.expectingMessage_ = null;
-  }
-};
+    this.recognizerState_ = ManagerState_.SHUTDOWN;
+    if (this.stream_)
+      this.stream_.getAudioTracks()[0].stop();
+    this.stream_ = null;
+  };
 
-/**
- * Handle a REQUEST_MODEL message from the plugin.
- * The plugin sends this message immediately after starting.
- * @private
- */
-NaClManager.prototype.handleRequestModel_ = function() {
-  if (this.recognizerState_ != ManagerState_.LOADING) {
-    return;
-  }
-  this.logPluginLoadResult_(UmaNaClPluginLoadResult_.SUCCESS);
-  this.sendDataToPlugin_(
-      hotword.constants.NaClPlugin.MODEL_PREFIX + this.modelUrl_);
-  this.waitForMessage_(hotword.constants.TimeoutMs.LONG,
-                       hotword.constants.NaClPlugin.MODEL_LOADED);
+  /**
+   * Sends data to the NaCl plugin.
+   * @param {!string|!MediaStreamTrack} data Command to be sent to NaCl plugin.
+   * @private
+   */
+  NaClManager.prototype.sendDataToPlugin_ = function(data) {
+    assert(
+        this.recognizerState_ != ManagerState_.UNINITIALIZED,
+        'Recognizer in uninitialized state');
+    this.plugin_.postMessage(data);
+  };
 
-  // Configure logging in the plugin. This can be configured any time before
-  // starting the recognizer, and now is as good a time as any.
-  if (this.loggingEnabled_) {
-    this.sendDataToPlugin_(
-        hotword.constants.NaClPlugin.LOG + ':' +
-        hotword.constants.AUDIO_LOG_SECONDS);
-  }
+  /**
+   * Waits, with a timeout, for a message to be received from the plugin. If the
+   * message is not seen within the timeout, dispatch an 'error' event and go
+   * into
+   * the ERROR state.
+   * @param {number} timeout Timeout, in milliseconds, to wait for the message.
+   * @param {!string} message Message to wait for.
+   * @private
+   */
+  NaClManager.prototype.waitForMessage_ = function(timeout, message) {
+    assert(
+        this.expectingMessage_ == null, 'Cannot wait for message: ' + message +
+            ', already waiting for message ' + this.expectingMessage_);
+    this.setTimeout_(function() {
+      this.recognizerState_ = ManagerState_.ERROR;
+      this.handleError_(Error_.TIMEOUT);
+      switch (this.expectingMessage_) {
+        case hotword.constants.NaClPlugin.REQUEST_MODEL:
+          var metricValue = UmaNaClMessageTimeout_.REQUEST_MODEL;
+          break;
+        case hotword.constants.NaClPlugin.MODEL_LOADED:
+          var metricValue = UmaNaClMessageTimeout_.MODEL_LOADED;
+          break;
+        case hotword.constants.NaClPlugin.READY_FOR_AUDIO:
+          var metricValue = UmaNaClMessageTimeout_.READY_FOR_AUDIO;
+          break;
+        case hotword.constants.NaClPlugin.STOPPED:
+          var metricValue = UmaNaClMessageTimeout_.STOPPED;
+          break;
+        case hotword.constants.NaClPlugin.HOTWORD_DETECTED:
+          var metricValue = UmaNaClMessageTimeout_.HOTWORD_DETECTED;
+          break;
+        case hotword.constants.NaClPlugin.MS_CONFIGURED:
+          var metricValue = UmaNaClMessageTimeout_.MS_CONFIGURED;
+          break;
+      }
+      hotword.metrics.recordEnum(
+          hotword.constants.UmaMetrics.NACL_MESSAGE_TIMEOUT, metricValue,
+          UmaNaClMessageTimeout_.MAX);
+    }.bind(this), timeout);
+    this.expectingMessage_ = message;
+  };
 
-  // If the audio stream is from a hotword stream, tell the plugin.
-  if (this.hotwordStream_) {
-    this.sendDataToPlugin_(
-        hotword.constants.NaClPlugin.DSP + ':' +
-        hotword.constants.HOTWORD_STREAM_TIMEOUT_SECONDS);
-  }
-};
+  /**
+   * Called when a message is received from the plugin. If we're waiting for
+   * that
+   * message, cancel the pending timeout.
+   * @param {string} message Message received.
+   * @private
+   */
+  NaClManager.prototype.receivedMessage_ = function(message) {
+    if (message == this.expectingMessage_) {
+      this.clearTimeout_();
+      this.expectingMessage_ = null;
+    }
+  };
 
-/**
- * Handle a MODEL_LOADED message from the plugin.
- * The plugin sends this message after successfully loading the language model.
- * @private
- */
-NaClManager.prototype.handleModelLoaded_ = function() {
-  if (this.recognizerState_ != ManagerState_.LOADING) {
-    return;
-  }
-  this.sendDataToPlugin_(this.stream_.getAudioTracks()[0]);
-  // The plugin will send a MS_CONFIGURED, but don't set a timeout waiting for
-  // it. MediaStreamAudioTrack::Configure() will remain pending until the first
-  // audio buffer is received. When the audio source is a DSP for always-on
-  // detection, no audio sample is sent until the DSP detects a potential
-  // hotword trigger. Thus, Configure would remain pending indefinitely if we
-  // were to wait here. See https://crbug.com/616203
-};
-
-/**
- * Handle a MS_CONFIGURED message from the plugin.
- * The plugin sends this message after successfully configuring the audio input
- * stream.
- * @private
- */
-NaClManager.prototype.handleMsConfigured_ = function() {
-  if (this.recognizerState_ != ManagerState_.LOADING) {
-    return;
-  }
-  this.recognizerState_ = ManagerState_.STOPPED;
-  this.dispatchEvent(new Event(hotword.constants.Event.READY));
-};
-
-/**
- * Handle a READY_FOR_AUDIO message from the plugin.
- * The plugin sends this message after the recognizer is started and
- * successfully receives and processes audio data.
- * @private
- */
-NaClManager.prototype.handleReadyForAudio_ = function() {
-  if (this.recognizerState_ != ManagerState_.STARTING) {
-    return;
-  }
-  this.recognizerState_ = ManagerState_.RUNNING;
-};
-
-/**
- * Handle a HOTWORD_DETECTED message from the plugin.
- * The plugin sends this message after detecting the hotword.
- * @private
- */
-NaClManager.prototype.handleHotwordDetected_ = function() {
-  if (this.recognizerState_ != ManagerState_.RUNNING) {
-    return;
-  }
-  // We'll receive a STOPPED message very soon.
-  this.recognizerState_ = ManagerState_.STOPPING;
-  this.waitForMessage_(hotword.constants.TimeoutMs.NORMAL,
-                       hotword.constants.NaClPlugin.STOPPED);
-  var event = new Event(hotword.constants.Event.TRIGGER);
-  event.log = this.preambleLog_;
-  this.dispatchEvent(event);
-};
-
-/**
- * Handle a STOPPED message from the plugin.
- * This plugin sends this message after stopping the recognizer. This can happen
- * either in response to a stop request, or after the hotword is detected.
- * @private
- */
-NaClManager.prototype.handleStopped_ = function() {
-  this.recognizerState_ = ManagerState_.STOPPED;
-  if (this.restartOnStop_) {
-    this.restartOnStop_ = false;
-    this.startRecognizer(this.startMode_);
-  }
-};
-
-/**
- * Handle a TIMEOUT message from the plugin.
- * The plugin sends this message when it thinks the stream is from a DSP and
- * a hotword wasn't detected within a timeout period after arrival of the first
- * audio samples.
- * @private
- */
-NaClManager.prototype.handleTimeout_ = function() {
-  if (this.recognizerState_ != ManagerState_.RUNNING) {
-    return;
-  }
-  this.recognizerState_ = ManagerState_.STOPPED;
-  this.dispatchEvent(new Event(hotword.constants.Event.TIMEOUT));
-};
-
-/**
- * Handle a SPEAKER_MODEL_SAVED message from the plugin.
- * The plugin sends this message after writing the model to a file.
- * @private
- */
-NaClManager.prototype.handleSpeakerModelSaved_ = function() {
-  this.dispatchEvent(new Event(hotword.constants.Event.SPEAKER_MODEL_SAVED));
-};
-
-/**
- * Handles a message from the NaCl plugin.
- * @param {!Event} msg Message from NaCl plugin.
- * @private
- */
-NaClManager.prototype.handlePluginMessage_ = function(msg) {
-  if (msg['data']) {
-    if (typeof(msg['data']) == 'object') {
-      // Save the preamble for delivery to the trigger handler when the trigger
-      // message arrives.
-      this.preambleLog_ = msg['data'];
+  /**
+   * Handle a REQUEST_MODEL message from the plugin.
+   * The plugin sends this message immediately after starting.
+   * @private
+   */
+  NaClManager.prototype.handleRequestModel_ = function() {
+    if (this.recognizerState_ != ManagerState_.LOADING) {
       return;
     }
-    this.receivedMessage_(msg['data']);
-    switch (msg['data']) {
-      case hotword.constants.NaClPlugin.REQUEST_MODEL:
-        this.handleRequestModel_();
-        break;
-      case hotword.constants.NaClPlugin.MODEL_LOADED:
-        this.handleModelLoaded_();
-        break;
-      case hotword.constants.NaClPlugin.MS_CONFIGURED:
-        this.handleMsConfigured_();
-        break;
-      case hotword.constants.NaClPlugin.READY_FOR_AUDIO:
-        this.handleReadyForAudio_();
-        break;
-      case hotword.constants.NaClPlugin.HOTWORD_DETECTED:
-        this.handleHotwordDetected_();
-        break;
-      case hotword.constants.NaClPlugin.STOPPED:
-        this.handleStopped_();
-        break;
-      case hotword.constants.NaClPlugin.TIMEOUT:
-        this.handleTimeout_();
-        break;
-      case hotword.constants.NaClPlugin.SPEAKER_MODEL_SAVED:
-        this.handleSpeakerModelSaved_();
-        break;
-    }
-  }
-};
+    this.logPluginLoadResult_(UmaNaClPluginLoadResult_.SUCCESS);
+    this.sendDataToPlugin_(
+        hotword.constants.NaClPlugin.MODEL_PREFIX + this.modelUrl_);
+    this.waitForMessage_(
+        hotword.constants.TimeoutMs.LONG,
+        hotword.constants.NaClPlugin.MODEL_LOADED);
 
-return {
-  NaClManager: NaClManager
-};
+    // Configure logging in the plugin. This can be configured any time before
+    // starting the recognizer, and now is as good a time as any.
+    if (this.loggingEnabled_) {
+      this.sendDataToPlugin_(
+          hotword.constants.NaClPlugin.LOG + ':' +
+          hotword.constants.AUDIO_LOG_SECONDS);
+    }
+
+    // If the audio stream is from a hotword stream, tell the plugin.
+    if (this.hotwordStream_) {
+      this.sendDataToPlugin_(
+          hotword.constants.NaClPlugin.DSP + ':' +
+          hotword.constants.HOTWORD_STREAM_TIMEOUT_SECONDS);
+    }
+  };
+
+  /**
+   * Handle a MODEL_LOADED message from the plugin.
+   * The plugin sends this message after successfully loading the language
+   * model.
+   * @private
+   */
+  NaClManager.prototype.handleModelLoaded_ = function() {
+    if (this.recognizerState_ != ManagerState_.LOADING) {
+      return;
+    }
+    this.sendDataToPlugin_(this.stream_.getAudioTracks()[0]);
+    // The plugin will send a MS_CONFIGURED, but don't set a timeout waiting for
+    // it. MediaStreamAudioTrack::Configure() will remain pending until the
+    // first
+    // audio buffer is received. When the audio source is a DSP for always-on
+    // detection, no audio sample is sent until the DSP detects a potential
+    // hotword trigger. Thus, Configure would remain pending indefinitely if we
+    // were to wait here. See https://crbug.com/616203
+  };
+
+  /**
+   * Handle a MS_CONFIGURED message from the plugin.
+   * The plugin sends this message after successfully configuring the audio
+   * input
+   * stream.
+   * @private
+   */
+  NaClManager.prototype.handleMsConfigured_ = function() {
+    if (this.recognizerState_ != ManagerState_.LOADING) {
+      return;
+    }
+    this.recognizerState_ = ManagerState_.STOPPED;
+    this.dispatchEvent(new Event(hotword.constants.Event.READY));
+  };
+
+  /**
+   * Handle a READY_FOR_AUDIO message from the plugin.
+   * The plugin sends this message after the recognizer is started and
+   * successfully receives and processes audio data.
+   * @private
+   */
+  NaClManager.prototype.handleReadyForAudio_ = function() {
+    if (this.recognizerState_ != ManagerState_.STARTING) {
+      return;
+    }
+    this.recognizerState_ = ManagerState_.RUNNING;
+  };
+
+  /**
+   * Handle a HOTWORD_DETECTED message from the plugin.
+   * The plugin sends this message after detecting the hotword.
+   * @private
+   */
+  NaClManager.prototype.handleHotwordDetected_ = function() {
+    if (this.recognizerState_ != ManagerState_.RUNNING) {
+      return;
+    }
+    // We'll receive a STOPPED message very soon.
+    this.recognizerState_ = ManagerState_.STOPPING;
+    this.waitForMessage_(
+        hotword.constants.TimeoutMs.NORMAL,
+        hotword.constants.NaClPlugin.STOPPED);
+    var event = new Event(hotword.constants.Event.TRIGGER);
+    event.log = this.preambleLog_;
+    this.dispatchEvent(event);
+  };
+
+  /**
+   * Handle a STOPPED message from the plugin.
+   * This plugin sends this message after stopping the recognizer. This can
+   * happen
+   * either in response to a stop request, or after the hotword is detected.
+   * @private
+   */
+  NaClManager.prototype.handleStopped_ = function() {
+    this.recognizerState_ = ManagerState_.STOPPED;
+    if (this.restartOnStop_) {
+      this.restartOnStop_ = false;
+      this.startRecognizer(this.startMode_);
+    }
+  };
+
+  /**
+   * Handle a TIMEOUT message from the plugin.
+   * The plugin sends this message when it thinks the stream is from a DSP and
+   * a hotword wasn't detected within a timeout period after arrival of the
+   * first
+   * audio samples.
+   * @private
+   */
+  NaClManager.prototype.handleTimeout_ = function() {
+    if (this.recognizerState_ != ManagerState_.RUNNING) {
+      return;
+    }
+    this.recognizerState_ = ManagerState_.STOPPED;
+    this.dispatchEvent(new Event(hotword.constants.Event.TIMEOUT));
+  };
+
+  /**
+   * Handle a SPEAKER_MODEL_SAVED message from the plugin.
+   * The plugin sends this message after writing the model to a file.
+   * @private
+   */
+  NaClManager.prototype.handleSpeakerModelSaved_ = function() {
+    this.dispatchEvent(new Event(hotword.constants.Event.SPEAKER_MODEL_SAVED));
+  };
+
+  /**
+   * Handles a message from the NaCl plugin.
+   * @param {!Event} msg Message from NaCl plugin.
+   * @private
+   */
+  NaClManager.prototype.handlePluginMessage_ = function(msg) {
+    if (msg['data']) {
+      if (typeof(msg['data']) == 'object') {
+        // Save the preamble for delivery to the trigger handler when the
+        // trigger
+        // message arrives.
+        this.preambleLog_ = msg['data'];
+        return;
+      }
+      this.receivedMessage_(msg['data']);
+      switch (msg['data']) {
+        case hotword.constants.NaClPlugin.REQUEST_MODEL:
+          this.handleRequestModel_();
+          break;
+        case hotword.constants.NaClPlugin.MODEL_LOADED:
+          this.handleModelLoaded_();
+          break;
+        case hotword.constants.NaClPlugin.MS_CONFIGURED:
+          this.handleMsConfigured_();
+          break;
+        case hotword.constants.NaClPlugin.READY_FOR_AUDIO:
+          this.handleReadyForAudio_();
+          break;
+        case hotword.constants.NaClPlugin.HOTWORD_DETECTED:
+          this.handleHotwordDetected_();
+          break;
+        case hotword.constants.NaClPlugin.STOPPED:
+          this.handleStopped_();
+          break;
+        case hotword.constants.NaClPlugin.TIMEOUT:
+          this.handleTimeout_();
+          break;
+        case hotword.constants.NaClPlugin.SPEAKER_MODEL_SAVED:
+          this.handleSpeakerModelSaved_();
+          break;
+      }
+    }
+  };
+
+  return {NaClManager: NaClManager};
 
 });
diff --git a/chrome/browser/resources/hotword/page_audio_manager.js b/chrome/browser/resources/hotword/page_audio_manager.js
index d5cdc86..5d45d00 100644
--- a/chrome/browser/resources/hotword/page_audio_manager.js
+++ b/chrome/browser/resources/hotword/page_audio_manager.js
@@ -62,13 +62,10 @@
      * @private
      */
     checkUrlPathIsEligible_: function(url, base) {
-      if (url == base ||
-          url == base + '/' ||
+      if (url == base || url == base + '/' ||
           url.startsWith(base + '/_/chrome/newtab?') ||  // Appcache NTP.
-          url.startsWith(base + '/?') ||
-          url.startsWith(base + '/#') ||
-          url.startsWith(base + '/webhp') ||
-          url.startsWith(base + '/search') ||
+          url.startsWith(base + '/?') || url.startsWith(base + '/#') ||
+          url.startsWith(base + '/webhp') || url.startsWith(base + '/search') ||
           url.startsWith(base + '/imghp')) {
         return true;
       }
@@ -87,28 +84,13 @@
         return false;
 
       var baseGoogleUrls = [
-        'https://encrypted.google.',
-        'https://images.google.',
+        'https://encrypted.google.', 'https://images.google.',
         'https://www.google.'
       ];
       // TODO(amistry): Get this list from a file in the shared module instead.
       var tlds = [
-        'at',
-        'ca',
-        'com',
-        'com.au',
-        'com.mx',
-        'com.br',
-        'co.jp',
-        'co.kr',
-        'co.nz',
-        'co.uk',
-        'co.za',
-        'de',
-        'es',
-        'fr',
-        'it',
-        'ru'
+        'at', 'ca', 'com', 'com.au', 'com.mx', 'com.br', 'co.jp', 'co.kr',
+        'co.nz', 'co.uk', 'co.za', 'de', 'es', 'fr', 'it', 'ru'
       ];
 
       // Check for the new tab page first.
@@ -135,23 +117,21 @@
      * @private
      */
     findCurrentTab_: function(callback) {
-      chrome.windows.getAll(
-          {'populate': true},
-          function(windows) {
-            for (var i = 0; i < windows.length; ++i) {
-              if (!windows[i].focused)
-                continue;
+      chrome.windows.getAll({'populate': true}, function(windows) {
+        for (var i = 0; i < windows.length; ++i) {
+          if (!windows[i].focused)
+            continue;
 
-              for (var j = 0; j < windows[i].tabs.length; ++j) {
-                var tab = windows[i].tabs[j];
-                if (tab.active) {
-                  callback.call(this, tab);
-                  return;
-                }
-              }
+          for (var j = 0; j < windows[i].tabs.length; ++j) {
+            var tab = windows[i].tabs[j];
+            if (tab.active) {
+              callback.call(this, tab);
+              return;
             }
-            callback.call(this, null);
-          }.bind(this));
+          }
+        }
+        callback.call(this, null);
+      }.bind(this));
     },
 
     /**
@@ -182,9 +162,7 @@
         return;
 
       chrome.tabs.executeScript(
-          tab.id,
-          {'file': 'audio_client.js'},
-          function(results) {
+          tab.id, {'file': 'audio_client.js'}, function(results) {
             if (chrome.runtime.lastError) {
               // Ignore this error. For new tab pages, even though the URL is
               // reported to be chrome://newtab/, the actual URL is a
@@ -274,7 +252,7 @@
       if (port.name != hotword.constants.CLIENT_PORT_NAME)
         return;
 
-      var tab = /** @type {!Tab} */(port.sender.tab);
+      var tab = /** @type {!Tab} */ (port.sender.tab);
       // An existing port from the same tab might already exist. But that port
       // may be from the previous page, so just overwrite the port.
       this.portMap_[tab.id] = port;
@@ -358,11 +336,9 @@
      */
     startHotwording_: function() {
       this.stateManager_.startSession(
-          hotword.constants.SessionSource.NTP,
-          function() {
+          hotword.constants.SessionSource.NTP, function() {
             this.sendAllClients_(CommandToPage.HOTWORD_STARTED);
-          }.bind(this),
-          this.hotwordTriggered_.bind(this));
+          }.bind(this), this.hotwordTriggered_.bind(this));
     },
 
     /**
@@ -444,8 +420,7 @@
             chrome.hotwordPrivate.getStatus(
                 true /* getOptionalFields */,
                 this.statusDone_.bind(
-                    this,
-                    request.tab || sender.tab || {incognito: true},
+                    this, request.tab || sender.tab || {incognito: true},
                     sendResponse));
             return true;
           }
@@ -527,8 +502,7 @@
           this.microphoneStateChangedListener_);
       if (chrome.runtime.onMessage.hasListener(this.messageListener_))
         return;
-      chrome.runtime.onMessageExternal.addListener(
-          this.messageListener_);
+      chrome.runtime.onMessageExternal.addListener(this.messageListener_);
     },
 
     /**
@@ -563,7 +537,5 @@
     }
   };
 
-  return {
-    PageAudioManager: PageAudioManager
-  };
+  return {PageAudioManager: PageAudioManager};
 });
diff --git a/chrome/browser/resources/hotword/state_manager.js b/chrome/browser/resources/hotword/state_manager.js
index a7939b1..98e0a5c 100644
--- a/chrome/browser/resources/hotword/state_manager.js
+++ b/chrome/browser/resources/hotword/state_manager.js
@@ -26,10 +26,10 @@
      */
     this.source_ = source;
 
-     /**
-      * Callback invoked when the hotword has triggered.
-      * @private {!function()}
-      */
+    /**
+     * Callback invoked when the hotword has triggered.
+     * @private {!function()}
+     */
     this.triggerCb_ = triggerCb;
 
     /**
@@ -93,7 +93,7 @@
      * @private {!HTMLAudioElement}
      */
     this.chime_ =
-        /** @type {!HTMLAudioElement} */(document.createElement('audio'));
+        /** @type {!HTMLAudioElement} */ (document.createElement('audio'));
 
     /**
      * Chrome event listeners. Saved so that they can be de-registered when
@@ -216,8 +216,8 @@
      * @return {boolean} True if google.com/NTP/launcher hotwording is enabled.
      */
     isSometimesOnEnabled: function() {
-      assert(this.hotwordStatus_,
-             'No hotwording status (isSometimesOnEnabled)');
+      assert(
+          this.hotwordStatus_, 'No hotwording status (isSometimesOnEnabled)');
       // Although the two settings are supposed to be mutually exclusive, it's
       // possible for both to be set. In that case, always-on takes precedence.
       return this.hotwordStatus_.enabled &&
@@ -263,8 +263,7 @@
       if (!this.hotwordStatus_)
         return;
 
-      if (this.hotwordStatus_.enabled ||
-          this.hotwordStatus_.alwaysOnEnabled ||
+      if (this.hotwordStatus_.enabled || this.hotwordStatus_.alwaysOnEnabled ||
           this.hotwordStatus_.trainingEnabled) {
         // Detect changes to audio logging and kill the detector if that setting
         // has changed.
@@ -322,16 +321,16 @@
         this.state_ = State_.STARTING;
         var isHotwordStream = this.isAlwaysOnEnabled() &&
             this.hotwordStatus_.hotwordHardwareAvailable;
-        this.pluginManager_ = new hotword.NaClManager(this.loggingEnabled_,
-                                                      isHotwordStream);
-        this.pluginManager_.addEventListener(hotword.constants.Event.READY,
-                                             this.onReady_.bind(this));
-        this.pluginManager_.addEventListener(hotword.constants.Event.ERROR,
-                                             this.onError_.bind(this));
-        this.pluginManager_.addEventListener(hotword.constants.Event.TRIGGER,
-                                             this.onTrigger_.bind(this));
-        this.pluginManager_.addEventListener(hotword.constants.Event.TIMEOUT,
-                                             this.onTimeout_.bind(this));
+        this.pluginManager_ =
+            new hotword.NaClManager(this.loggingEnabled_, isHotwordStream);
+        this.pluginManager_.addEventListener(
+            hotword.constants.Event.READY, this.onReady_.bind(this));
+        this.pluginManager_.addEventListener(
+            hotword.constants.Event.ERROR, this.onError_.bind(this));
+        this.pluginManager_.addEventListener(
+            hotword.constants.Event.TRIGGER, this.onTrigger_.bind(this));
+        this.pluginManager_.addEventListener(
+            hotword.constants.Event.TIMEOUT, this.onTimeout_.bind(this));
         this.pluginManager_.addEventListener(
             hotword.constants.Event.SPEAKER_MODEL_SAVED,
             this.onSpeakerModelSaved_.bind(this));
@@ -346,10 +345,14 @@
           // detection via a flag, and hence the hotword stream may not be
           // available.
           var constraints = /** @type {googMediaStreamConstraints} */
-              ({audio: {optional: [
-                { googDucking: false },
-                { googHotword: this.isAlwaysOnEnabled() }
-              ]}});
+              ({
+                audio: {
+                  optional: [
+                    {googDucking: false},
+                    {googHotword: this.isAlwaysOnEnabled()}
+                  ]
+                }
+              });
           navigator.webkitGetUserMedia(
               /** @type {MediaStreamConstraints} */ (constraints),
               function(stream) {
@@ -452,8 +455,9 @@
      * started.
      */
     finalizeSpeakerModel: function() {
-      assert(this.pluginManager_,
-             'Cannot finalize speaker model: No NaCl plugin loaded');
+      assert(
+          this.pluginManager_,
+          'Cannot finalize speaker model: No NaCl plugin loaded');
       if (this.state_ != State_.RUNNING) {
         hotword.debug('Cannot finalize speaker model: NaCl plugin not started');
         return;
@@ -577,8 +581,8 @@
      * @param {hotword.constants.RecognizerStartMode=} opt_mode The mode to
      *     start the recognizer in.
      */
-    startSession: function(source, startedCb, triggerCb,
-                           opt_modelSavedCb, opt_mode) {
+    startSession: function(
+        source, startedCb, triggerCb, opt_modelSavedCb, opt_mode) {
       if (this.isTrainingEnabled() && opt_mode) {
         this.startMode_ = opt_mode;
       } else {
@@ -586,8 +590,8 @@
       }
       hotword.debug('Starting session for source: ' + source);
       this.removeSession_(source);
-      this.sessions_.push(new Session_(source, triggerCb, startedCb,
-                                       opt_modelSavedCb));
+      this.sessions_.push(
+          new Session_(source, triggerCb, startedCb, opt_modelSavedCb));
       this.updateStateFromStatus_();
     },
 
@@ -633,7 +637,5 @@
     }
   };
 
-  return {
-    StateManager: StateManager
-  };
+  return {StateManager: StateManager};
 });
diff --git a/chrome/browser/resources/hotword/training_manager.js b/chrome/browser/resources/hotword/training_manager.js
index 15752eb1..f5b68d4 100644
--- a/chrome/browser/resources/hotword/training_manager.js
+++ b/chrome/browser/resources/hotword/training_manager.js
@@ -22,9 +22,8 @@
     this.finalizedSpeakerModelListener_ =
         this.handleFinalizeSpeakerModel_.bind(this);
 
-    hotword.BaseSessionManager.call(this,
-                                    stateManager,
-                                    hotword.constants.SessionSource.TRAINING);
+    hotword.BaseSessionManager.call(
+        this, stateManager, hotword.constants.SessionSource.TRAINING);
   }
 
   /**
@@ -34,14 +33,16 @@
    * @private
    */
   TrainingManager.deleteFiles_ = function(fs) {
-    fs.root.getFile(hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false},
+    fs.root.getFile(
+        hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false},
         TrainingManager.deleteFile_, TrainingManager.fileErrorHandler_);
 
     for (var i = 0; i < hotword.constants.NUM_TRAINING_UTTERANCES; ++i) {
-      fs.root.getFile(hotword.constants.UTTERANCE_FILE_PREFIX + i +
-          hotword.constants.UTTERANCE_FILE_EXTENSION,
-          {create: false},
-          TrainingManager.deleteFile_, TrainingManager.fileErrorHandler_);
+      fs.root.getFile(
+          hotword.constants.UTTERANCE_FILE_PREFIX + i +
+              hotword.constants.UTTERANCE_FILE_EXTENSION,
+          {create: false}, TrainingManager.deleteFile_,
+          TrainingManager.fileErrorHandler_);
     }
   };
 
@@ -59,7 +60,7 @@
         });
       }
       fileEntry.remove(function() {
-          hotword.debug('File removed: ' + fileEntry.fullPath);
+        hotword.debug('File removed: ' + fileEntry.fullPath);
       }, TrainingManager.fileErrorHandler_);
     }
   };
@@ -70,7 +71,7 @@
    * @private
    */
   TrainingManager.fileErrorHandler_ = function(e) {
-      hotword.debug('File error: ' + e.code);
+    hotword.debug('File error: ' + e.code);
   };
 
   /**
@@ -89,7 +90,8 @@
    * @private
    */
   TrainingManager.speakerModelExists_ = function(fs) {
-    fs.root.getFile(hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false},
+    fs.root.getFile(
+        hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false},
         TrainingManager.sendSpeakerModelExistsResponse_,
         TrainingManager.sendNoSpeakerModelResponse_);
   };
@@ -116,29 +118,27 @@
    * Handles a request to delete the speaker model.
    */
   TrainingManager.handleDeleteSpeakerModel = function() {
-    window.webkitRequestFileSystem(PERSISTENT,
-        hotword.constants.FILE_SYSTEM_SIZE_BYTES,
-        TrainingManager.deleteFiles_,
-        TrainingManager.fileErrorHandler_);
+    window.webkitRequestFileSystem(
+        PERSISTENT, hotword.constants.FILE_SYSTEM_SIZE_BYTES,
+        TrainingManager.deleteFiles_, TrainingManager.fileErrorHandler_);
   };
 
   /**
    * Handles a request for the speaker model existence.
    */
   TrainingManager.handleSpeakerModelExists = function() {
-    window.webkitRequestFileSystem(PERSISTENT,
-        hotword.constants.FILE_SYSTEM_SIZE_BYTES,
-        TrainingManager.speakerModelExists_,
-        TrainingManager.fileErrorHandler_);
+    window.webkitRequestFileSystem(
+        PERSISTENT, hotword.constants.FILE_SYSTEM_SIZE_BYTES,
+        TrainingManager.speakerModelExists_, TrainingManager.fileErrorHandler_);
   };
 
   TrainingManager.prototype = {
     __proto__: hotword.BaseSessionManager.prototype,
 
     /** @override */
-     enabled: function() {
-       return this.stateManager.isTrainingEnabled();
-     },
+    enabled: function() {
+      return this.stateManager.isTrainingEnabled();
+    },
 
     /** @override */
     updateListeners: function() {
@@ -175,8 +175,7 @@
             chrome.hotwordPrivate.setHotwordSessionState(true, function() {});
           },
           this.handleHotwordTrigger.bind(this),
-          this.handleSpeakerModelSaved_.bind(this),
-          opt_mode);
+          this.handleSpeakerModelSaved_.bind(this), opt_mode);
     },
 
     /**
@@ -198,7 +197,5 @@
     },
   };
 
-  return {
-    TrainingManager: TrainingManager
-  };
+  return {TrainingManager: TrainingManager};
 });
diff --git a/chrome/browser/resources/net_internals/source_entry.js b/chrome/browser/resources/net_internals/source_entry.js
index bca0e04b..14e98a4 100644
--- a/chrome/browser/resources/net_internals/source_entry.js
+++ b/chrome/browser/resources/net_internals/source_entry.js
@@ -186,8 +186,10 @@
     /**
      * Returns the starting entry for this source. Conceptually this is the
      * first entry that was logged to this source. However, we skip over the
-     * TYPE_REQUEST_ALIVE entries which wrap TYPE_URL_REQUEST_START_JOB
-     * entries.
+     * TYPE_REQUEST_ALIVE entries without parameters which wrap
+     * TYPE_URL_REQUEST_START_JOB entries.  (TYPE_REQUEST_ALIVE may or may not
+     * have parameters depending on what version of Chromium they were
+     * generated from.)
      */
     getStartEntry_: function() {
       if (this.entries_.length < 1)
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index 246ea761..0c92ba54 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -459,9 +459,8 @@
   /** @private */
   onForgetTap_: function() {
     this.networkingPrivate.forgetNetwork(this.guid);
-    // A forgotten WiFi network can still be configured, but not other types.
-    if (this.networkProperties.Type != CrOnc.Type.WI_FI)
-      this.close_();
+    // A forgotten network no longer has a valid GUID, close the subpage.
+    this.close_();
   },
 
   /** @private */
diff --git a/chrome/browser/resources/settings/people_page/setup_pin_dialog.html b/chrome/browser/resources/settings/people_page/setup_pin_dialog.html
index 50d933aa7..3713f67 100644
--- a/chrome/browser/resources/settings/people_page/setup_pin_dialog.html
+++ b/chrome/browser/resources/settings/people_page/setup_pin_dialog.html
@@ -1,3 +1,4 @@
+<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
diff --git a/chrome/browser/resources/settings/people_page/setup_pin_dialog.js b/chrome/browser/resources/settings/people_page/setup_pin_dialog.js
index 28748c0..428b5ae 100644
--- a/chrome/browser/resources/settings/people_page/setup_pin_dialog.js
+++ b/chrome/browser/resources/settings/people_page/setup_pin_dialog.js
@@ -16,14 +16,21 @@
 'use strict';
 
 /**
- * A list of the top-10 most commmonly used PINs. This list is taken from
- * www.datagenetics.com/blog/september32012/.
- * @const
+ * Keep in sync with the string keys provided by settings.
+ * @enum {string}
  */
-var WEAK_PINS = [
-  '1234', '1111', '0000', '1212', '7777', '1004', '2000', '4444', '2222',
-  '6969'
-];
+var MessageType = {
+  TOO_SHORT: 'configurePinTooShort',
+  TOO_LONG: 'configurePinTooLong',
+  TOO_WEAK: 'configurePinWeakPin',
+  MISMATCH: 'configurePinMismatched'
+};
+
+/** @enum {string} */
+var ProblemType = {
+  WARNING: 'warning',
+  ERROR: 'error'
+};
 
 Polymer({
   is: 'settings-setup-pin-dialog',
@@ -80,6 +87,15 @@
       type: Boolean,
       value: false
     },
+
+    /**
+     * Interface for chrome.quickUnlockPrivate calls. May be overriden by tests.
+     * @private
+     */
+    quickUnlockPrivate_: {
+      type: Object,
+      value: chrome.quickUnlockPrivate
+    },
   },
 
   /** @override */
@@ -108,6 +124,7 @@
     this.pinKeyboardValue_ = '';
     this.enableSubmit_ = false;
     this.isConfirmStep_ = false;
+    this.hideProblem_();
     this.onPinChange_();
   },
 
@@ -118,49 +135,38 @@
   },
 
   /**
-   * Returns true if the given PIN is likely easy to guess.
+   * Returns true if the PIN is ready to be changed to a new value.
    * @private
-   * @param {string} pin
    * @return {boolean}
    */
-  isPinWeak_: function(pin) {
-    // Warn if it's a top-10 pin.
-    if (WEAK_PINS.includes(pin))
-      return true;
+  canSubmit_: function() {
+    return this.initialPin_ == this.pinKeyboardValue_;
+  },
 
-    // Warn if the PIN is consecutive digits.
-    var delta = 0;
-    for (var i = 1; i < pin.length; ++i) {
-      var prev = Number(pin[i - 1]);
-      var num = Number(pin[i]);
-      if (Number.isNaN(prev) || Number.isNaN(num))
-        return false;
-      delta = Math.max(delta, Math.abs(num - prev));
+  /**
+   * Handles writting the appropriate message to |problemMessage_|.
+   * @private
+   * @param {string} messageId
+   * @param {chrome.quickUnlockPrivate.CredentialRequirements} requirements
+   *     The requirements received from getCredentialRequirements.
+   */
+  processPinRequirements_: function(messageId, requirements) {
+    var additionalInformation = '';
+    switch (messageId) {
+      case MessageType.TOO_SHORT:
+        additionalInformation = requirements.minLength.toString();
+        break;
+      case MessageType.TOO_LONG:
+        additionalInformation = requirements.maxLength.toString();
+        break;
+      case MessageType.TOO_WEAK:
+      case MessageType.MISMATCH:
+        break;
+      default:
+        assertNotReached();
+        break;
     }
-
-    return delta <= 1;
-  },
-
-  /**
-   * Returns true if the given PIN matches PIN requirements, such as minimum
-   * length.
-   * @private
-   * @param {string|undefined} pin
-   * @return {boolean}
-   */
-  isPinLongEnough_: function(pin) {
-    return !!pin && pin.length >= 4;
-  },
-
-  /**
-   * Returns true if the currently entered PIN is the same as the initially
-   * submitted PIN.
-   * @private
-   * @return {boolean}
-   */
-  isPinConfirmed_: function() {
-    return this.isPinLongEnough_(this.pinKeyboardValue_) &&
-           this.initialPin_ == this.pinKeyboardValue_;
+    this.problemMessage_ = this.i18n(messageId, additionalInformation);
   },
 
   /**
@@ -170,17 +176,14 @@
    * @param {string} problemClass
    */
   showProblem_: function(messageId, problemClass) {
-    var previousMessage = this.problemMessage_;
-
-    // Update problem info.
-    this.problemMessage_ = this.i18n(messageId);
+    this.quickUnlockPrivate_.getCredentialRequirements(
+        chrome.quickUnlockPrivate.QuickUnlockMode.PIN,
+        this.processPinRequirements_.bind(this, messageId));
     this.problemClass_ = problemClass;
     this.updateStyles();
-
-    // If the problem message has changed, fire an alert.
-    if (previousMessage != this.problemMessage_)
-      this.$.problemDiv.setAttribute('role', 'alert');
-   },
+    this.enableSubmit_ = problemClass != ProblemType.ERROR &&
+        messageId != MessageType.MISMATCH;
+  },
 
   /** @private */
   hideProblem_: function() {
@@ -188,67 +191,100 @@
     this.problemClass_ = '';
   },
 
+  /**
+   * Processes the message received from the quick unlock api and hides/shows
+   * the problem based on the message.
+   * @private
+   * @param {chrome.quickUnlockPrivate.CredentialCheck} message The message
+   *     received from checkCredential.
+   */
+  processPinProblems_: function(message) {
+    if (!message.errors.length && !message.warnings.length) {
+      this.hideProblem_();
+      this.enableSubmit_ = true;
+      return;
+    }
+
+    if (message.warnings.length) {
+      assert(message.warnings[0] ==
+          chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK);
+      this.showProblem_(MessageType.TOO_WEAK, ProblemType.WARNING);
+    }
+
+    if (message.errors.length) {
+      switch (message.errors[0]) {
+        case chrome.quickUnlockPrivate.CredentialProblem.TOO_SHORT:
+          this.showProblem_(MessageType.TOO_SHORT, ProblemType.ERROR);
+          break;
+        case chrome.quickUnlockPrivate.CredentialProblem.TOO_LONG:
+          this.showProblem_(MessageType.TOO_LONG, ProblemType.ERROR);
+          break;
+        case chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK:
+          this.showProblem_(MessageType.TOO_WEAK, ProblemType.ERROR);
+          break;
+        default:
+          assertNotReached();
+          break;
+      }
+    }
+
+  },
+
   /** @private */
   onPinChange_: function() {
     if (!this.isConfirmStep_) {
-      var isPinLongEnough = this.isPinLongEnough_(this.pinKeyboardValue_);
-      var isWeak = isPinLongEnough && this.isPinWeak_(this.pinKeyboardValue_);
-
-      if (!isPinLongEnough)
-        this.showProblem_('configurePinTooShort', 'warning');
-      else if (isWeak)
-        this.showProblem_('configurePinWeakPin', 'warning');
-      else
-        this.hideProblem_();
-
-      this.enableSubmit_ = isPinLongEnough;
-
-    } else {
-      if (this.isPinConfirmed_())
-        this.hideProblem_();
-      else
-        this.showProblem_('configurePinMismatched', 'warning');
-
-      this.enableSubmit_ = true;
+      if (this.pinKeyboardValue_) {
+        this.quickUnlockPrivate_.checkCredential(
+            chrome.quickUnlockPrivate.QuickUnlockMode.PIN,
+            this.pinKeyboardValue_,
+            this.processPinProblems_.bind(this));
+      }
+      return;
     }
+
+    if (this.canSubmit_()) {
+      this.hideProblem_();
+      this.enableSubmit_ = true;
+      return;
+    }
+
+    this.showProblem_(MessageType.MISMATCH, ProblemType.WARNING);
   },
 
   /** @private */
   onPinSubmit_: function() {
     if (!this.isConfirmStep_) {
-      if (this.isPinLongEnough_(this.pinKeyboardValue_)) {
-        this.initialPin_ = this.pinKeyboardValue_;
-        this.pinKeyboardValue_ = '';
-        this.isConfirmStep_ = true;
-        this.onPinChange_();
-        this.$.pinKeyboard.focus();
-        this.writeUma_(LockScreenProgress.ENTER_PIN);
-      }
-    } else {
-      // onPinSubmit_ gets called if the user hits enter on the PIN keyboard.
-      // The PIN is not guaranteed to be valid in that case.
-      if (!this.isPinConfirmed_()) {
-        this.showProblem_('configurePinMismatched', 'error');
+      this.initialPin_ = this.pinKeyboardValue_;
+      this.pinKeyboardValue_ = '';
+      this.isConfirmStep_ = true;
+      this.onPinChange_();
+      this.$.pinKeyboard.focus();
+      this.writeUma_(LockScreenProgress.ENTER_PIN);
+      return;
+    }
+    // onPinSubmit_ gets called if the user hits enter on the PIN keyboard.
+    // The PIN is not guaranteed to be valid in that case.
+    if (!this.canSubmit_()) {
+      this.showProblem_(MessageType.MISMATCH, ProblemType.ERROR);
+      return;
+    }
+
+    function onSetModesCompleted(didSet) {
+      if (!didSet) {
+        console.error('Failed to update pin');
         return;
       }
 
-      function onSetModesCompleted(didSet) {
-        if (!didSet) {
-          console.error('Failed to update pin');
-          return;
-        }
-
-        this.resetState_();
-        this.fire('done');
-      }
-
-      this.setModes.call(
-        null,
-        [chrome.quickUnlockPrivate.QuickUnlockMode.PIN],
-        [this.pinKeyboardValue_],
-        onSetModesCompleted.bind(this));
-      this.writeUma_(LockScreenProgress.CONFIRM_PIN);
+      this.resetState_();
+      this.fire('done');
     }
+
+    this.setModes.call(
+      null,
+      [chrome.quickUnlockPrivate.QuickUnlockMode.PIN],
+      [this.pinKeyboardValue_],
+      onSetModesCompleted.bind(this));
+    this.writeUma_(LockScreenProgress.CONFIRM_PIN);
   },
 
   /**
diff --git a/chrome/browser/themes/theme_service_win.cc b/chrome/browser/themes/theme_service_win.cc
index defcb63..e556783 100644
--- a/chrome/browser/themes/theme_service_win.cc
+++ b/chrome/browser/themes/theme_service_win.cc
@@ -43,12 +43,28 @@
     if (id == ThemeProperties::COLOR_ACCENT_BORDER)
       return dwm_accent_border_color_;
 
-    if (use_dwm_frame_color_) {
-      // Incognito frame is the same as normal when we're using DWM colors.
-      if (id == ThemeProperties::COLOR_FRAME)
-        return dwm_frame_color_;
-      if (id == ThemeProperties::COLOR_FRAME_INACTIVE)
-        return dwm_inactive_frame_color_;
+    // When we're custom-drawing the titlebar we want to use either the colors
+    // we calculated in OnDwmKeyUpdated() or the default colors. When we're not
+    // custom-drawing the titlebar we want to match the color Windows actually
+    // uses because some things (like the incognito icon) use this color to
+    // decide whether they should draw in light or dark mode. Incognito colors
+    // should be the same as non-incognito in all cases here.
+    if (id == ThemeProperties::COLOR_FRAME) {
+      if (dwm_frame_color_)
+        return dwm_frame_color_.value();
+      if (!ShouldCustomDrawSystemTitlebar())
+        return SK_ColorWHITE;
+      // Fall through and use default.
+    }
+    if (id == ThemeProperties::COLOR_FRAME_INACTIVE) {
+      if (!ShouldCustomDrawSystemTitlebar()) {
+        return inactive_frame_color_from_registry_
+                   ? dwm_inactive_frame_color_.value()
+                   : SK_ColorWHITE;
+      }
+      if (dwm_inactive_frame_color_)
+        return dwm_inactive_frame_color_.value();
+      // Fall through and use default.
     }
   }
 
@@ -62,25 +78,30 @@
 
 void ThemeServiceWin::OnDwmKeyUpdated() {
   DWORD accent_color, color_prevalence;
-  use_dwm_frame_color_ =
+  bool use_dwm_frame_color =
       dwm_key_->ReadValueDW(L"AccentColor", &accent_color) == ERROR_SUCCESS &&
       dwm_key_->ReadValueDW(L"ColorPrevalence", &color_prevalence) ==
           ERROR_SUCCESS &&
       color_prevalence == 1;
-  if (use_dwm_frame_color_) {
+  inactive_frame_color_from_registry_ = false;
+  if (use_dwm_frame_color) {
     dwm_frame_color_ = skia::COLORREFToSkColor(accent_color);
     DWORD accent_color_inactive;
     if (dwm_key_->ReadValueDW(L"AccentColorInactive", &accent_color_inactive) ==
         ERROR_SUCCESS) {
       dwm_inactive_frame_color_ =
           skia::COLORREFToSkColor(accent_color_inactive);
+      inactive_frame_color_from_registry_ = true;
     } else {
       // Tint to create inactive color. Always use the non-incognito version of
       // the tint, since the frame should look the same in both modes.
       dwm_inactive_frame_color_ = color_utils::HSLShift(
-          dwm_frame_color_,
+          dwm_frame_color_.value(),
           GetTint(ThemeProperties::TINT_FRAME_INACTIVE, false));
     }
+  } else {
+    dwm_frame_color_.reset();
+    dwm_inactive_frame_color_.reset();
   }
 
   dwm_accent_border_color_ = SK_ColorWHITE;
diff --git a/chrome/browser/themes/theme_service_win.h b/chrome/browser/themes/theme_service_win.h
index de91fc6..6ea367f1 100644
--- a/chrome/browser/themes/theme_service_win.h
+++ b/chrome/browser/themes/theme_service_win.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_THEMES_THEME_SERVICE_WIN_H_
 #define CHROME_BROWSER_THEMES_THEME_SERVICE_WIN_H_
 
+#include "base/optional.h"
 #include "base/win/registry.h"
 #include "chrome/browser/themes/theme_service.h"
 
@@ -33,15 +34,15 @@
   // Registry key containing the params that determine the DWM frame color.
   std::unique_ptr<base::win::RegKey> dwm_key_;
 
-  // True if the frame should be colored using the DWM values here. False if the
-  // default frame colors should be used instead.
-  bool use_dwm_frame_color_;
+  // The frame color when active. If empty the default colors should be used.
+  base::Optional<SkColor> dwm_frame_color_;
 
-  // The frame color when active.
-  SkColor dwm_frame_color_;
+  // True if we took dwm_inactive_frame_color_ from the registry (vs calculating
+  // it ourselves) and thus Windows will use it too.
+  bool inactive_frame_color_from_registry_;
 
-  // The frame color when inactive.
-  SkColor dwm_inactive_frame_color_;
+  // The frame color when inactive. If empty the default colors should be used.
+  base::Optional<SkColor> dwm_inactive_frame_color_;
 
   // The DWM accent border color, if available; white otherwise.
   SkColor dwm_accent_border_color_;
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
index e1163f7..594fe1a 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
@@ -29,6 +29,8 @@
 #include "chrome/browser/ui/global_error/global_error.h"
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
+#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
+#include "chrome/browser/ui/toolbar/media_router_action.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
 #include "chrome/test/base/interactive_test_utils.h"
@@ -92,6 +94,35 @@
   runLoop.Run();
 }
 
+// Simulates a click on the action button in the overflow menu, and runs
+// |closure| upon completion.
+void ClickOnOverflowedAction(ToolbarController* toolbarController,
+                             const base::Closure& closure) {
+  AppMenuController* appMenuController = [toolbarController appMenuController];
+  // The app menu should start as open (since that's where the overflowed
+  // actions are).
+  EXPECT_TRUE([appMenuController isMenuOpen]);
+  BrowserActionsController* overflowController =
+      [appMenuController browserActionsController];
+
+  ASSERT_TRUE(overflowController);
+  BrowserActionButton* actionButton = [overflowController buttonWithIndex:0];
+  // The action should be attached to a superview.
+  EXPECT_TRUE([actionButton superview]);
+
+  // ui_controls:: methods don't play nice when there is an open menu (like the
+  // app menu). Instead, simulate a right click by feeding the event directly to
+  // the button.
+  NSPoint centerPoint = GetCenterPoint(actionButton);
+  NSEvent* mouseEvent = cocoa_test_event_utils::RightMouseDownAtPointInWindow(
+      centerPoint, [actionButton window]);
+  [actionButton rightMouseDown:mouseEvent];
+
+  // This should close the app menu.
+  EXPECT_FALSE([appMenuController isMenuOpen]);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
+}
+
 }  // namespace
 
 // A simple helper menu delegate that will keep track of if a menu is opened,
@@ -229,6 +260,35 @@
     ExtensionBrowserTest::TearDownOnMainThread();
   }
 
+  // Opens the app menu and the context menu of the overflowed action, and
+  // checks that the menus get opened/closed properly. |menuHelper| must be set
+  // as the delegate for the context menu.
+  void OpenAppMenuAndActionContextMenu(MenuHelper* menuHelper) {
+    // Move the mouse over the app menu button.
+    MoveMouseToCenter(appMenuButton());
+
+    // No menu yet (on the browser action).
+    EXPECT_FALSE([menuHelper menuOpened]);
+    base::RunLoop runLoop;
+    // Click on the app menu, and pass in a callback to continue the test in
+    // ClickOnOverflowedAction. Due to the blocking nature of Cocoa menus, we
+    // can't test the menu synchronously here by quitting the run loop when it
+    // opens, and instead need to test through a callback.
+    base::scoped_nsobject<MenuWatcher> menuWatcher(
+        [[MenuWatcher alloc] initWithController:appMenuController()]);
+    [menuWatcher
+        setOpenClosure:base::Bind(&ClickOnOverflowedAction,
+                                  base::Unretained(toolbarController()),
+                                  runLoop.QuitClosure())];
+    ui_controls::SendMouseEvents(ui_controls::LEFT,
+                                 ui_controls::DOWN | ui_controls::UP);
+    runLoop.Run();
+
+    // The menu opened on the main bar's action button rather than the
+    // overflow's since Cocoa does not support running a menu within a menu.
+    EXPECT_TRUE([menuHelper menuOpened]);
+  }
+
   ToolbarController* toolbarController() { return toolbarController_; }
   AppMenuController* appMenuController() { return appMenuController_; }
   ToolbarActionsModel* model() { return model_; }
@@ -244,37 +304,6 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserActionButtonUiTest);
 };
 
-// Simulates a clicks on the action button in the overflow menu, and runs
-// |closure| upon completion.
-void ClickOnOverflowedAction(ToolbarController* toolbarController,
-                             const base::Closure& closure) {
-  AppMenuController* appMenuController =
-      [toolbarController appMenuController];
-  // The app menu should start as open (since that's where the overflowed
-  // actions are).
-  EXPECT_TRUE([appMenuController isMenuOpen]);
-  BrowserActionsController* overflowController =
-      [appMenuController browserActionsController];
-
-  ASSERT_TRUE(overflowController);
-  BrowserActionButton* actionButton =
-      [overflowController buttonWithIndex:0];
-  // The action should be attached to a superview.
-  EXPECT_TRUE([actionButton superview]);
-
-  // ui_controls:: methods don't play nice when there is an open menu (like the
-  // app menu). Instead, simulate a right click by feeding the event directly to
-  // the button.
-  NSPoint centerPoint = GetCenterPoint(actionButton);
-  NSEvent* mouseEvent = cocoa_test_event_utils::RightMouseDownAtPointInWindow(
-      centerPoint, [actionButton window]);
-  [actionButton rightMouseDown:mouseEvent];
-
-  // This should close the app menu.
-  EXPECT_FALSE([appMenuController isMenuOpen]);
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
-}
-
 // Verifies that the action is "popped out" of overflow; that is, it is visible
 // on the main bar, and is set as the popped out action on the controlling
 // ToolbarActionsBar.
@@ -342,32 +371,37 @@
   model()->SetVisibleIconCount(0);
   EXPECT_EQ(nil, [actionButton superview]);
 
-  // Move the mouse over the app menu button.
-  MoveMouseToCenter(appMenuButton());
+  OpenAppMenuAndActionContextMenu(menuHelper.get());
+}
 
-  {
-    // No menu yet (on the browser action).
-    EXPECT_FALSE([menuHelper menuOpened]);
-    base::RunLoop runLoop;
-    // Click on the app menu, and pass in a callback to continue the test in
-    // ClickOnOverflowedAction (Due to the blocking nature of Cocoa menus,
-    // passing in runLoop.QuitClosure() is not sufficient here.)
-    base::scoped_nsobject<MenuWatcher> menuWatcher(
-        [[MenuWatcher alloc] initWithController:appMenuController()]);
-    [menuWatcher setOpenClosure:
-        base::Bind(&ClickOnOverflowedAction,
-                   base::Unretained(toolbarController()),
-                   runLoop.QuitClosure())];
-    ui_controls::SendMouseEvents(ui_controls::LEFT,
-                                 ui_controls::DOWN | ui_controls::UP);
-    runLoop.Run();
+IN_PROC_BROWSER_TEST_F(BrowserActionButtonUiTest,
+                       MediaRouterActionContextMenuInOverflow) {
+  model()->AddComponentAction(
+      ComponentToolbarActionsFactory::kMediaRouterActionId);
+  ASSERT_EQ(1u, model()->toolbar_items().size());
 
-    // The menu should have opened. Note that the menu opened on the main bar's
-    // action button, not the overflow's. Since Cocoa doesn't support running
-    // a menu-within-a-menu, this is what has to happen.
-    EXPECT_TRUE([menuHelper menuOpened]);
-    EXPECT_FALSE([actionButton isHighlighted]);
-  }
+  BrowserActionButton* actionButton =
+      [[toolbarController() browserActionsController] buttonWithIndex:0];
+  ASSERT_TRUE(actionButton);
+
+  // Stub out the action button's normal context menu with a fake one so we
+  // can track when it opens.
+  base::scoped_nsobject<NSMenu> testContextMenu(
+      [[NSMenu alloc] initWithTitle:@""]);
+  base::scoped_nsobject<MenuHelper> menuHelper([[MenuHelper alloc] init]);
+  [testContextMenu setDelegate:menuHelper.get()];
+  [actionButton setTestContextMenu:testContextMenu.get()];
+
+  model()->SetActionVisibility(
+      ComponentToolbarActionsFactory::kMediaRouterActionId, false);
+
+  OpenAppMenuAndActionContextMenu(menuHelper.get());
+
+  ToolbarActionsBar* actionsBar =
+      [[toolbarController() browserActionsController] toolbarActionsBar];
+  // The action should be back in the overflow.
+  EXPECT_FALSE(
+      actionsBar->IsActionVisibleOnMainBar(actionsBar->GetActions()[0]));
 }
 
 // Checks the layout of the overflow bar in the app menu.
diff --git a/chrome/browser/ui/toolbar/media_router_action.cc b/chrome/browser/ui/toolbar/media_router_action.cc
index d9b7850c..ed5d6969 100644
--- a/chrome/browser/ui/toolbar/media_router_action.cc
+++ b/chrome/browser/ui/toolbar/media_router_action.cc
@@ -138,6 +138,13 @@
   return contextual_menu_.menu_model();
 }
 
+void MediaRouterAction::OnContextMenuClosed() {
+  if (toolbar_actions_bar_->popped_out_action() == this &&
+      !GetMediaRouterDialogController()->IsShowingMediaRouterDialog()) {
+    toolbar_actions_bar_->UndoPopOut();
+  }
+}
+
 bool MediaRouterAction::ExecuteAction(bool by_user) {
   base::RecordAction(base::UserMetricsAction("MediaRouter_Icon_Click"));
 
diff --git a/chrome/browser/ui/toolbar/media_router_action.h b/chrome/browser/ui/toolbar/media_router_action.h
index 14564a5..ec88162 100644
--- a/chrome/browser/ui/toolbar/media_router_action.h
+++ b/chrome/browser/ui/toolbar/media_router_action.h
@@ -56,6 +56,7 @@
   void HidePopup() override;
   gfx::NativeView GetPopupNativeView() override;
   ui::MenuModel* GetContextMenu() override;
+  void OnContextMenuClosed() override;
   bool ExecuteAction(bool by_user) override;
   void UpdateState() override;
   bool DisabledClickOpensMenu() const override;
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc
index f3397ded..9c7381f 100644
--- a/chrome/browser/ui/views/certificate_selector.cc
+++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -223,7 +223,7 @@
                                     ui::TableColumn::LEFT, -1, 0.2f));
   table_ = new views::TableView(model_.get(), columns, views::TEXT_ONLY,
                                 true /* single_selection */);
-  table_->SetObserver(this);
+  table_->set_observer(this);
   layout->StartRow(1, kColumnSetId);
   layout->AddView(table_->CreateParentIfNecessary(), 1, 1,
                   views::GridLayout::FILL, views::GridLayout::FILL,
diff --git a/chrome/browser/ui/views/chooser_content_view.cc b/chrome/browser/ui/views/chooser_content_view.cc
index 1c05f1b..0310f30 100644
--- a/chrome/browser/ui/views/chooser_content_view.cc
+++ b/chrome/browser/ui/views/chooser_content_view.cc
@@ -66,7 +66,7 @@
                                                       : views::TEXT_ONLY,
       !chooser_controller_->AllowMultipleSelection() /* single_selection */);
   table_view_->set_select_on_remove(false);
-  table_view_->SetObserver(table_view_observer);
+  table_view_->set_observer(table_view_observer);
   table_view_->SetEnabled(chooser_controller_->NumOptions() > 0);
 
   table_parent_ = table_view_->CreateParentIfNecessary();
@@ -91,7 +91,7 @@
 
 ChooserContentView::~ChooserContentView() {
   chooser_controller_->set_view(nullptr);
-  table_view_->SetObserver(nullptr);
+  table_view_->set_observer(nullptr);
   table_view_->SetModel(nullptr);
 }
 
diff --git a/chrome/browser/ui/views/chooser_content_view_unittest.cc b/chrome/browser/ui/views/chooser_content_view_unittest.cc
index cd8456b..dbfd1539 100644
--- a/chrome/browser/ui/views/chooser_content_view_unittest.cc
+++ b/chrome/browser/ui/views/chooser_content_view_unittest.cc
@@ -14,6 +14,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/list_selection_model.h"
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/controls/table/table_view.h"
@@ -89,7 +90,7 @@
   // |table_view_| should be disabled since there is no option shown.
   EXPECT_FALSE(table_view_->enabled());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
   EXPECT_FALSE(throbber_->visible());
   EXPECT_FALSE(turn_adapter_off_help_->visible());
@@ -109,7 +110,7 @@
   // |table_view_| should be enabled since there is an option.
   EXPECT_TRUE(table_view_->enabled());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 
   mock_chooser_controller_->OptionAdded(
@@ -118,7 +119,7 @@
   EXPECT_EQ(2, table_view_->RowCount());
   EXPECT_EQ(base::ASCIIToUTF16("b"), table_model_->GetText(1, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 
   mock_chooser_controller_->OptionAdded(
@@ -127,7 +128,7 @@
   EXPECT_EQ(3, table_view_->RowCount());
   EXPECT_EQ(base::ASCIIToUTF16("c"), table_model_->GetText(2, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -152,7 +153,7 @@
   EXPECT_EQ(GetPairedText("a"), table_model_->GetText(0, 0));
   EXPECT_EQ(base::ASCIIToUTF16("c"), table_model_->GetText(1, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 
   // Remove a non-existent option, the number of rows should not change.
@@ -161,14 +162,14 @@
   EXPECT_EQ(GetPairedText("a"), table_model_->GetText(0, 0));
   EXPECT_EQ(base::ASCIIToUTF16("c"), table_model_->GetText(1, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 
   mock_chooser_controller_->OptionRemoved(base::ASCIIToUTF16("c"));
   EXPECT_EQ(1, table_view_->RowCount());
   EXPECT_EQ(GetPairedText("a"), table_model_->GetText(0, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 
   mock_chooser_controller_->OptionRemoved(base::ASCIIToUTF16("a"));
@@ -180,7 +181,7 @@
       table_model_->GetText(0, 0));
   // |table_view_| should be disabled since all options are removed.
   EXPECT_FALSE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -209,7 +210,7 @@
   EXPECT_EQ(GetPairedText("d"), table_model_->GetText(1, 0));
   EXPECT_EQ(base::ASCIIToUTF16("c"), table_model_->GetText(2, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -271,7 +272,7 @@
   EXPECT_EQ(GetPairedText("a"), table_model_->GetText(0, 0));
   EXPECT_EQ(base::ASCIIToUTF16("c"), table_model_->GetText(1, 0));
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -292,22 +293,22 @@
 
   // Select option 0.
   table_view_->Select(0);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
 
   // Unselect option 0.
   table_view_->Select(-1);
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 
   // Select option 1.
   table_view_->Select(1);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(1, table_view_->FirstSelectedRow());
 
   // Unselect option 1.
   table_view_->Select(-1);
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -328,17 +329,17 @@
 
   // Select option 0.
   table_view_->Select(0);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
 
   // Select option 1.
   table_view_->Select(1);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(1, table_view_->FirstSelectedRow());
 
   // Select option 2.
   table_view_->Select(2);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(2, table_view_->FirstSelectedRow());
 }
 
@@ -361,13 +362,13 @@
 
   // Select option 1.
   table_view_->Select(1);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(1, table_view_->FirstSelectedRow());
 
   // Remove option 0, the list becomes: b c.
   mock_chooser_controller_->OptionRemoved(base::ASCIIToUTF16("a"));
   EXPECT_EQ(2, table_view_->RowCount());
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   // Since option 0 is removed, the original selected option 1 becomes
   // the first option in the list.
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
@@ -375,7 +376,7 @@
   // Remove option 1.
   mock_chooser_controller_->OptionRemoved(base::ASCIIToUTF16("c"));
   EXPECT_EQ(1, table_view_->RowCount());
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
 }
 
@@ -396,14 +397,14 @@
 
   // Select option 1.
   table_view_->Select(1);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(1, table_view_->FirstSelectedRow());
 
   // Remove option 1.
   mock_chooser_controller_->OptionRemoved(base::ASCIIToUTF16("b"));
   EXPECT_EQ(2, table_view_->RowCount());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -432,7 +433,7 @@
       MockChooserController::ConnectedPairedStatus::CONNECTED |
           MockChooserController::ConnectedPairedStatus::PAIRED);
 
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(1, table_view_->FirstSelectedRow());
   EXPECT_EQ(GetPairedText("a"), table_model_->GetText(0, 0));
   EXPECT_EQ(GetPairedText("d"), table_model_->GetText(1, 0));
@@ -451,7 +452,7 @@
 
   // Select option 0.
   table_view_->Select(0);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
 
   // Remove option 0.
@@ -465,7 +466,7 @@
   // |table_view_| should be disabled since all options are removed.
   EXPECT_FALSE(table_view_->enabled());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
 }
 
@@ -484,7 +485,7 @@
   // |table_view_| should be disabled since there is no option shown.
   EXPECT_FALSE(table_view_->enabled());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
   EXPECT_FALSE(throbber_->visible());
   EXPECT_FALSE(turn_adapter_off_help_->visible());
@@ -522,7 +523,7 @@
       l10n_util::GetStringUTF16(IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT),
       table_model_->GetText(0, 0));
   EXPECT_FALSE(table_view_->enabled());
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
   EXPECT_FALSE(throbber_->visible());
   EXPECT_FALSE(turn_adapter_off_help_->visible());
@@ -565,7 +566,7 @@
   // |table_view_| should be disabled since there is no option shown.
   EXPECT_FALSE(table_view_->enabled());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
   EXPECT_FALSE(throbber_->visible());
   EXPECT_EQ(chooser_content_view_->help_and_re_scan_text_,
@@ -599,13 +600,13 @@
   // |table_view_| should be enabled since there is an option.
   EXPECT_TRUE(table_view_->enabled());
   // No option selected.
-  EXPECT_EQ(0, table_view_->SelectedRowCount());
+  EXPECT_EQ(0UL, table_view_->selection_model().size());
   EXPECT_EQ(-1, table_view_->FirstSelectedRow());
   EXPECT_FALSE(throbber_->visible());
   EXPECT_EQ(chooser_content_view_->help_and_scanning_text_,
             footnote_link_->text());
   table_view_->Select(0);
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
 
   mock_chooser_controller_->OnDiscoveryStateChanged(
@@ -615,7 +616,7 @@
   EXPECT_EQ(base::ASCIIToUTF16("d"), table_model_->GetText(0, 0));
   // |table_view_| should be enabled since there is an option.
   EXPECT_TRUE(table_view_->enabled());
-  EXPECT_EQ(1, table_view_->SelectedRowCount());
+  EXPECT_EQ(1UL, table_view_->selection_model().size());
   EXPECT_EQ(0, table_view_->FirstSelectedRow());
   EXPECT_FALSE(throbber_->visible());
   EXPECT_EQ(chooser_content_view_->help_and_re_scan_text_,
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc
index 4fa2e9c0..60717d7 100644
--- a/chrome/browser/ui/views/task_manager_view.cc
+++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -324,7 +324,7 @@
       this));
   tab_table_->SetModel(table_model_.get());
   tab_table_->SetGrouper(this);
-  tab_table_->SetObserver(this);
+  tab_table_->set_observer(this);
   tab_table_->set_context_menu_controller(this);
   set_context_menu_controller(this);
 
diff --git a/chrome/browser/ui/views/task_manager_view_browsertest.cc b/chrome/browser/ui/views/task_manager_view_browsertest.cc
index d6f996e..c45a7fd7 100644
--- a/chrome/browser/ui/views/task_manager_view_browsertest.cc
+++ b/chrome/browser/ui/views/task_manager_view_browsertest.cc
@@ -207,13 +207,13 @@
   // be selected.
   chrome::ShowTaskManager(browser());
 
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
   EXPECT_EQ(GetTable()->FirstSelectedRow(),
             FindRowForTab(browser()->tab_strip_model()->GetWebContentsAt(1)));
 
   // Activate tab 0. The selection should not change.
   browser()->tab_strip_model()->ActivateTabAt(0, true);
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
   EXPECT_EQ(GetTable()->FirstSelectedRow(),
             FindRowForTab(browser()->tab_strip_model()->GetWebContentsAt(1)));
 
@@ -221,7 +221,7 @@
   // should set the TaskManager selection to the active tab.
   chrome::ShowTaskManager(browser());
 
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
   EXPECT_EQ(GetTable()->FirstSelectedRow(),
             FindRowForTab(browser()->tab_strip_model()->GetWebContentsAt(0)));
 }
@@ -267,7 +267,7 @@
   // Select the middle row, and store its tab id.
   GetTable()->Select(FindRowForTab(tabs[1]));
   EXPECT_EQ(GetTable()->FirstSelectedRow(), FindRowForTab(tabs[1]));
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
 
   // Add 3 rows above the selection. The selected tab should not change.
   for (int i = 0; i < 3; ++i) {
@@ -276,7 +276,7 @@
   }
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows += 3), pattern));
   EXPECT_EQ(GetTable()->FirstSelectedRow(), FindRowForTab(tabs[1]));
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
 
   // Add 2 rows below the selection. The selected tab should not change.
   for (int i = 0; i < 2; ++i) {
@@ -285,7 +285,7 @@
   }
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows += 2), pattern));
   EXPECT_EQ(GetTable()->FirstSelectedRow(), FindRowForTab(tabs[1]));
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
 
   // Add a new row in the same process as the selection. The selected tab should
   // not change.
@@ -293,7 +293,7 @@
   EXPECT_EQ(GetTable()->FirstSelectedRow(), FindRowForTab(tabs[1]));
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows += 1), pattern));
   EXPECT_EQ(GetTable()->FirstSelectedRow(), FindRowForTab(tabs[1]));
-  EXPECT_EQ(1, GetTable()->SelectedRowCount());
+  EXPECT_EQ(1UL, GetTable()->selection_model().size());
 
   // Press the button, which kills the process of the selected row.
   PressKillButton();
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 77eb09d..d10d452 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -516,6 +516,7 @@
       IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONTINUE_BUTTON },
     { "configurePinMismatched", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED },
     { "configurePinTooShort", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT} ,
+    { "configurePinTooLong", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG} ,
     { "configurePinWeakPin", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN },
     { "lockScreenChangePinButton",
       IDS_SETTINGS_PEOPLE_LOCK_SCREEN_CHANGE_PIN_BUTTON},
diff --git a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
index 4290d407..36a33031 100644
--- a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
@@ -82,6 +82,7 @@
 ChangePictureHandler::ChangePictureHandler()
     : previous_image_url_(url::kAboutBlankURL),
       previous_image_index_(user_manager::User::USER_IMAGE_INVALID),
+      user_manager_observer_(this),
       camera_observer_(this) {
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   media::SoundsManager* manager = media::SoundsManager::Get();
@@ -116,14 +117,12 @@
 }
 
 void ChangePictureHandler::OnJavascriptAllowed() {
-  user_manager::UserManager::Get()->AddObserver(this);
-
+  user_manager_observer_.Add(user_manager::UserManager::Get());
   camera_observer_.Add(CameraPresenceNotifier::GetInstance());
 }
 
 void ChangePictureHandler::OnJavascriptDisallowed() {
-  user_manager::UserManager::Get()->RemoveObserver(this);
-
+  user_manager_observer_.Remove(user_manager::UserManager::Get());
   camera_observer_.Remove(CameraPresenceNotifier::GetInstance());
 }
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
index 55e718b..a2132822 100644
--- a/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
@@ -126,6 +126,8 @@
   // Data URL for |user_photo_|.
   std::string user_photo_data_url_;
 
+  ScopedObserver<user_manager::UserManager, ChangePictureHandler>
+      user_manager_observer_;
   ScopedObserver<CameraPresenceNotifier, ChangePictureHandler> camera_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(ChangePictureHandler);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 12de100..67c9d72a 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1068,6 +1068,7 @@
      IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONTINUE_BUTTON},
     {"configurePinMismatched", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED},
     {"configurePinTooShort", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT},
+    {"configurePinTooLong", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG},
     {"configurePinWeakPin", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN},
     {"enableScreenlock", IDS_SETTINGS_PEOPLE_ENABLE_SCREENLOCK},
     {"lockScreenChangePinButton",
diff --git a/chrome/browser/ui/webui/settings/profile_info_handler.cc b/chrome/browser/ui/webui/settings/profile_info_handler.cc
index 60f91128..4ce53d7 100644
--- a/chrome/browser/ui/webui/settings/profile_info_handler.cc
+++ b/chrome/browser/ui/webui/settings/profile_info_handler.cc
@@ -41,6 +41,9 @@
 
 ProfileInfoHandler::ProfileInfoHandler(Profile* profile)
     : profile_(profile),
+#if defined(OS_CHROMEOS)
+      user_manager_observer_(this),
+#endif
       profile_observer_(this) {
 #if defined(OS_CHROMEOS)
   // Set up the chrome://userimage/ source.
@@ -79,7 +82,7 @@
                  base::Unretained(this)));
 
 #if defined(OS_CHROMEOS)
-  user_manager::UserManager::Get()->AddObserver(this);
+  user_manager_observer_.Add(user_manager::UserManager::Get());
 #endif
 }
 
@@ -90,7 +93,7 @@
   profile_pref_registrar_.RemoveAll();
 
 #if defined(OS_CHROMEOS)
-  user_manager::UserManager::Get()->RemoveObserver(this);
+  user_manager_observer_.Remove(user_manager::UserManager::Get());
 #endif
 }
 
diff --git a/chrome/browser/ui/webui/settings/profile_info_handler.h b/chrome/browser/ui/webui/settings/profile_info_handler.h
index c7919c4..6aa21f0 100644
--- a/chrome/browser/ui/webui/settings/profile_info_handler.h
+++ b/chrome/browser/ui/webui/settings/profile_info_handler.h
@@ -16,8 +16,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "components/user_manager/user_manager.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #else
 #include "chrome/browser/profiles/profile_statistics_common.h"
 #endif
@@ -86,6 +84,11 @@
   // Weak pointer.
   Profile* profile_;
 
+#if defined(OS_CHROMEOS)
+  ScopedObserver<user_manager::UserManager, ProfileInfoHandler>
+      user_manager_observer_;
+#endif
+
   ScopedObserver<ProfileAttributesStorage, ProfileInfoHandler>
       profile_observer_;
 
diff --git a/chrome/test/data/extensions/api_test/tab_capture/fullscreen_test.js b/chrome/test/data/extensions/api_test/tab_capture/fullscreen_test.js
index 7c54fe3..00897c0 100644
--- a/chrome/test/data/extensions/api_test/tab_capture/fullscreen_test.js
+++ b/chrome/test/data/extensions/api_test/tab_capture/fullscreen_test.js
@@ -2,49 +2,77 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-window.addEventListener('load', function() {
-  document.body.onclick = function toggleBodyFullscreen() {
+window.addEventListener('load', () => {
+  // Register the fullscreen change listener. This is used to determine when to
+  // signal the C++ side of this test that the document has entered/exited
+  // fullscreen. NOTE: It's very important NOT to use the
+  // tabCapture.onStatusChanged listener to signal the C++ side of this test
+  // because sometimes the document's fullscreen state change lags behind what
+  // the tabCapture API knows about the browser window's fullscreen state.
+  // Otherwise, the C++ side of this test might proceed out-of-sync and fail.
+  // http://crbug.com/675851
+  if (document.fullscreenEnabled) {
+    document.onfullscreenchange = () => {
+      if (document.fullscreenElement)
+        chrome.test.sendMessage('entered_fullscreen');
+    };
+  } else if (document.webkitFullscreenEnabled) {
+    document.onwebkitfullscreenchange = () => {
+      if (document.webkitFullscreenElement)
+        chrome.test.sendMessage('entered_fullscreen');
+    };
+  } else {
+    chrome.test.assertTrue(!'HTML5 Fullscreen API missing');
+  }
+
+  // Register an onclick listener that toggles the entire document into or
+  // out-of fullscreen mode. The clicks are generated by the C++ side of this
+  // test.
+  document.documentElement.onclick = () => {
     if (document.fullscreenElement || document.webkitFullscreenElement) {
       if (document.exitFullscreen)
         document.exitFullscreen();
       else if (document.webkitExitFullscreen)
         document.webkitExitFullscreen();
       else
-        chrome.test.assertTrue(!"HTML5 Fullscreen API missing");
+        chrome.test.assertTrue(!'HTML5 Fullscreen API missing');
     } else {
-      if (document.body.requestFullscreen)
-        document.body.requestFullscreen();
-      else if (document.body.webkitRequestFullscreen)
-        document.body.webkitRequestFullscreen();
+      if (document.documentElement.requestFullscreen)
+        document.documentElement.requestFullscreen();
+      else if (document.documentElement.webkitRequestFullscreen)
+        document.documentElement.webkitRequestFullscreen();
       else
-        chrome.test.assertTrue(!"HTML5 Fullscreen API missing");
+        chrome.test.assertTrue(!'HTML5 Fullscreen API missing');
     }
   };
-});
 
-var mediaStream = null;
-var events = [];
+  let mediaStream = null;
+  const events = [];
 
-chrome.tabCapture.onStatusChanged.addListener(function(info) {
-  if (info.status == 'active') {
-    events.push(info.fullscreen);
-    if (events.length == 3) {
-      chrome.test.assertFalse(events[0]);
-      chrome.test.assertTrue(events[1]);
-      chrome.test.assertFalse(events[2]);
-      mediaStream.getVideoTracks()[0].stop();
-      mediaStream.getAudioTracks()[0].stop();
-      chrome.test.succeed();
+  // Register the tab capture status change listener, which records the changes
+  // to fullscreen state according to the tab capture API implementation. Once
+  // there are three changes, check the results and succeed() the test.
+  chrome.tabCapture.onStatusChanged.addListener(info => {
+    if (info.status == 'active') {
+      events.push(info.fullscreen);
+      if (events.length == 3) {
+        mediaStream.getVideoTracks()[0].stop();
+        mediaStream.getAudioTracks()[0].stop();
+        chrome.test.assertFalse(events[0]);
+        chrome.test.assertTrue(events[1]);
+        chrome.test.assertFalse(events[2]);
+        chrome.test.succeed();
+      }
     }
+  });
 
-    if (info.fullscreen)
-      chrome.test.sendMessage('entered_fullscreen');
-  }
-});
-
-chrome.tabCapture.capture({audio: true, video: true}, function(stream) {
-  chrome.test.assertTrue(!!stream);
-  mediaStream = stream;
-  chrome.test.notifyPass();
-  chrome.test.sendMessage('tab_capture_started');
+  // Now that all listeners are in-place, start tab capture. Once tab capture is
+  // running, notify the C++ side of this test that it may commence with the
+  // "fullscreen toggle mouse clicks."
+  chrome.tabCapture.capture({audio: true, video: true}, stream => {
+    chrome.test.assertTrue(!!stream);
+    mediaStream = stream;
+    chrome.test.notifyPass();
+    chrome.test.sendMessage('tab_capture_started');
+  });
 });
diff --git a/chrome/test/data/webui/settings/fake_quick_unlock_private.js b/chrome/test/data/webui/settings/fake_quick_unlock_private.js
index 0852d67..ae005e7 100644
--- a/chrome/test/data/webui/settings/fake_quick_unlock_private.js
+++ b/chrome/test/data/webui/settings/fake_quick_unlock_private.js
@@ -5,6 +5,13 @@
 /**
  * @fileoverview Fake implementation of chrome.quickUnlockPrivate for testing.
  */
+
+/**
+ * A couple weak pins to use for testing.
+ * @const
+ */
+var TEST_WEAK_PINS = ['1111', '1234', '1313', '2001', '1010'];
+
 cr.define('settings', function() {
   /**
    * Fake of the chrome.quickUnlockPrivate API.
@@ -18,6 +25,8 @@
         this.activeModes = [];
     /** @type {!Array<string>} */ this.credentials = [];
     /** @type {string} */ this.accountPassword = '';
+    /** @type {!chrome.quickUnlockPrivate.CredentialRequirements} */
+        this.credentialRequirements = {minLength: 4, maxLength: 0};
   }
 
   FakeQuickUnlockPrivate.prototype = {
@@ -54,7 +63,47 @@
       this.activeModes = modes;
       this.credentials = credentials;
       onComplete(this.accountPassword == accountPassword);
-    }
+    },
+
+    /**
+     * @override
+     * @param {!chrome.quickUnlockPrivate.QuickUnlockMode} mode
+     * @param {string} credential
+     * @param {function(
+     *     !chrome.quickUnlockPrivate.CredentialCheck):void} onComplete
+     */
+    checkCredential: function(mode, credential, onComplete) {
+      var message = {};
+      var errors = [];
+      var warnings = [];
+
+      if (!!credential &&
+          credential.length < this.credentialRequirements.minLength) {
+        errors.push(chrome.quickUnlockPrivate.CredentialProblem.TOO_SHORT);
+      }
+
+      if (!!credential && this.credentialRequirements.maxLength != 0 &&
+          credential.length > this.credentialRequirements.maxLength) {
+        errors.push(chrome.quickUnlockPrivate.CredentialProblem.TOO_LONG);
+      }
+
+      if (!!credential && TEST_WEAK_PINS.includes(credential))
+        warnings.push(chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK);
+
+      message.errors = errors;
+      message.warnings = warnings;
+      onComplete(message);
+    },
+
+    /**
+     * @override.
+     * @param {!chrome.quickUnlockPrivate.QuickUnlockMode} mode
+     * @param {function(
+     *     !chrome.quickUnlockPrivate.CredentialRequirements):void onComplete
+     */
+    getCredentialRequirements: function(mode, onComplete) {
+      onComplete(this.credentialRequirements);
+    },
   };
 
   /** @type {!ChromeEvent} */
diff --git a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
index 67d62ec..aad0d078 100644
--- a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
@@ -337,6 +337,7 @@
 
         // Create setup-pin element.
         element = document.createElement('settings-setup-pin-dialog');
+        element.quickUnlockPrivate_ = quickUnlockPrivateApi;
         element.setModes =
             quickUnlockPrivateApi.setModes.bind(quickUnlockPrivateApi, '');
         element.writeUma_ = fakeUma.recordProgress.bind(fakeUma);
@@ -402,13 +403,30 @@
         pinKeyboard.value = '11';
 
         assertTrue(isVisible(problemDiv));
-        assertHasClass(problemDiv, 'warning');
+        assertHasClass(problemDiv, 'error');
+        assertTrue(continueButton.disabled);
+      });
+
+      // If the PIN is too long an error problem is shown.
+      test('WarningShownForLongPins', function() {
+        // By default, there is no max length on pins.
+        quickUnlockPrivateApi.credentialRequirements.maxLength = 5;
+
+        // A pin of length five should be valid when max length is five.
+        pinKeyboard.value = '11111';
+        assertFalse(isVisible(problemDiv));
+        assertFalse(continueButton.disabled);
+
+        // A pin of length six should not be valid when max length is five.
+        pinKeyboard.value = '111111';
+        assertTrue(isVisible(problemDiv));
+        assertHasClass(problemDiv, 'error');
         assertTrue(continueButton.disabled);
       });
 
       // If the PIN is weak a warning problem is shown.
       test('WarningShownForWeakPins', function() {
-        pinKeyboard.value = '111111';
+        pinKeyboard.value = '1111';
 
         assertTrue(isVisible(problemDiv));
         assertHasClass(problemDiv, 'warning');
@@ -425,8 +443,9 @@
         assertTrue(isVisible(problemDiv));
         assertHasClass(problemDiv, 'warning');
 
-        // Submitting a mistmatched PIN shows an error.
-        MockInteractions.tap(continueButton);
+        // Submitting a mistmatched PIN shows an error. Directly call the button
+        // event since a tap on the disabled button does nothing.
+        element.onPinSubmit_();
         assertHasClass(problemDiv, 'error');
 
         // Changing the PIN changes the error to a warning.
diff --git a/components/exo/buffer.cc b/components/exo/buffer.cc
index 9b1778d..060c7e5 100644
--- a/components/exo/buffer.cc
+++ b/components/exo/buffer.cc
@@ -24,6 +24,7 @@
 #include "cc/output/context_provider.h"
 #include "cc/resources/single_release_callback.h"
 #include "cc/resources/texture_mailbox.h"
+#include "components/exo/compositor_frame_sink_holder.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "ui/aura/env.h"
@@ -398,10 +399,12 @@
 
 Buffer::~Buffer() {}
 
-std::unique_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox(
-    cc::TextureMailbox* texture_mailbox,
+bool Buffer::ProduceTransferableResource(
+    CompositorFrameSinkHolder* compositor_frame_sink_holder,
+    cc::ResourceId resource_id,
     bool secure_output_only,
-    bool client_usage) {
+    bool client_usage,
+    cc::TransferableResource* resource) {
   DCHECK(attach_count_);
   DLOG_IF(WARNING, use_count_ && client_usage)
       << "Producing a texture mailbox for a buffer that has not been released";
@@ -433,9 +436,20 @@
   if (!context_provider) {
     DLOG(WARNING) << "Failed to acquire a context provider";
     Release();  // Decrements the use count
-    return nullptr;
+    resource->id = 0;
+    resource->size = gfx::Size();
+    return false;
   }
 
+  // The reference to the CompositorFrameSinkHolder keeps it alive until a
+  // release callback is received.
+  compositor_frame_sink_holder_ = compositor_frame_sink_holder;
+
+  resource->id = resource_id;
+  resource->format = cc::RGBA_8888;
+  resource->filter = GL_LINEAR;
+  resource->size = gpu_memory_buffer_->GetSize();
+
   // Create a new image texture for |gpu_memory_buffer_| with |texture_target_|
   // if one doesn't already exist. The contents of this buffer are copied to
   // |texture| using a call to CopyTexImage.
@@ -450,18 +464,19 @@
     Texture* texture = contents_texture_.get();
 
     // This binds the latest contents of this buffer to |texture|.
-    gpu::SyncToken sync_token = texture->BindTexImage();
+    resource->mailbox_holder = gpu::MailboxHolder(
+        texture->mailbox(), texture->BindTexImage(), texture_target_);
 
-    *texture_mailbox =
-        cc::TextureMailbox(texture->mailbox(), sync_token, texture_target_,
-                           gpu_memory_buffer_->GetSize(), is_overlay_candidate_,
-                           secure_output_only);
+    resource->is_overlay_candidate = is_overlay_candidate_;
+
     // The contents texture will be released when no longer used by the
     // compositor.
-    return cc::SingleReleaseCallback::Create(
+    compositor_frame_sink_holder_->SetResourceReleaseCallback(
+        resource_id,
         base::Bind(&Buffer::Texture::ReleaseTexImage, base::Unretained(texture),
                    base::Bind(&Buffer::ReleaseContentsTexture, AsWeakPtr(),
                               base::Passed(&contents_texture_))));
+    return true;
   }
 
   // Create a mailbox texture that we copy the buffer contents to.
@@ -476,19 +491,22 @@
   Texture* texture = texture_.get();
 
   // The contents texture will be released when copy has completed.
-  gpu::SyncToken sync_token = contents_texture->CopyTexImage(
-      texture, base::Bind(&Buffer::ReleaseContentsTexture, AsWeakPtr(),
-                          base::Passed(&contents_texture_)));
-  *texture_mailbox =
-      cc::TextureMailbox(texture->mailbox(), sync_token, GL_TEXTURE_2D,
-                         gpu_memory_buffer_->GetSize(),
-                         false /* is_overlay_candidate */, secure_output_only);
+  resource->mailbox_holder = gpu::MailboxHolder(
+      texture->mailbox(),
+      contents_texture->CopyTexImage(
+          texture, base::Bind(&Buffer::ReleaseContentsTexture, AsWeakPtr(),
+                              base::Passed(&contents_texture_))),
+      GL_TEXTURE_2D);
+  resource->is_overlay_candidate = false;
+
   // The mailbox texture will be released when no longer used by the
   // compositor.
-  return cc::SingleReleaseCallback::Create(
+  compositor_frame_sink_holder_->SetResourceReleaseCallback(
+      resource_id,
       base::Bind(&Buffer::Texture::Release, base::Unretained(texture),
                  base::Bind(&Buffer::ReleaseTexture, AsWeakPtr(),
                             base::Passed(&texture_))));
+  return true;
 }
 
 void Buffer::OnAttach() {
@@ -534,6 +552,8 @@
   // Run release callback to notify the client that buffer has been released.
   if (!release_callback_.is_null())
     release_callback_.Run();
+
+  compositor_frame_sink_holder_ = nullptr;
 }
 
 void Buffer::ReleaseTexture(std::unique_ptr<Texture> texture) {
@@ -542,7 +562,6 @@
 
 void Buffer::ReleaseContentsTexture(std::unique_ptr<Texture> texture) {
   TRACE_EVENT0("exo", "Buffer::ReleaseContentsTexture");
-
   contents_texture_ = std::move(texture);
   Release();
 }
diff --git a/components/exo/buffer.h b/components/exo/buffer.h
index e7b5b605..eeaf1c3 100644
--- a/components/exo/buffer.h
+++ b/components/exo/buffer.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "cc/resources/transferable_resource.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace base {
@@ -18,17 +19,14 @@
 }
 }
 
-namespace cc {
-class SingleReleaseCallback;
-class TextureMailbox;
-}
-
 namespace gfx {
 class GpuMemoryBuffer;
 }
 
 namespace exo {
 
+class CompositorFrameSinkHolder;
+
 // This class provides the content for a Surface. The mechanism by which a
 // client provides and updates the contents is the responsibility of the client
 // and not defined as part of this class.
@@ -53,10 +51,12 @@
   // buffer. Returns a release callback on success. The release callback should
   // be called before a new texture mailbox can be acquired unless
   // |non_client_usage| is true.
-  std::unique_ptr<cc::SingleReleaseCallback> ProduceTextureMailbox(
-      cc::TextureMailbox* mailbox,
+  bool ProduceTransferableResource(
+      CompositorFrameSinkHolder* compositor_frame_sink_holder,
+      cc::ResourceId resource_id,
       bool secure_output_only,
-      bool client_usage);
+      bool client_usage,
+      cc::TransferableResource* resource);
 
   // This should be called when the buffer is attached to a Surface.
   void OnAttach();
@@ -123,6 +123,12 @@
   // The client release callback.
   base::Closure release_callback_;
 
+  // The CompositorFrameSinkHolder that has the ReleaseCallback of this buffer
+  // produced in ProduceTextureMailbox().
+  // Buffer holds a reference to the CompositorFrameSinkHolder to keep it alive.
+  // The refptr is reset when the release callback is called.
+  scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder_;
+
   DISALLOW_COPY_AND_ASSIGN(Buffer);
 };
 
diff --git a/components/exo/buffer_unittest.cc b/components/exo/buffer_unittest.cc
index 987c2905..8572caa 100644
--- a/components/exo/buffer_unittest.cc
+++ b/components/exo/buffer_unittest.cc
@@ -31,6 +31,9 @@
   gfx::Size buffer_size(256, 256);
   std::unique_ptr<Buffer> buffer(
       new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface);
+  scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder =
+      new CompositorFrameSinkHolder(surface.get(), nullptr, nullptr);
 
   // Set the release callback.
   int release_call_count = 0;
@@ -38,15 +41,21 @@
       base::Bind(&Release, base::Unretained(&release_call_count)));
 
   buffer->OnAttach();
-  // Produce a texture mailbox for the contents of the buffer.
-  cc::TextureMailbox texture_mailbox;
-  std::unique_ptr<cc::SingleReleaseCallback> buffer_release_callback =
-      buffer->ProduceTextureMailbox(&texture_mailbox, false, true);
-  ASSERT_TRUE(buffer_release_callback);
+  cc::TransferableResource resource;
+  // Produce a transferable resource for the contents of the buffer.
+  bool rv = buffer->ProduceTransferableResource(
+      compositor_frame_sink_holder.get(), 0, false, true, &resource);
+  ASSERT_TRUE(rv);
 
   // Release buffer.
-  buffer_release_callback->Run(gpu::SyncToken(), false);
+  cc::ReturnedResource returned_resource;
+  returned_resource.id = resource.id;
+  returned_resource.sync_token = resource.mailbox_holder.sync_token;
+  returned_resource.lost = false;
+  cc::ReturnedResourceArray resources = {returned_resource};
+  compositor_frame_sink_holder->ReclaimResources(resources);
 
+  RunAllPendingInMessageLoop();
   ASSERT_EQ(release_call_count, 0);
 
   buffer->OnDetach();
@@ -59,13 +68,17 @@
   gfx::Size buffer_size(256, 256);
   std::unique_ptr<Buffer> buffer(
       new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface);
+  scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder =
+      new CompositorFrameSinkHolder(surface.get(), nullptr, nullptr);
+  cc::ResourceId resource_id = 0;
 
   buffer->OnAttach();
-  // Acquire a texture mailbox for the contents of the buffer.
-  cc::TextureMailbox texture_mailbox;
-  std::unique_ptr<cc::SingleReleaseCallback> buffer_release_callback =
-      buffer->ProduceTextureMailbox(&texture_mailbox, false, true);
-  ASSERT_TRUE(buffer_release_callback);
+  // Acquire a texture transferable resource for the contents of the buffer.
+  cc::TransferableResource resource;
+  bool rv = buffer->ProduceTransferableResource(
+      compositor_frame_sink_holder.get(), resource_id, false, true, &resource);
+  ASSERT_TRUE(rv);
 
   scoped_refptr<cc::ContextProvider> context_provider =
       aura::Env::GetInstance()
@@ -79,15 +92,31 @@
 
   // Release buffer.
   bool is_lost = true;
-  buffer_release_callback->Run(gpu::SyncToken(), is_lost);
+  cc::ReturnedResource returned_resource;
+  returned_resource.id = resource_id;
+  returned_resource.sync_token = gpu::SyncToken();
+  returned_resource.lost = is_lost;
+  cc::ReturnedResourceArray resources = {returned_resource};
+  compositor_frame_sink_holder->ReclaimResources(resources);
+  RunAllPendingInMessageLoop();
 
-  // Producing a new texture mailbox for the contents of the buffer.
-  std::unique_ptr<cc::SingleReleaseCallback> buffer_release_callback2 =
-      buffer->ProduceTextureMailbox(&texture_mailbox, false, false);
-  ASSERT_TRUE(buffer_release_callback2);
+  // Producing a new texture transferable resource for the contents of the
+  // buffer.
+  ++resource_id;
+  cc::TransferableResource new_resource;
+  rv = buffer->ProduceTransferableResource(compositor_frame_sink_holder.get(),
+                                           resource_id, false, false,
+                                           &new_resource);
+  ASSERT_TRUE(rv);
   buffer->OnDetach();
 
-  buffer_release_callback2->Run(gpu::SyncToken(), false);
+  cc::ReturnedResource returned_resource2;
+  returned_resource2.id = resource_id;
+  returned_resource2.sync_token = gpu::SyncToken();
+  returned_resource2.lost = false;
+  cc::ReturnedResourceArray resources2 = {returned_resource2};
+  compositor_frame_sink_holder->ReclaimResources(resources2);
+  RunAllPendingInMessageLoop();
 }
 
 }  // namespace
diff --git a/components/exo/compositor_frame_sink_holder.cc b/components/exo/compositor_frame_sink_holder.cc
index dcd3110a..e6cb2b8d 100644
--- a/components/exo/compositor_frame_sink_holder.cc
+++ b/components/exo/compositor_frame_sink_holder.cc
@@ -4,7 +4,6 @@
 
 #include "components/exo/compositor_frame_sink_holder.h"
 
-#include "base/memory/ptr_util.h"
 #include "cc/resources/returned_resource.h"
 #include "components/exo/surface.h"
 
@@ -30,10 +29,11 @@
   return release_callbacks_.find(id) != release_callbacks_.end();
 }
 
-void CompositorFrameSinkHolder::AddResourceReleaseCallback(
+void CompositorFrameSinkHolder::SetResourceReleaseCallback(
     cc::ResourceId id,
-    std::unique_ptr<cc::SingleReleaseCallback> callback) {
-  release_callbacks_[id] = std::make_pair(this, std::move(callback));
+    const cc::ReleaseCallback& callback) {
+  DCHECK(!callback.is_null());
+  release_callbacks_[id] = callback;
 }
 
 void CompositorFrameSinkHolder::ActivateFrameCallbacks(
@@ -84,10 +84,10 @@
   for (auto& resource : resources) {
     auto it = release_callbacks_.find(resource.id);
     DCHECK(it != release_callbacks_.end());
-    std::unique_ptr<cc::SingleReleaseCallback> callback =
-        std::move(it->second.second);
-    release_callbacks_.erase(it);
-    callback->Run(resource.sync_token, resource.lost);
+    if (it != release_callbacks_.end()) {
+      it->second.Run(resource.sync_token, resource.lost);
+      release_callbacks_.erase(it);
+    }
   }
 }
 
@@ -126,7 +126,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 // ExoComopositorFrameSink, private:
 
-CompositorFrameSinkHolder::~CompositorFrameSinkHolder() {}
+CompositorFrameSinkHolder::~CompositorFrameSinkHolder() {
+  if (surface_)
+    surface_->RemoveSurfaceObserver(this);
+}
 
 void CompositorFrameSinkHolder::UpdateNeedsBeginFrame() {
   if (!begin_frame_source_)
diff --git a/components/exo/compositor_frame_sink_holder.h b/components/exo/compositor_frame_sink_holder.h
index 59cc987..d2f8ab8f 100644
--- a/components/exo/compositor_frame_sink_holder.h
+++ b/components/exo/compositor_frame_sink_holder.h
@@ -10,7 +10,7 @@
 #include <memory>
 
 #include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
-#include "cc/resources/single_release_callback.h"
+#include "cc/resources/release_callback.h"
 #include "cc/resources/transferable_resource.h"
 #include "cc/scheduler/begin_frame_source.h"
 #include "components/exo/compositor_frame_sink.h"
@@ -37,9 +37,8 @@
       cc::mojom::MojoCompositorFrameSinkClientRequest request);
 
   bool HasReleaseCallbackForResource(cc::ResourceId id);
-  void AddResourceReleaseCallback(
-      cc::ResourceId id,
-      std::unique_ptr<cc::SingleReleaseCallback> callback);
+  void SetResourceReleaseCallback(cc::ResourceId id,
+                                  const cc::ReleaseCallback& callback);
 
   CompositorFrameSink* GetCompositorFrameSink() { return frame_sink_.get(); }
 
@@ -79,14 +78,8 @@
 
   void UpdateNeedsBeginFrame();
 
-  // Each release callback holds a reference to the CompositorFrameSinkHolder
-  // itself to keep it alive. Running and erasing the callbacks might result in
-  // the instance being destroyed. Therefore, we should not access any member
-  // variables after running and erasing the callbacks.
-  using ResourceReleaseCallbackMap =
-      std::map<int,
-               std::pair<scoped_refptr<CompositorFrameSinkHolder>,
-                         std::unique_ptr<cc::SingleReleaseCallback>>>;
+  // A collection of callbacks used to release resources.
+  using ResourceReleaseCallbackMap = std::map<int, cc::ReleaseCallback>;
   ResourceReleaseCallbackMap release_callbacks_;
 
   Surface* surface_;
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 6c81151..e2f8d4a 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -658,32 +658,11 @@
 }
 
 void Surface::UpdateResource(bool client_usage) {
-  std::unique_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback;
-
-  cc::TextureMailbox texture_mailbox;
-  if (current_buffer_.buffer()) {
-    texture_mailbox_release_callback =
-        current_buffer_.buffer()->ProduceTextureMailbox(
-            &texture_mailbox, state_.only_visible_on_secure_output,
-            client_usage);
-  }
-
-  if (texture_mailbox_release_callback) {
-    cc::TransferableResource resource;
-    resource.id = next_resource_id_++;
-    resource.format = cc::RGBA_8888;
-    resource.filter =
-        texture_mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR;
-    resource.size = texture_mailbox.size_in_pixels();
-    resource.mailbox_holder = gpu::MailboxHolder(texture_mailbox.mailbox(),
-                                                 texture_mailbox.sync_token(),
-                                                 texture_mailbox.target());
-    resource.is_overlay_candidate = texture_mailbox.is_overlay_candidate();
-
-    compositor_frame_sink_holder_->AddResourceReleaseCallback(
-        resource.id, std::move(texture_mailbox_release_callback));
-    current_resource_ = resource;
-  } else {
+  if (!current_buffer_.buffer() ||
+      !current_buffer_.buffer()->ProduceTransferableResource(
+          compositor_frame_sink_holder_.get(), next_resource_id_++,
+          state_.only_visible_on_secure_output, client_usage,
+          &current_resource_)) {
     current_resource_.id = 0;
     current_resource_.size = gfx::Size();
   }
diff --git a/components/offline_pages/core/offline_page_model_impl.cc b/components/offline_pages/core/offline_page_model_impl.cc
index 011dfbe..245db22 100644
--- a/components/offline_pages/core/offline_page_model_impl.cc
+++ b/components/offline_pages/core/offline_page_model_impl.cc
@@ -708,11 +708,12 @@
   store_->AddOfflinePage(offline_page_item,
                          base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
                                     weak_ptr_factory_.GetWeakPtr(), archiver,
-                                    callback, offline_page_item));
+                                    file_path, callback, offline_page_item));
 }
 
 void OfflinePageModelImpl::OnAddOfflinePageDone(
     OfflinePageArchiver* archiver,
+    const base::FilePath& file_path,
     const SavePageCallback& callback,
     const OfflinePageItem& offline_page,
     ItemActionStatus status) {
@@ -727,6 +728,8 @@
         offline_page.client_id.name_space, offline_page.url.spec(),
         std::to_string(offline_page.offline_id));
   } else if (status == ItemActionStatus::ALREADY_EXISTS) {
+    // Remove the orphaned archive.  No callback necessary.
+    archive_manager_->DeleteArchive(file_path, base::Bind([](bool) {}));
     result = SavePageResult::ALREADY_EXISTS;
   } else {
     result = SavePageResult::STORE_FAILURE;
diff --git a/components/offline_pages/core/offline_page_model_impl.h b/components/offline_pages/core/offline_page_model_impl.h
index 15846d5..d0ca9bb5 100644
--- a/components/offline_pages/core/offline_page_model_impl.h
+++ b/components/offline_pages/core/offline_page_model_impl.h
@@ -169,6 +169,7 @@
                            const base::string16& title,
                            int64_t file_size);
   void OnAddOfflinePageDone(OfflinePageArchiver* archiver,
+                            const base::FilePath& file_path,
                             const SavePageCallback& callback,
                             const OfflinePageItem& offline_page,
                             ItemActionStatus status);
diff --git a/components/sync/syncable/model_type.cc b/components/sync/syncable/model_type.cc
index a4d34f1..363c848 100644
--- a/components/sync/syncable/model_type.cc
+++ b/components/sync/syncable/model_type.cc
@@ -31,23 +31,23 @@
 namespace syncer {
 
 struct ModelTypeInfo {
-  const ModelType model_type;
+  ModelType model_type;
   // Model Type notification string.
   // This needs to match the corresponding proto message name in sync.proto
-  const char* const notification_type;
+  const char* notification_type;
   // Root tag for Model Type
   // This should be the same as the model type but all lowercase.
-  const char* const root_tag;
+  const char* root_tag;
   // String value for Model Type
   // This should be the same as the model type but space separated and the
   // first letter of every word capitalized.
-  const char* const model_type_string;
+  const char* model_type_string;
   // SpecificsFieldNumber for Model Type
-  const int specifics_field_number;
+  int specifics_field_number;
   // Histogram value should be unique for the Model Type, Existing histogram
   // values should never be modified without updating "SyncModelTypes" enum in
   // histograms.xml to maintain backward compatibility.
-  const int model_type_histogram_val;
+  int model_type_histogram_val;
 };
 
 // Below struct entries are in the same order as their definition in the
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4d681cc1..9c7cd17cb0 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1141,6 +1141,8 @@
     "renderer_host/media/video_capture_host.h",
     "renderer_host/media/video_capture_manager.cc",
     "renderer_host/media/video_capture_manager.h",
+    "renderer_host/media/video_frame_receiver_on_io_thread.cc",
+    "renderer_host/media/video_frame_receiver_on_io_thread.h",
     "renderer_host/native_web_keyboard_event_aura.cc",
     "renderer_host/native_web_keyboard_event_mac.mm",
     "renderer_host/offscreen_canvas_compositor_frame_sink.cc",
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 440767b0..3f7121b5 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -920,11 +920,6 @@
     return;
 
   node->manager()->SetAccessibilityFocus(*node);
-
-  // Auto-focus links, because some websites have skip links that are
-  // only visible when focused.  See http://crbug.com/657157
-  if (node->IsLink())
-    node->manager()->SetFocus(*node);
 }
 
 bool BrowserAccessibilityManagerAndroid::IsSlider(
diff --git a/content/browser/loader/redirect_to_file_resource_handler.cc b/content/browser/loader/redirect_to_file_resource_handler.cc
index 2d55420e..a30d9dc 100644
--- a/content/browser/loader/redirect_to_file_resource_handler.cc
+++ b/content/browser/loader/redirect_to_file_resource_handler.cc
@@ -172,6 +172,7 @@
   // network request like this.
   will_start_url_ = url;
   did_defer_ = *defer = true;
+  request()->LogBlockedBy("RedirectToFileResourceHandler");
   if (create_temporary_file_stream_.is_null()) {
     CreateTemporaryFileStream(
         base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
@@ -215,6 +216,7 @@
 
   if (BufIsFull()) {
     did_defer_ = *defer = true;
+    request()->LogBlockedBy("RedirectToFileResourceHandler");
 
     if (buf_->capacity() == bytes_read) {
       // The network layer has saturated our buffer in one read. Next time, we
@@ -234,6 +236,7 @@
     completed_status_ = status;
     did_defer_ = true;
     *defer = true;
+    request()->LogBlockedBy("RedirectToFileResourceHandler");
     return;
   }
   next_handler_->OnResponseCompleted(status, defer);
@@ -254,10 +257,11 @@
   // Resume the request.
   DCHECK(did_defer_);
   bool defer = false;
+  request()->LogUnblocked();
   if (!next_handler_->OnWillStart(will_start_url_, &defer)) {
     controller()->Cancel();
   } else if (!defer) {
-    ResumeIfDeferred();
+    Resume();
   } else {
     did_defer_ = false;
   }
@@ -293,9 +297,10 @@
     // this should run even in the |failed| case above, otherwise a failed write
     // leaves the handler stuck.
     bool defer = false;
+    request()->LogUnblocked();
     next_handler_->OnResponseCompleted(completed_status_, &defer);
     if (!defer) {
-      ResumeIfDeferred();
+      Resume();
     } else {
       did_defer_ = false;
     }
@@ -309,8 +314,10 @@
       // We've caught up to the network load, but it may be in the process of
       // appending more data to the buffer.
       if (!buf_write_pending_) {
-        if (BufIsFull())
-          ResumeIfDeferred();
+        if (BufIsFull()) {
+          request()->LogUnblocked();
+          Resume();
+        }
         buf_->set_offset(0);
         write_cursor_ = 0;
       }
@@ -356,11 +363,10 @@
   return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff);
 }
 
-void RedirectToFileResourceHandler::ResumeIfDeferred() {
-  if (did_defer_) {
-    did_defer_ = false;
-    controller()->Resume();
-  }
+void RedirectToFileResourceHandler::Resume() {
+  DCHECK(did_defer_);
+  did_defer_ = false;
+  controller()->Resume();
 }
 
 }  // namespace content
diff --git a/content/browser/loader/redirect_to_file_resource_handler.h b/content/browser/loader/redirect_to_file_resource_handler.h
index bcda474..8163a42 100644
--- a/content/browser/loader/redirect_to_file_resource_handler.h
+++ b/content/browser/loader/redirect_to_file_resource_handler.h
@@ -76,7 +76,7 @@
 
   bool WriteMore();
   bool BufIsFull() const;
-  void ResumeIfDeferred();
+  void Resume();
 
   CreateTemporaryFileStreamFunction create_temporary_file_stream_;
 
diff --git a/content/browser/loader/resource_scheduler.cc b/content/browser/loader/resource_scheduler.cc
index 9bbae929..1cd91c3e 100644
--- a/content/browser/loader/resource_scheduler.cc
+++ b/content/browser/loader/resource_scheduler.cc
@@ -52,6 +52,38 @@
 const RequestAttributes kAttributeDelayable = 0x02;
 const RequestAttributes kAttributeLayoutBlocking = 0x04;
 
+// Reasons why pending requests may be started.  For logging only.
+enum class RequestStartTrigger {
+  NONE,
+  COMPLETION_PRE_BODY,
+  COMPLETION_POST_BODY,
+  BODY_REACHED,
+  CLIENT_KILL,
+  SPDY_PROXY_DETECTED,
+  REQUEST_REPRIORITIZED,
+};
+
+const char* RequestStartTriggerString(RequestStartTrigger trigger) {
+  switch (trigger) {
+    case RequestStartTrigger::NONE:
+      return "NONE";
+    case RequestStartTrigger::COMPLETION_PRE_BODY:
+      return "COMPLETION_PRE_BODY";
+    case RequestStartTrigger::COMPLETION_POST_BODY:
+      return "COMPLETION_POST_BODY";
+    case RequestStartTrigger::BODY_REACHED:
+      return "BODY_REACHED";
+    case RequestStartTrigger::CLIENT_KILL:
+      return "CLIENT_KILL";
+    case RequestStartTrigger::SPDY_PROXY_DETECTED:
+      return "SPDY_PROXY_DETECTED";
+    case RequestStartTrigger::REQUEST_REPRIORITIZED:
+      return "REQUEST_REPRIORITIZED";
+  }
+  NOTREACHED();
+  return "Unknown";
+}
+
 }  // namespace
 
 // The maximum number of delayable requests to allow to be in-flight at any
@@ -331,7 +363,7 @@
     SetRequestAttributes(request, DetermineRequestAttributes(request));
     if (ShouldStartRequest(request) == START_REQUEST) {
       // New requests can be started synchronously without issue.
-      StartRequest(request, START_SYNC);
+      StartRequest(request, START_SYNC, RequestStartTrigger::NONE);
     } else {
       pending_requests_.Insert(request);
     }
@@ -345,7 +377,10 @@
       EraseInFlightRequest(request);
 
       // Removing this request may have freed up another to load.
-      LoadAnyStartablePendingRequests();
+      LoadAnyStartablePendingRequests(
+          has_html_body_
+          ? RequestStartTrigger::COMPLETION_POST_BODY
+          : RequestStartTrigger::COMPLETION_PRE_BODY);
     }
   }
 
@@ -362,7 +397,7 @@
       pending_requests_.Erase(request);
       // Starting requests asynchronously ensures no side effects, and avoids
       // starting a bunch of requests that may be about to be deleted.
-      StartRequest(request, START_ASYNC);
+      StartRequest(request, START_ASYNC, RequestStartTrigger::CLIENT_KILL);
     }
     RequestSet unowned_requests;
     for (RequestSet::iterator it = in_flight_requests_.begin();
@@ -387,13 +422,13 @@
 
   void OnWillInsertBody() {
     has_html_body_ = true;
-    LoadAnyStartablePendingRequests();
+    LoadAnyStartablePendingRequests(RequestStartTrigger::BODY_REACHED);
   }
 
   void OnReceivedSpdyProxiedHttpResponse() {
     if (!using_spdy_proxy_) {
       using_spdy_proxy_ = true;
-      LoadAnyStartablePendingRequests();
+      LoadAnyStartablePendingRequests(RequestStartTrigger::SPDY_PROXY_DETECTED);
     }
   }
 
@@ -414,7 +449,8 @@
 
     if (new_priority_params.priority > old_priority_params.priority) {
       // Check if this request is now able to load at its new priority.
-      LoadAnyStartablePendingRequests();
+      LoadAnyStartablePendingRequests(
+          RequestStartTrigger::REQUEST_REPRIORITIZED);
     }
   }
 
@@ -560,7 +596,16 @@
   }
 
   void StartRequest(ScheduledResourceRequest* request,
-                    StartMode start_mode) {
+                    StartMode start_mode,
+                    RequestStartTrigger trigger) {
+    // Only log on requests that were blocked by the ResourceScheduler.
+    if (start_mode == START_ASYNC) {
+      DCHECK_NE(RequestStartTrigger::NONE, trigger);
+      request->url_request()->net_log().AddEvent(
+          net::NetLogEventType::RESOURCE_SCHEDULER_REQUEST_STARTED,
+          net::NetLog::StringCallback(
+              "trigger", RequestStartTriggerString(trigger)));
+    }
     InsertInFlightRequest(request);
     request->Start(start_mode);
   }
@@ -670,7 +715,7 @@
     return START_REQUEST;
   }
 
-  void LoadAnyStartablePendingRequests() {
+  void LoadAnyStartablePendingRequests(RequestStartTrigger trigger) {
     // We iterate through all the pending requests, starting with the highest
     // priority one. For each entry, one of three things can happen:
     // 1) We start the request, remove it from the list, and keep checking.
@@ -688,7 +733,7 @@
 
       if (query_result == START_REQUEST) {
         pending_requests_.Erase(request);
-        StartRequest(request, START_ASYNC);
+        StartRequest(request, START_ASYNC, trigger);
 
         // StartRequest can modify the pending list, so we (re)start evaluation
         // from the currently highest priority request. Avoid copying a singular
diff --git a/content/browser/media/capture/web_contents_tracker.cc b/content/browser/media/capture/web_contents_tracker.cc
index 668eb6d..770cb1e 100644
--- a/content/browser/media/capture/web_contents_tracker.cc
+++ b/content/browser/media/capture/web_contents_tracker.cc
@@ -5,21 +5,20 @@
 #include "content/browser/media/capture/web_contents_tracker.h"
 
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/browser/frame_host/render_frame_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 
 namespace content {
 
-WebContentsTracker::WebContentsTracker(bool track_fullscreen_rwh)
-    : track_fullscreen_rwh_(track_fullscreen_rwh),
-      last_target_(NULL) {}
+WebContentsTracker::WebContentsTracker(bool track_fullscreen_rwhv)
+    : track_fullscreen_rwhv_(track_fullscreen_rwhv),
+      last_target_view_(nullptr) {}
 
 WebContentsTracker::~WebContentsTracker() {
-  DCHECK(!web_contents()) << "BUG: Still observering!";
+  // Likely unintentional BUG if Stop() was not called before this point.
+  DCHECK(!web_contents());
 }
 
 void WebContentsTracker::Start(int render_process_id, int main_render_frame_id,
@@ -47,36 +46,34 @@
   resize_callback_.Reset();
 
   if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    WebContentsObserver::Observe(NULL);
+    WebContentsObserver::Observe(nullptr);
   } else {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(&WebContentsTracker::Observe, this,
-                   static_cast<WebContents*>(NULL)));
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                            base::Bind(&WebContentsTracker::Observe, this,
+                                       static_cast<WebContents*>(nullptr)));
   }
 }
 
-RenderWidgetHost* WebContentsTracker::GetTargetRenderWidgetHost() const {
+RenderWidgetHostView* WebContentsTracker::GetTargetView() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   WebContents* const wc = web_contents();
   if (!wc)
-    return NULL;
+    return nullptr;
 
-  RenderWidgetHost* rwh = NULL;
-  if (track_fullscreen_rwh_) {
-    RenderWidgetHostView* const view = wc->GetFullscreenRenderWidgetHostView();
-    if (view)
-      rwh = view->GetRenderWidgetHost();
-  }
-  if (!rwh) {
-    RenderFrameHostImpl* const rfh =
-        static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
-    if (rfh)
-      rwh = rfh->GetRenderWidgetHost();
+  if (track_fullscreen_rwhv_) {
+    if (auto* view = wc->GetFullscreenRenderWidgetHostView())
+      return view;
   }
 
-  return rwh;
+  if (auto* view = wc->GetRenderWidgetHostView()) {
+    // Make sure the RWHV is still associated with a RWH before considering the
+    // view "alive." This is because a null RWH indicates the RWHV has had its
+    // Destroy() method called.
+    if (view->GetRenderWidgetHost())
+      return view;
+  }
+  return nullptr;
 }
 
 void WebContentsTracker::SetResizeChangeCallback(
@@ -88,21 +85,23 @@
 void WebContentsTracker::OnPossibleTargetChange(bool force_callback_run) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  RenderWidgetHost* const rwh = GetTargetRenderWidgetHost();
-  if (rwh == last_target_ && !force_callback_run)
+  RenderWidgetHostView* const rwhv = GetTargetView();
+  if (rwhv == last_target_view_ && !force_callback_run) {
+    DVLOG(1) << "No target view change (RenderWidgetHostView@" << rwhv << ')';
     return;
-  DVLOG(1) << "Will report target change from RenderWidgetHost@" << last_target_
-           << " to RenderWidgetHost@" << rwh;
-  last_target_ = rwh;
+  }
+  DVLOG(1) << "Will report target change from RenderWidgetHostView@"
+           << last_target_view_ << " to RenderWidgetHostView@" << rwhv;
+  last_target_view_ = rwhv;
 
   if (task_runner_->BelongsToCurrentThread()) {
-    MaybeDoCallback(rwh != nullptr);
+    MaybeDoCallback(is_still_tracking());
     return;
   }
 
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&WebContentsTracker::MaybeDoCallback, this, rwh != nullptr));
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&WebContentsTracker::MaybeDoCallback, this,
+                                    is_still_tracking()));
 }
 
 void WebContentsTracker::MaybeDoCallback(bool was_still_tracking) {
@@ -135,13 +134,22 @@
   OnPossibleTargetChange(true);
 }
 
+void WebContentsTracker::RenderFrameCreated(
+    RenderFrameHost* render_frame_host) {
+  DVLOG(1) << "RenderFrameCreated(rfh=" << render_frame_host << ')';
+  OnPossibleTargetChange(false);
+}
+
 void WebContentsTracker::RenderFrameDeleted(
     RenderFrameHost* render_frame_host) {
+  DVLOG(1) << "RenderFrameDeleted(rfh=" << render_frame_host << ')';
   OnPossibleTargetChange(false);
 }
 
 void WebContentsTracker::RenderFrameHostChanged(RenderFrameHost* old_host,
                                                 RenderFrameHost* new_host) {
+  DVLOG(1) << "RenderFrameHostChanged(old=" << old_host << ", new=" << new_host
+           << ')';
   OnPossibleTargetChange(false);
 }
 
@@ -159,15 +167,18 @@
 }
 
 void WebContentsTracker::WebContentsDestroyed() {
-  Observe(NULL);
-  OnPossibleTargetChange(false);
+  DVLOG(1) << "WebContentsDestroyed()";
+  Observe(nullptr);
+  OnPossibleTargetChange(true);
 }
 
 void WebContentsTracker::DidShowFullscreenWidget() {
+  DVLOG(1) << "DidShowFullscreenWidget()";
   OnPossibleTargetChange(false);
 }
 
 void WebContentsTracker::DidDestroyFullscreenWidget() {
+  DVLOG(1) << "DidDestroyFullscreenWidget()";
   OnPossibleTargetChange(false);
 }
 
diff --git a/content/browser/media/capture/web_contents_tracker.h b/content/browser/media/capture/web_contents_tracker.h
index aacbfcf..0805772 100644
--- a/content/browser/media/capture/web_contents_tracker.h
+++ b/content/browser/media/capture/web_contents_tracker.h
@@ -4,14 +4,13 @@
 //
 // Given a starting render_process_id and main_render_frame_id, the
 // WebContentsTracker tracks changes to the active RenderFrameHost tree during
-// the lifetime of a WebContents instance.  This is used when mirroring tab
-// video and audio so that user navigations, crashes, iframes, etc., during a
-// tab's lifetime allow the capturing code to remain active on the
-// current/latest render frame tree.
+// the lifetime of a WebContents instance. This is used to maintain capture of
+// the WebContents's video and audio across page transitions such as user
+// navigations, crashes, iframes, etc..
 //
-// Threading issues: Start(), Stop() and the ChangeCallback are invoked on the
-// same thread.  This can be any thread, and the decision is locked-in by
-// WebContentsTracker when Start() is called.
+// Threading issues: Start(), Stop() and the ChangeCallback must be invoked on
+// the same thread. This can be any thread, and the decision is locked-in once
+// WebContentsTracker::Start() is called.
 
 #ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_TRACKER_H_
 #define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_TRACKER_H_
@@ -28,21 +27,22 @@
 
 namespace content {
 
-class RenderWidgetHost;
+class RenderWidgetHostView;
 
 class CONTENT_EXPORT WebContentsTracker
     : public base::RefCountedThreadSafe<WebContentsTracker>,
       public WebContentsObserver {
  public:
-  // If |track_fullscreen_rwh| is true, the ChangeCallback will be run when a
-  // WebContents shows/destroys a fullscreen RenderWidgetHost view.  If false,
-  // fullscreen events are ignored.  Specify true for video tab capture and
-  // false for audio tab capture.
-  explicit WebContentsTracker(bool track_fullscreen_rwh);
+  // If |track_fullscreen_rwhv| is true, the ChangeCallback will be run when a
+  // WebContents shows/destroys a fullscreen RenderWidgetHostView. If false,
+  // fullscreen events are ignored. Normally, specify true for video capture and
+  // false for audio capture.
+  explicit WebContentsTracker(bool track_fullscreen_rwhv);
 
-  // Callback to indicate a new RenderWidgetHost should be targeted for capture.
-  // This is also invoked with false to indicate tracking will not continue
-  // (i.e., the WebContents instance was not found or has been destroyed).
+  // Callback to indicate a new RenderWidgetHostView should be targeted for
+  // capture. This is also invoked with false to indicate tracking will not
+  // continue (i.e., the WebContents instance was not found or has been
+  // destroyed).
   typedef base::Callback<void(bool was_still_tracking)> ChangeCallback;
 
   // Start tracking.  The last-known |render_process_id| and
@@ -57,8 +57,13 @@
   // be invoked again.
   virtual void Stop();
 
-  // Current target.  This must only be called on the UI BrowserThread.
-  RenderWidgetHost* GetTargetRenderWidgetHost() const;
+  // Returns true if this tracker is still able to continue tracking changes.
+  // This must only be called on the UI BrowserThread.
+  bool is_still_tracking() const { return !!web_contents(); }
+
+  // Current target view. May return nullptr during certain transient periods.
+  // This must only be called on the UI BrowserThread.
+  RenderWidgetHostView* GetTargetView() const;
 
   // Set a callback that is run whenever the main frame of the WebContents is
   // resized.  This method must be called on the same thread that calls
@@ -71,10 +76,10 @@
   ~WebContentsTracker() override;
 
  private:
-  // Determine the target RenderWidgetHost and, if different from that last
-  // reported, runs the ChangeCallback on the appropriate thread.  If
+  // Determine the target RenderWidgetHostView and, if different from that last
+  // reported, runs the ChangeCallback on the appropriate thread. If
   // |force_callback_run| is true, the ChangeCallback is run even if the
-  // RenderWidgetHost has not changed.
+  // RenderWidgetHostView has not changed.
   void OnPossibleTargetChange(bool force_callback_run);
 
   // Called on the thread that Start()/Stop() are called on.  Checks whether the
@@ -91,41 +96,41 @@
   void StartObservingWebContents(int render_process_id,
                                  int main_render_frame_id);
 
-  // WebContentsObserver overrides: According to web_contents_observer.h, these
-  // two method overrides are all that is necessary to track the set of active
-  // RenderFrameHosts.
-  void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
+  // WebContentsObserver overrides: These events indicate that the view of the
+  // main frame may have changed.
+  void RenderFrameCreated(RenderFrameHost* render_frame_host) final;
+  void RenderFrameDeleted(RenderFrameHost* render_frame_host) final;
   void RenderFrameHostChanged(RenderFrameHost* old_host,
-                              RenderFrameHost* new_host) override;
+                              RenderFrameHost* new_host) final;
 
   // WebContentsObserver override to notify the client that the source size has
   // changed.
-  void MainFrameWasResized(bool width_changed) override;
+  void MainFrameWasResized(bool width_changed) final;
 
   // WebContentsObserver override to notify the client that the capture target
   // has been permanently lost.
-  void WebContentsDestroyed() override;
+  void WebContentsDestroyed() final;
 
   // WebContentsObserver overrides to notify the client that the capture target
   // may have changed due to a separate fullscreen widget shown/destroyed.
-  void DidShowFullscreenWidget() override;
-  void DidDestroyFullscreenWidget() override;
+  void DidShowFullscreenWidget() final;
+  void DidDestroyFullscreenWidget() final;
 
   // If true, the client is interested in the showing/destruction of fullscreen
-  // RenderWidgetHosts.
-  const bool track_fullscreen_rwh_;
+  // RenderWidgetHostViews.
+  const bool track_fullscreen_rwhv_;
+
+  // Pointer to the RenderWidgetHostView provided in the last run of
+  // |callback_|. This is used to eliminate duplicate callback runs.
+  RenderWidgetHostView* last_target_view_;
 
   // TaskRunner corresponding to the thread that called Start().
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  // Callback to run when the target RenderWidgetHost has changed.
+  // Callback to run when the target RenderWidgetHostView has changed.
   ChangeCallback callback_;
 
-  // Pointer to the RenderWidgetHost provided in the last run of |callback_|.
-  // This is used to eliminate duplicate callback runs.
-  RenderWidgetHost* last_target_;
-
-  // Callback to run when the target RenderWidgetHost has resized.
+  // Callback to run when the target RenderWidgetHostView has resized.
   base::Closure resize_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsTracker);
diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc
index d258238e..aae4ed1d 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -155,7 +155,7 @@
   base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_;
 };
 
-// ContentCaptureSubscription is the relationship between a RenderWidgetHost
+// ContentCaptureSubscription is the relationship between a RenderWidgetHostView
 // whose content is updating, a subscriber that is deciding which of these
 // updates to capture (and where to deliver them to), and a callback that
 // knows how to do the capture and prepare the result for delivery.
@@ -180,7 +180,7 @@
   // subscription will invoke |capture_callback| on the UI thread to do the
   // work.
   ContentCaptureSubscription(
-      const RenderWidgetHost& source,
+      base::WeakPtr<RenderWidgetHostViewBase> source_view,
       const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
       const CaptureCallback& capture_callback);
   ~ContentCaptureSubscription();
@@ -191,11 +191,7 @@
   // Called for active frame refresh requests, or mouse activity events.
   void OnEvent(FrameSubscriber* subscriber);
 
-  // Maintain a weak reference to the RenderWidgetHost (via its routing ID),
-  // since the instance could be destroyed externally during the lifetime of
-  // |this|.
-  const int render_process_id_;
-  const int render_widget_id_;
+  const base::WeakPtr<RenderWidgetHostViewBase> source_view_;
 
   std::unique_ptr<FrameSubscriber> refresh_subscriber_;
   std::unique_ptr<FrameSubscriber> mouse_activity_subscriber_;
@@ -288,9 +284,10 @@
       const gfx::Rect& region_in_frame,
       bool success);
 
-  // Remove the old subscription, and attempt to start a new one if |had_target|
-  // is true.
-  void RenewFrameSubscription(bool had_target);
+  // Remove the old subscription, and attempt to start a new one. If
+  // |is_still_tracking| is false, emit an error rather than attempt to start a
+  // new subscription.
+  void RenewFrameSubscription(bool is_still_tracking);
 
   // Called whenever the render widget is resized.
   void UpdateCaptureSize();
@@ -300,7 +297,7 @@
   const int initial_main_render_frame_id_;
 
   // Tracks events and calls back to RenewFrameSubscription() to maintain
-  // capture on the correct RenderWidgetHost.
+  // capture on the correct RenderWidgetHostView.
   const scoped_refptr<WebContentsTracker> tracker_;
 
   // Set to false to prevent the capture size from automatically adjusting in
@@ -404,21 +401,17 @@
 }
 
 ContentCaptureSubscription::ContentCaptureSubscription(
-    const RenderWidgetHost& source,
+    base::WeakPtr<RenderWidgetHostViewBase> source_view,
     const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
     const CaptureCallback& capture_callback)
-    : render_process_id_(source.GetProcess()->GetID()),
-      render_widget_id_(source.GetRoutingID()),
-      capture_callback_(capture_callback) {
+    : source_view_(source_view), capture_callback_(capture_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(source_view_);
 
-  RenderWidgetHostView* const view = source.GetView();
 #if defined(USE_AURA) || defined(OS_MACOSX)
-  if (view) {
-    cursor_renderer_ = CursorRenderer::Create(view->GetNativeView());
-    window_activity_tracker_ =
-        WindowActivityTracker::Create(view->GetNativeView());
-  }
+  cursor_renderer_ = CursorRenderer::Create(source_view_->GetNativeView());
+  window_activity_tracker_ =
+      WindowActivityTracker::Create(source_view_->GetNativeView());
 #endif
   refresh_subscriber_.reset(new FrameSubscriber(
       media::VideoCaptureOracle::kActiveRefreshRequest, oracle_proxy,
@@ -435,16 +428,14 @@
 
   // Subscribe to compositor updates. These will be serviced directly by the
   // oracle.
-  if (view) {
-    std::unique_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
-        new FrameSubscriber(
-            media::VideoCaptureOracle::kCompositorUpdate, oracle_proxy,
-            cursor_renderer_ ? cursor_renderer_->GetWeakPtr()
-                             : base::WeakPtr<CursorRenderer>(),
-            window_activity_tracker_ ? window_activity_tracker_->GetWeakPtr()
-                                     : base::WeakPtr<WindowActivityTracker>()));
-    view->BeginFrameSubscription(std::move(subscriber));
-  }
+  std::unique_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
+      new FrameSubscriber(
+          media::VideoCaptureOracle::kCompositorUpdate, oracle_proxy,
+          cursor_renderer_ ? cursor_renderer_->GetWeakPtr()
+                           : base::WeakPtr<CursorRenderer>(),
+          window_activity_tracker_ ? window_activity_tracker_->GetWeakPtr()
+                                   : base::WeakPtr<WindowActivityTracker>()));
+  source_view_->BeginFrameSubscription(std::move(subscriber));
 
   // Subscribe to mouse movement and mouse cursor update events.
   if (window_activity_tracker_) {
@@ -462,11 +453,13 @@
     return;
 
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  RenderWidgetHost* const source =
-      RenderWidgetHost::FromID(render_process_id_, render_widget_id_);
-  RenderWidgetHostView* const view = source ? source->GetView() : NULL;
-  if (view)
-    view->EndFrameSubscription();
+  // If the RWHV weak pointer is still valid, make sure the view is still
+  // associated with a RWH before attempting to end the frame subscription. This
+  // is because a null RWH indicates the RWHV has had its Destroy() method
+  // called, which makes it invalid to call any of its methods that assume the
+  // compositor is present.
+  if (source_view_ && source_view_->GetRenderWidgetHost())
+    source_view_->EndFrameSubscription();
 }
 
 void ContentCaptureSubscription::MaybeCaptureForRefresh() {
@@ -641,7 +634,7 @@
     return;
   frame_capture_active_ = false;
   if (IsStarted())
-    RenewFrameSubscription(true);
+    RenewFrameSubscription(tracker_->is_still_tracking());
 }
 
 void WebContentsCaptureMachine::Resume() {
@@ -656,7 +649,7 @@
     return;
   frame_capture_active_ = true;
   if (IsStarted())
-    RenewFrameSubscription(true);
+    RenewFrameSubscription(tracker_->is_still_tracking());
 }
 
 void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
@@ -714,9 +707,8 @@
         deliver_frame_cb) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  RenderWidgetHost* rwh = tracker_->GetTargetRenderWidgetHost();
-  RenderWidgetHostViewBase* view =
-      rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL;
+  auto* const view =
+      static_cast<RenderWidgetHostViewBase*>(tracker_->GetTargetView());
   if (!view) {
     deliver_frame_cb.Run(base::TimeTicks(), gfx::Rect(), false);
     return;
@@ -736,13 +728,15 @@
             ? gfx::Size()
             : media::ComputeLetterboxRegion(target->visible_rect(), view_size)
                   .size();
-    rwh->CopyFromBackingStore(
-        gfx::Rect(),
-        fitted_size,  // Size here is a request not always honored.
-        base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
-                   weak_ptr_factory_.GetWeakPtr(), start_time, target,
-                   deliver_frame_cb),
-        kN32_SkColorType);
+    if (auto* rwh = view->GetRenderWidgetHost()) {
+      rwh->CopyFromBackingStore(
+          gfx::Rect(),
+          fitted_size,  // Size here is a request not always honored.
+          base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
+                     weak_ptr_factory_.GetWeakPtr(), start_time, target,
+                     deliver_frame_cb),
+          kN32_SkColorType);
+    }
   }
 }
 
@@ -795,9 +789,7 @@
   // render widget to the "preferred size," the widget will be physically
   // rendered at the exact capture size, thereby eliminating unnecessary scaling
   // operations in the graphics pipeline.
-  RenderWidgetHost* const rwh = tracker_->GetTargetRenderWidgetHost();
-  RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL;
-  if (rwhv) {
+  if (auto* rwhv = tracker_->GetTargetView()) {
     const gfx::NativeView view = rwhv->GetNativeView();
     const float scale = ui::GetScaleFactorForNativeView(view);
     if (scale > 1.0f) {
@@ -849,20 +841,16 @@
   deliver_frame_cb.Run(start_time, region_in_frame, success);
 }
 
-void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) {
+void WebContentsCaptureMachine::RenewFrameSubscription(bool is_still_tracking) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  RenderWidgetHost* const rwh =
-      had_target ? tracker_->GetTargetRenderWidgetHost() : nullptr;
-
   // Always destroy the old subscription before creating a new one.
   if (subscription_) {
-    DVLOG(1) << "Cancelling existing ContentCaptureSubscription.";
+    DVLOG(1) << "Cancelling existing subscription.";
     subscription_.reset();
   }
 
-  if (!rwh) {
-    DVLOG(1) << "Cannot renew ContentCaptureSubscription: no RWH target.";
+  if (!is_still_tracking) {
     if (IsStarted()) {
       // Tracking of WebContents and/or its main frame has failed before Stop()
       // was called, so report this as an error:
@@ -872,12 +860,23 @@
     return;
   }
 
-  if (frame_capture_active_) {
-    DVLOG(1) << "Renewing ContentCaptureSubscription to RWH@" << rwh;
-    subscription_.reset(new ContentCaptureSubscription(
-        *rwh, oracle_proxy_, base::Bind(&WebContentsCaptureMachine::Capture,
-                                        weak_ptr_factory_.GetWeakPtr())));
+  if (!frame_capture_active_) {
+    DVLOG(1) << "Not renewing subscription: Frame capture is suspended.";
+    return;
   }
+
+  auto* const view =
+      static_cast<RenderWidgetHostViewBase*>(tracker_->GetTargetView());
+  if (!view) {
+    DVLOG(1) << "Cannot renew subscription: No view to capture.";
+    return;
+  }
+
+  DVLOG(1) << "Renewing subscription to RenderWidgetHostView@" << view;
+  subscription_.reset(new ContentCaptureSubscription(
+      view->GetWeakPtr(), oracle_proxy_,
+      base::Bind(&WebContentsCaptureMachine::Capture,
+                 weak_ptr_factory_.GetWeakPtr())));
 }
 
 void WebContentsCaptureMachine::UpdateCaptureSize() {
@@ -885,8 +884,7 @@
 
   if (!oracle_proxy_)
     return;
-  RenderWidgetHost* const rwh = tracker_->GetTargetRenderWidgetHost();
-  RenderWidgetHostView* const view = rwh ? rwh->GetView() : nullptr;
+  RenderWidgetHostView* const view = tracker_->GetTargetView();
   if (!view)
     return;
 
diff --git a/content/browser/renderer_host/media/OWNERS b/content/browser/renderer_host/media/OWNERS
index 8ac77eba..a8e56056 100644
--- a/content/browser/renderer_host/media/OWNERS
+++ b/content/browser/renderer_host/media/OWNERS
@@ -6,4 +6,5 @@
 
 per-file gpu_memory*=mcasas@chromium.org
 per-file shared_memory*=mcasas@chromium.org
-per-file video_capture*=mcasas@chromium.org
+per-file video_*=mcasas@chromium.org
+per-file video_*=chfremer@chromium.org
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index fd171279..ac5bc721 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -18,12 +18,11 @@
 #include "build/build_config.h"
 #include "components/display_compositor/gl_helper.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
-#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
 #include "media/base/video_frame.h"
-#include "media/capture/video/video_capture_buffer_pool_impl.h"
+#include "media/capture/video/video_capture_buffer_pool.h"
 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
 #include "media/capture/video/video_capture_device_client.h"
 
@@ -46,50 +45,6 @@
         name, \
         (height) ? ((width) * 100) / (height) : kInfiniteRatio);
 
-std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
-    const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) {
-  return base::MakeUnique<VideoCaptureGpuJpegDecoder>(decode_done_cb);
-}
-
-// Decorator for media::VideoFrameReceiver that forwards all incoming calls
-// to the Browser IO thread.
-class VideoFrameReceiverOnIOThread : public media::VideoFrameReceiver {
- public:
-  explicit VideoFrameReceiverOnIOThread(
-      const base::WeakPtr<VideoFrameReceiver>& receiver)
-      : receiver_(receiver) {}
-
-  void OnIncomingCapturedVideoFrame(
-      std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
-      scoped_refptr<media::VideoFrame> frame) override {
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&VideoFrameReceiver::OnIncomingCapturedVideoFrame, receiver_,
-                   base::Passed(&buffer), std::move(frame)));
-  }
-
-  void OnError() override {
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&VideoFrameReceiver::OnError, receiver_));
-  }
-
-  void OnLog(const std::string& message) override {
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&VideoFrameReceiver::OnLog, receiver_, message));
-  }
-
-  void OnBufferDestroyed(int buffer_id_to_drop) override {
-    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                            base::Bind(&VideoFrameReceiver::OnBufferDestroyed,
-                                       receiver_, buffer_id_to_drop));
-  }
-
- private:
-  base::WeakPtr<VideoFrameReceiver> receiver_;
-};
-
 }  // anonymous namespace
 
 struct VideoCaptureController::ControllerClient {
@@ -140,12 +95,12 @@
     int buffer_id,
     int frame_feedback_id,
     media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer,
-    scoped_refptr<media::VideoCaptureBufferPool> buffer_pool,
+    media::FrameBufferPool* frame_buffer_pool,
     scoped_refptr<media::VideoFrame> frame)
     : buffer_id_(buffer_id),
       frame_feedback_id_(frame_feedback_id),
       consumer_feedback_observer_(consumer_feedback_observer),
-      buffer_pool_(std::move(buffer_pool)),
+      frame_buffer_pool_(frame_buffer_pool),
       frame_(std::move(frame)),
       max_consumer_utilization_(
           media::VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded),
@@ -166,7 +121,8 @@
 
 void VideoCaptureController::BufferState::IncreaseConsumerCount() {
   if (consumer_hold_count_ == 0)
-    buffer_pool_->HoldForConsumers(buffer_id_, 1);
+    if (frame_buffer_pool_ != nullptr)
+      frame_buffer_pool_->SetBufferHold(buffer_id_);
   consumer_hold_count_++;
 }
 
@@ -179,7 +135,8 @@
       consumer_feedback_observer_->OnUtilizationReport(
           frame_feedback_id_, max_consumer_utilization_);
     }
-    buffer_pool_->RelinquishConsumerHold(buffer_id_, 1);
+    if (frame_buffer_pool_ != nullptr)
+      frame_buffer_pool_->ReleaseBufferHold(buffer_id_);
     max_consumer_utilization_ =
         media::VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded;
   }
@@ -194,10 +151,13 @@
   consumer_feedback_observer_ = consumer_feedback_observer;
 }
 
-VideoCaptureController::VideoCaptureController(int max_buffers)
-    : buffer_pool_(new media::VideoCaptureBufferPoolImpl(
-          base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(),
-          max_buffers)),
+void VideoCaptureController::BufferState::SetFrameBufferPool(
+    media::FrameBufferPool* frame_buffer_pool) {
+  frame_buffer_pool_ = frame_buffer_pool;
+}
+
+VideoCaptureController::VideoCaptureController()
+    : frame_buffer_pool_(nullptr),
       consumer_feedback_observer_(nullptr),
       state_(VIDEO_CAPTURE_STATE_STARTED),
       has_received_frames_(false),
@@ -205,11 +165,22 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
+VideoCaptureController::~VideoCaptureController() = default;
+
 base::WeakPtr<VideoCaptureController>
 VideoCaptureController::GetWeakPtrForIOThread() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+void VideoCaptureController::SetFrameBufferPool(
+    std::unique_ptr<media::FrameBufferPool> frame_buffer_pool) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  frame_buffer_pool_ = std::move(frame_buffer_pool);
+  // Update existing BufferState entries.
+  for (auto& entry : buffer_id_to_state_map_)
+    entry.second.SetFrameBufferPool(frame_buffer_pool_.get());
+}
+
 void VideoCaptureController::SetConsumerFeedbackObserver(
     std::unique_ptr<media::VideoFrameConsumerFeedbackObserver>
         consumer_feedback_observer) {
@@ -220,18 +191,6 @@
     entry.second.SetConsumerFeedbackObserver(consumer_feedback_observer_.get());
 }
 
-std::unique_ptr<media::VideoCaptureDevice::Client>
-VideoCaptureController::NewDeviceClient() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return base::MakeUnique<media::VideoCaptureDeviceClient>(
-      base::MakeUnique<VideoFrameReceiverOnIOThread>(
-          this->GetWeakPtrForIOThread()),
-      buffer_pool_,
-      base::Bind(&CreateGpuJpegDecoder,
-                 base::Bind(&VideoFrameReceiver::OnIncomingCapturedVideoFrame,
-                            this->GetWeakPtrForIOThread())));
-}
-
 void VideoCaptureController::AddClient(
     VideoCaptureControllerID id,
     VideoCaptureControllerEventHandler* event_handler,
@@ -378,7 +337,6 @@
     VideoCaptureControllerID id,
     VideoCaptureControllerEventHandler* event_handler,
     int buffer_id,
-    const gpu::SyncToken& sync_token,
     double consumer_resource_utilization) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -410,13 +368,11 @@
   return video_capture_format_;
 }
 
-VideoCaptureController::~VideoCaptureController() {
-}
-
 void VideoCaptureController::OnIncomingCapturedVideoFrame(
     std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
     scoped_refptr<VideoFrame> frame) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(frame_buffer_pool_);
   const int buffer_id = buffer->id();
   DCHECK_NE(buffer_id, media::VideoCaptureBufferPool::kInvalidId);
 
@@ -426,7 +382,7 @@
           .insert(std::make_pair(
               buffer_id, BufferState(buffer_id, buffer->frame_feedback_id(),
                                      consumer_feedback_observer_.get(),
-                                     buffer_pool_, frame)))
+                                     frame_buffer_pool_.get(), frame)))
           .first;
   BufferState& buffer_state = it->second;
   DCHECK(buffer_state.HasZeroConsumerHoldCount());
@@ -519,6 +475,7 @@
 
 void VideoCaptureController::OnBufferDestroyed(int buffer_id_to_drop) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(frame_buffer_pool_);
 
   for (const auto& client : controller_clients_) {
     if (client->session_closed)
@@ -546,7 +503,7 @@
 
   const int buffer_id = buffer->id();
   mojo::ScopedSharedBufferHandle handle =
-      buffer_pool_->GetHandleForTransit(buffer_id);
+      frame_buffer_pool_->GetHandleForTransit(buffer_id);
   client->event_handler->OnBufferCreated(client->controller_id,
                                          std::move(handle),
                                          buffer->mapped_size(), buffer_id);
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index b1a455f..4be8085 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -55,23 +55,27 @@
 #include "media/capture/video_capture_types.h"
 
 namespace media {
-class VideoCaptureBufferPool;
+class FrameBufferPool;
 }
 
 namespace content {
 
 class CONTENT_EXPORT VideoCaptureController : public media::VideoFrameReceiver {
  public:
-  // |max_buffers| is the maximum number of video frame buffers in-flight at any
-  // one time.  This value should be based on the logical capacity of the
-  // capture pipeline, and not on hardware performance.  For example, tab
-  // capture requires more buffers than webcam capture because the pipeline is
-  // longer (it includes read-backs pending in the GPU pipeline).
-  explicit VideoCaptureController(int max_buffers);
+  VideoCaptureController();
   ~VideoCaptureController() override;
 
   base::WeakPtr<VideoCaptureController> GetWeakPtrForIOThread();
 
+  // Factory code creating instances of VideoCaptureController must set a
+  // FrameBufferPool before any of the media::VideoFrameReceiver are used.
+  // Setting the observer is done in this method separate from the constructor
+  // in order to allow use the media::VideoFrameReceiver methods
+  // before the observer can be provided. (This is the case with
+  // VideoCaptureManager).
+  void SetFrameBufferPool(
+      std::unique_ptr<media::FrameBufferPool> frame_buffer_pool);
+
   // Factory code creating instances of VideoCaptureController may optionally
   // set a VideoFrameConsumerFeedbackObserver. Setting the observer is done in
   // this method separate from the constructor to allow clients to create and
@@ -80,10 +84,6 @@
   void SetConsumerFeedbackObserver(
       std::unique_ptr<media::VideoFrameConsumerFeedbackObserver> observer);
 
-  // Return a new VideoCaptureDeviceClient to forward capture events to this
-  // instance.
-  std::unique_ptr<media::VideoCaptureDevice::Client> NewDeviceClient();
-
   // Start video capturing and try to use the resolution specified in |params|.
   // Buffers will be shared to the client as necessary. The client will continue
   // to receive frames from the device until RemoveClient() is called.
@@ -120,15 +120,12 @@
   void StopSession(int session_id);
 
   // Return a buffer with id |buffer_id| previously given in
-  // VideoCaptureControllerEventHandler::OnBufferReady. In the case that the
-  // buffer was backed by a texture, |sync_token| will be waited on before
-  // destroying or recycling the texture, to synchronize with texture users in
-  // the renderer process. If the consumer provided resource utilization
+  // VideoCaptureControllerEventHandler::OnBufferReady.
+  // If the consumer provided resource utilization
   // feedback, this will be passed here (-1.0 indicates no feedback).
   void ReturnBuffer(VideoCaptureControllerID id,
                     VideoCaptureControllerEventHandler* event_handler,
                     int buffer_id,
-                    const gpu::SyncToken& sync_token,
                     double consumer_resource_utilization);
 
   const media::VideoCaptureFormat& GetVideoCaptureFormat() const;
@@ -153,7 +150,7 @@
         int buffer_id,
         int frame_feedback_id,
         media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer,
-        scoped_refptr<media::VideoCaptureBufferPool> buffer_pool,
+        media::FrameBufferPool* frame_buffer_pool,
         scoped_refptr<media::VideoFrame> frame);
     ~BufferState();
     BufferState(const BufferState& other);
@@ -163,12 +160,13 @@
     bool HasZeroConsumerHoldCount();
     void SetConsumerFeedbackObserver(
         media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer);
+    void SetFrameBufferPool(media::FrameBufferPool* frame_buffer_pool);
 
    private:
     const int buffer_id_;
     const int frame_feedback_id_;
     media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer_;
-    const scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
+    media::FrameBufferPool* frame_buffer_pool_;
     const scoped_refptr<media::VideoFrame> frame_;
     double max_consumer_utilization_;
     int consumer_hold_count_;
@@ -188,8 +186,7 @@
   ControllerClient* FindClient(int session_id,
                                const ControllerClients& clients);
 
-  // The pool of shared-memory buffers used for capturing.
-  const scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
+  std::unique_ptr<media::FrameBufferPool> frame_buffer_pool_;
 
   std::unique_ptr<media::VideoFrameConsumerFeedbackObserver>
       consumer_feedback_observer_;
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 8a28593d..e9e59c4 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -24,11 +24,16 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/renderer_host/media/media_stream_provider.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
+#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
+#include "content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h"
 #include "content/common/media/media_stream_options.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/base/video_frame_metadata.h"
 #include "media/base/video_util.h"
+#include "media/capture/video/video_capture_buffer_pool_impl.h"
+#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
+#include "media/capture/video/video_capture_device_client.h"
 #include "media/capture/video_capture_types.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,13 +46,17 @@
 
 namespace content {
 
+std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
+    const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) {
+  return base::MakeUnique<content::VideoCaptureGpuJpegDecoder>(decode_done_cb);
+}
+
 class MockVideoCaptureControllerEventHandler
     : public VideoCaptureControllerEventHandler {
  public:
   explicit MockVideoCaptureControllerEventHandler(
       VideoCaptureController* controller)
-      : controller_(controller),
-        resource_utilization_(-1.0) {}
+      : controller_(controller), resource_utilization_(-1.0) {}
   ~MockVideoCaptureControllerEventHandler() override {}
 
   // These mock methods are delegated to by our fake implementation of
@@ -58,12 +67,11 @@
   MOCK_METHOD1(DoEnded, void(VideoCaptureControllerID));
   MOCK_METHOD1(DoError, void(VideoCaptureControllerID));
 
-  void OnError(VideoCaptureControllerID id) override {
-    DoError(id);
-  }
+  void OnError(VideoCaptureControllerID id) override { DoError(id); }
   void OnBufferCreated(VideoCaptureControllerID id,
                        mojo::ScopedSharedBufferHandle handle,
-                       int length, int buffer_id) override {
+                       int length,
+                       int buffer_id) override {
     DoBufferCreated(id);
   }
   void OnBufferDestroyed(VideoCaptureControllerID id, int buffer_id) override {
@@ -78,10 +86,9 @@
         media::VideoFrameMetadata::REFERENCE_TIME, &reference_time));
     DoBufferReady(id, frame->coded_size());
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&VideoCaptureController::ReturnBuffer,
-                   base::Unretained(controller_), id, this, buffer_id,
-                   gpu::SyncToken(), resource_utilization_));
+        FROM_HERE, base::Bind(&VideoCaptureController::ReturnBuffer,
+                              base::Unretained(controller_), id, this,
+                              buffer_id, resource_utilization_));
   }
   void OnEnded(VideoCaptureControllerID id) override {
     DoEnded(id);
@@ -104,6 +111,14 @@
                void(int frame_feedback_id, double utilization));
 };
 
+class MockFrameBufferPool : public media::FrameBufferPool {
+ public:
+  MOCK_METHOD1(SetBufferHold, void(int buffer_id));
+  MOCK_METHOD1(ReleaseBufferHold, void(int buffer_id));
+  MOCK_METHOD1(GetHandleForTransit,
+               mojo::ScopedSharedBufferHandle(int buffer_id));
+};
+
 // Test class.
 class VideoCaptureControllerTest
     : public testing::Test,
@@ -116,8 +131,22 @@
   static const int kPoolSize = 3;
 
   void SetUp() override {
-    controller_.reset(new VideoCaptureController(kPoolSize));
-    device_ = controller_->NewDeviceClient();
+    scoped_refptr<media::VideoCaptureBufferPool> buffer_pool(
+        new media::VideoCaptureBufferPoolImpl(
+            base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(),
+            kPoolSize));
+    controller_.reset(new VideoCaptureController());
+    device_client_.reset(new media::VideoCaptureDeviceClient(
+        base::MakeUnique<VideoFrameReceiverOnIOThread>(
+            controller_->GetWeakPtrForIOThread()),
+        buffer_pool,
+        base::Bind(
+            &CreateGpuJpegDecoder,
+            base::Bind(&media::VideoFrameReceiver::OnIncomingCapturedVideoFrame,
+                       controller_->GetWeakPtrForIOThread()))));
+    auto frame_receiver_observer = base::MakeUnique<MockFrameBufferPool>();
+    mock_frame_receiver_observer_ = frame_receiver_observer.get();
+    controller_->SetFrameBufferPool(std::move(frame_receiver_observer));
     auto consumer_feedback_observer =
         base::MakeUnique<MockConsumerFeedbackObserver>();
     mock_consumer_feedback_observer_ = consumer_feedback_observer.get();
@@ -148,7 +177,8 @@
   std::unique_ptr<MockVideoCaptureControllerEventHandler> client_a_;
   std::unique_ptr<MockVideoCaptureControllerEventHandler> client_b_;
   std::unique_ptr<VideoCaptureController> controller_;
-  std::unique_ptr<media::VideoCaptureDevice::Client> device_;
+  std::unique_ptr<media::VideoCaptureDevice::Client> device_client_;
+  MockFrameBufferPool* mock_frame_receiver_observer_;
   MockConsumerFeedbackObserver* mock_consumer_feedback_observer_;
 
  private:
@@ -300,13 +330,13 @@
   // side effect this will cause the first buffer to be shared with clients.
   uint8_t buffer_no = 1;
   const int arbitrary_frame_feedback_id = 101;
-  ASSERT_EQ(0.0, device_->GetBufferPoolUtilization());
+  ASSERT_EQ(0.0, device_client_->GetBufferPoolUtilization());
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
-      device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU,
-                                   arbitrary_frame_feedback_id));
+      device_client_->ReserveOutputBuffer(capture_resolution, format,
+                                          media::PIXEL_STORAGE_CPU,
+                                          arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
-  ASSERT_EQ(1.0 / kPoolSize, device_->GetBufferPoolUtilization());
+  ASSERT_EQ(1.0 / kPoolSize, device_client_->GetBufferPoolUtilization());
   memset(buffer->data(), buffer_no++, buffer->mapped_size());
   {
     InSequence s;
@@ -333,30 +363,37 @@
       media::VideoFrameMetadata::RESOURCE_UTILIZATION));
   client_a_->resource_utilization_ = 0.5;
   client_b_->resource_utilization_ = -1.0;
-
-  // Expect VideoCaptureController to call the load observer with a
-  // resource utilization of 0.5 (the largest of all reported values).
-  EXPECT_CALL(*mock_consumer_feedback_observer_,
-              OnUtilizationReport(arbitrary_frame_feedback_id, 0.5))
-      .Times(1);
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_frame_receiver_observer_, SetBufferHold(buffer->id()))
+        .Times(1);
+    // Expect VideoCaptureController to call the load observer with a
+    // resource utilization of 0.5 (the largest of all reported values).
+    EXPECT_CALL(*mock_consumer_feedback_observer_,
+                OnUtilizationReport(arbitrary_frame_feedback_id, 0.5))
+        .Times(1);
+    EXPECT_CALL(*mock_frame_receiver_observer_, ReleaseBufferHold(buffer->id()))
+        .Times(1);
+  }
 
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
 
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
   Mock::VerifyAndClearExpectations(client_b_.get());
   Mock::VerifyAndClearExpectations(mock_consumer_feedback_observer_);
+  Mock::VerifyAndClearExpectations(mock_frame_receiver_observer_);
 
   // Second buffer which ought to use the same shared memory buffer. In this
   // case pretend that the Buffer pointer is held by the device for a long
   // delay. This shouldn't affect anything.
   const int arbitrary_frame_feedback_id_2 = 102;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer2 =
-      device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU,
-                                   arbitrary_frame_feedback_id_2);
+      device_client_->ReserveOutputBuffer(capture_resolution, format,
+                                          media::PIXEL_STORAGE_CPU,
+                                          arbitrary_frame_feedback_id_2);
   ASSERT_TRUE(buffer2.get());
   memset(buffer2->data(), buffer_no++, buffer2->mapped_size());
   video_frame = WrapBuffer(capture_resolution,
@@ -367,11 +404,21 @@
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
   // Expect VideoCaptureController to call the load observer with a
   // resource utilization of 3.14 (the largest of all reported values).
-  EXPECT_CALL(*mock_consumer_feedback_observer_,
-              OnUtilizationReport(arbitrary_frame_feedback_id_2, 3.14))
-      .Times(1);
+  {
+    InSequence s;
+    EXPECT_CALL(*mock_frame_receiver_observer_, SetBufferHold(buffer2->id()))
+        .Times(1);
+    // Expect VideoCaptureController to call the load observer with a
+    // resource utilization of 3.14 (the largest of all reported values).
+    EXPECT_CALL(*mock_consumer_feedback_observer_,
+                OnUtilizationReport(arbitrary_frame_feedback_id_2, 3.14))
+        .Times(1);
+    EXPECT_CALL(*mock_frame_receiver_observer_,
+                ReleaseBufferHold(buffer2->id()))
+        .Times(1);
+  }
 
-  device_->OnIncomingCapturedVideoFrame(std::move(buffer2), video_frame);
+  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer2), video_frame);
 
   // The buffer should be delivered to the clients in any order.
   {
@@ -396,6 +443,7 @@
   Mock::VerifyAndClearExpectations(client_a_.get());
   Mock::VerifyAndClearExpectations(client_b_.get());
   Mock::VerifyAndClearExpectations(mock_consumer_feedback_observer_);
+  Mock::VerifyAndClearExpectations(mock_frame_receiver_observer_);
 
   // Add a fourth client now that some buffers have come through.
   controller_->AddClient(client_b_route_2,
@@ -408,9 +456,9 @@
   for (int i = 0; i < kPoolSize; i++) {
     const int arbitrary_frame_feedback_id = 200 + i;
     std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer =
-        device_->ReserveOutputBuffer(capture_resolution, format,
-                                     media::PIXEL_STORAGE_CPU,
-                                     arbitrary_frame_feedback_id);
+        device_client_->ReserveOutputBuffer(capture_resolution, format,
+                                            media::PIXEL_STORAGE_CPU,
+                                            arbitrary_frame_feedback_id);
     ASSERT_TRUE(buffer.get());
     memset(buffer->data(), buffer_no++, buffer->mapped_size());
     video_frame = WrapBuffer(capture_resolution,
@@ -418,10 +466,11 @@
     ASSERT_TRUE(video_frame);
     video_frame->metadata()->SetTimeTicks(
         media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-    device_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+    device_client_->OnIncomingCapturedVideoFrame(std::move(buffer),
+                                                 video_frame);
   }
   // ReserveOutputBuffer ought to fail now, because the pool is depleted.
-  ASSERT_FALSE(device_
+  ASSERT_FALSE(device_client_
                    ->ReserveOutputBuffer(capture_resolution, format,
                                          media::PIXEL_STORAGE_CPU,
                                          arbitrary_frame_feedback_id)
@@ -456,9 +505,9 @@
   controller_->StopSession(300);
   // Queue up another buffer.
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer3 =
-      device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU,
-                                   arbitrary_frame_feedback_id);
+      device_client_->ReserveOutputBuffer(capture_resolution, format,
+                                          media::PIXEL_STORAGE_CPU,
+                                          arbitrary_frame_feedback_id);
   ASSERT_TRUE(buffer3.get());
   memset(buffer3->data(), buffer_no++, buffer3->mapped_size());
   video_frame = WrapBuffer(capture_resolution,
@@ -466,12 +515,12 @@
   ASSERT_TRUE(video_frame);
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_->OnIncomingCapturedVideoFrame(std::move(buffer3), video_frame);
+  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer3), video_frame);
 
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer4 =
-      device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU,
-                                   arbitrary_frame_feedback_id);
+      device_client_->ReserveOutputBuffer(capture_resolution, format,
+                                          media::PIXEL_STORAGE_CPU,
+                                          arbitrary_frame_feedback_id);
   {
     // Kill A2 via session close (posts a task to disconnect, but A2 must not
     // be sent either of these two buffers).
@@ -485,7 +534,7 @@
   ASSERT_TRUE(video_frame);
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_->OnIncomingCapturedVideoFrame(std::move(buffer4), video_frame);
+  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer4), video_frame);
   // B2 is the only client left, and is the only one that should
   // get the buffer.
   EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2, capture_resolution))
@@ -515,7 +564,7 @@
 
   // Start with one client.
   controller_->AddClient(route_id, client_a_.get(), 100, session_100);
-  device_->OnError(FROM_HERE,  "Test Error");
+  device_client_->OnError(FROM_HERE, "Test Error");
   EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
@@ -529,16 +578,16 @@
 
   const int arbitrary_frame_feedback_id = 101;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
-      device_->ReserveOutputBuffer(capture_resolution, media::PIXEL_FORMAT_I420,
-                                   media::PIXEL_STORAGE_CPU,
-                                   arbitrary_frame_feedback_id));
+      device_client_->ReserveOutputBuffer(
+          capture_resolution, media::PIXEL_FORMAT_I420,
+          media::PIXEL_STORAGE_CPU, arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
   scoped_refptr<media::VideoFrame> video_frame =
       WrapBuffer(capture_resolution, static_cast<uint8_t*>(buffer->data()));
   ASSERT_TRUE(video_frame);
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
 
   base::RunLoop().RunUntilIdle();
 }
@@ -568,18 +617,18 @@
   const gfx::Size dims(320, 240);
   const int arbitrary_frame_feedback_id = 101;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
-      device_->ReserveOutputBuffer(dims, media::PIXEL_FORMAT_I420,
-                                   media::PIXEL_STORAGE_CPU,
-                                   arbitrary_frame_feedback_id));
+      device_client_->ReserveOutputBuffer(dims, media::PIXEL_FORMAT_I420,
+                                          media::PIXEL_STORAGE_CPU,
+                                          arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
 
   scoped_refptr<media::VideoFrame> video_frame =
       WrapBuffer(dims, static_cast<uint8_t*>(buffer->data()));
   ASSERT_TRUE(video_frame);
-  device_->OnError(FROM_HERE, "Test Error");
+  device_client_->OnError(FROM_HERE, "Test Error");
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
-  device_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
+  device_client_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
 
   EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
   base::RunLoop().RunUntilIdle();
diff --git a/content/browser/renderer_host/media/video_capture_device_client_unittest.cc b/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
index 020854b..e2a6a82 100644
--- a/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
@@ -16,9 +16,12 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/media/video_capture_controller.h"
+#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
+#include "content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/base/limits.h"
-#include "media/capture/video/video_capture_buffer_pool.h"
+#include "media/capture/video/video_capture_buffer_pool_impl.h"
+#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -33,8 +36,7 @@
 
 class MockVideoCaptureController : public VideoCaptureController {
  public:
-  explicit MockVideoCaptureController(int max_buffers)
-      : VideoCaptureController(max_buffers) {}
+  explicit MockVideoCaptureController() : VideoCaptureController() {}
   ~MockVideoCaptureController() override {}
 
   MOCK_METHOD1(MockOnIncomingCapturedVideoFrame, void(const gfx::Size&));
@@ -49,6 +51,11 @@
   }
 };
 
+std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
+    const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) {
+  return base::MakeUnique<content::VideoCaptureGpuJpegDecoder>(decode_done_cb);
+}
+
 // Note that this test does not exercise the class VideoCaptureDeviceClient
 // in isolation. The "unit under test" is an instance of
 // VideoCaptureDeviceClient with some context that is specific to
@@ -57,17 +64,29 @@
 class VideoCaptureDeviceClientTest : public ::testing::Test {
  public:
   VideoCaptureDeviceClientTest()
-      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
-        controller_(new MockVideoCaptureController(1)),
-        device_client_(controller_->NewDeviceClient()) {}
+      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+    scoped_refptr<media::VideoCaptureBufferPoolImpl> buffer_pool(
+        new media::VideoCaptureBufferPoolImpl(
+            base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(),
+            1));
+    controller_ = base::MakeUnique<MockVideoCaptureController>();
+    device_client_ = base::MakeUnique<media::VideoCaptureDeviceClient>(
+        base::MakeUnique<VideoFrameReceiverOnIOThread>(
+            controller_->GetWeakPtrForIOThread()),
+        buffer_pool,
+        base::Bind(
+            &CreateGpuJpegDecoder,
+            base::Bind(&media::VideoFrameReceiver::OnIncomingCapturedVideoFrame,
+                       controller_->GetWeakPtrForIOThread())));
+  }
   ~VideoCaptureDeviceClientTest() override {}
 
   void TearDown() override { base::RunLoop().RunUntilIdle(); }
 
  protected:
   const content::TestBrowserThreadBundle thread_bundle_;
-  const std::unique_ptr<MockVideoCaptureController> controller_;
-  const std::unique_ptr<media::VideoCaptureDevice::Client> device_client_;
+  std::unique_ptr<MockVideoCaptureController> controller_;
+  std::unique_ptr<media::VideoCaptureDeviceClient> device_client_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceClientTest);
diff --git a/content/browser/renderer_host/media/video_capture_host.cc b/content/browser/renderer_host/media/video_capture_host.cc
index bb78fd1..4c458f5 100644
--- a/content/browser/renderer_host/media/video_capture_host.cc
+++ b/content/browser/renderer_host/media/video_capture_host.cc
@@ -212,7 +212,6 @@
 
 void VideoCaptureHost::ReleaseBuffer(int32_t device_id,
                                      int32_t buffer_id,
-                                     const gpu::SyncToken& sync_token,
                                      double consumer_resource_utilization) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -223,7 +222,7 @@
 
   const base::WeakPtr<VideoCaptureController>& controller = it->second;
   if (controller) {
-    controller->ReturnBuffer(controller_id, this, buffer_id, sync_token,
+    controller->ReturnBuffer(controller_id, this, buffer_id,
                              consumer_resource_utilization);
   }
 }
diff --git a/content/browser/renderer_host/media/video_capture_host.h b/content/browser/renderer_host/media/video_capture_host.h
index 71d6ebc3..a92ac9a 100644
--- a/content/browser/renderer_host/media/video_capture_host.h
+++ b/content/browser/renderer_host/media/video_capture_host.h
@@ -64,7 +64,6 @@
   void RequestRefreshFrame(int32_t device_id) override;
   void ReleaseBuffer(int32_t device_id,
                      int32_t buffer_id,
-                     const gpu::SyncToken& sync_token,
                      double consumer_resource_utilization) override;
   void GetDeviceSupportedFormats(
       int32_t device_id,
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index a316e94..dca1d39c 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -27,12 +27,17 @@
 #include "content/browser/media/media_internals.h"
 #include "content/browser/renderer_host/media/video_capture_controller.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
+#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
+#include "content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/media_switches.h"
+#include "media/capture/video/video_capture_buffer_pool_impl.h"
+#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
 #include "media/capture/video/video_capture_device.h"
+#include "media/capture/video/video_capture_device_client.h"
 #include "media/capture/video/video_capture_device_factory.h"
 
 #if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID)
@@ -114,8 +119,11 @@
   }
 }
 
-// The maximum number of buffers in the capture pipeline. See
-// VideoCaptureController ctor comments for more details.
+// The maximum number of video frame buffers in-flight at any one time. This
+// value should be based on the logical capacity of the capture pipeline, and
+// not on hardware performance.  For example, tab capture requires more buffers
+// than webcam capture because the pipeline is longer (it includes read-backs
+// pending in the GPU pipeline).
 const int kMaxNumberOfBuffers = 3;
 // TODO(miu): The value for tab capture should be determined programmatically.
 // http://crbug.com/460318
@@ -144,46 +152,51 @@
 
 const media::VideoCaptureSessionId kFakeSessionId = -1;
 
+std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
+    const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) {
+  return base::MakeUnique<content::VideoCaptureGpuJpegDecoder>(decode_done_cb);
+}
+
 }  // namespace
 
 namespace content {
 
-// Instances of this struct go through 3 different phases during their lifetime.
+// Instances of this struct go through several different phases during their
+// lifetime.
 // Phase 1: When first created (in GetOrCreateDeviceEntry()), this consists of
-// only a controller. Clients can already connect to the controller, but there
-// is no device present.
+// only the |video_capture_controller|. Clients can already connect to the
+// controller, but there is no |buffer_pool| or |video_capture_device| present.
 // Phase 2: When a request to "start" the entry comes in (via
-// HandleQueuedStartRequest()), a VideoCaptureDevice::Client is created
-// via video_capture_controller()->NewDeviceClient() and is used to schedule the
-// creation and start of a VideoCaptureDevice on the Device Thread.
+// HandleQueuedStartRequest()), |buffer_pool| is created and creation of
+// |video_capture_device| is scheduled to run asynchronously on the Device
+// Thread.
 // Phase 3: As soon as the creation of the VideoCaptureDevice is complete, this
 // newly created VideoCaptureDevice instance is connected to the
-// VideoCaptureController via SetConsumerFeedbackObserver().
-class VideoCaptureManager::DeviceEntry {
+// VideoCaptureController via SetConsumerFeedbackObserver(). Furthermore, the
+// |buffer_pool| is connected to the |video_capture_controller| as a
+// FrameBufferPool via SetFrameBufferPool().
+// Phase 4: This phase can only be reached on Android. When the application goes
+// to the background, the |video_capture_device| is asynchronously stopped and
+// released on the Device Thread. The existing |buffer_pool| is kept alive, and
+// all clients of |video_capture_controller| stay connected. When the
+// application is resumed, we transition to Phase 2, except that the existing
+// |buffer_pool| get reused instead of creating a new one.
+struct VideoCaptureManager::DeviceEntry {
  public:
   DeviceEntry(MediaStreamType stream_type,
               const std::string& id,
-              std::unique_ptr<VideoCaptureController> controller,
               const media::VideoCaptureParams& params);
   ~DeviceEntry();
+  std::unique_ptr<media::VideoCaptureDevice::Client> CreateDeviceClient();
+  std::unique_ptr<media::FrameBufferPool> CreateFrameBufferPool();
 
   const int serial_id;
   const MediaStreamType stream_type;
   const std::string id;
   const media::VideoCaptureParams parameters;
-
-  VideoCaptureController* video_capture_controller() const;
-  media::VideoCaptureDevice* video_capture_device() const;
-
-  void SetVideoCaptureDevice(std::unique_ptr<VideoCaptureDevice> device);
-  std::unique_ptr<VideoCaptureDevice> ReleaseVideoCaptureDevice();
-
- private:
-  const std::unique_ptr<VideoCaptureController> video_capture_controller_;
-
-  std::unique_ptr<VideoCaptureDevice> video_capture_device_;
-
-  base::ThreadChecker thread_checker_;
+  VideoCaptureController video_capture_controller;
+  scoped_refptr<media::VideoCaptureBufferPool> buffer_pool;
+  std::unique_ptr<media::VideoCaptureDevice> video_capture_device;
 };
 
 // Bundles a media::VideoCaptureDeviceDescriptor with corresponding supported
@@ -199,6 +212,28 @@
   media::VideoCaptureFormats supported_formats;
 };
 
+class BufferPoolFrameBufferPool : public media::FrameBufferPool {
+ public:
+  explicit BufferPoolFrameBufferPool(
+      scoped_refptr<media::VideoCaptureBufferPool> buffer_pool)
+      : buffer_pool_(std::move(buffer_pool)) {}
+
+  void SetBufferHold(int buffer_id) override {
+    buffer_pool_->HoldForConsumers(buffer_id, 1);
+  }
+
+  void ReleaseBufferHold(int buffer_id) override {
+    buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
+  }
+
+  mojo::ScopedSharedBufferHandle GetHandleForTransit(int buffer_id) override {
+    return buffer_pool_->GetHandleForTransit(buffer_id);
+  }
+
+ private:
+  scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
+};
+
 // Class used for queuing request for starting a device.
 class VideoCaptureManager::CaptureDeviceStartRequest {
  public:
@@ -226,44 +261,48 @@
 VideoCaptureManager::DeviceEntry::DeviceEntry(
     MediaStreamType stream_type,
     const std::string& id,
-    std::unique_ptr<VideoCaptureController> controller,
     const media::VideoCaptureParams& params)
     : serial_id(g_device_start_id++),
       stream_type(stream_type),
       id(id),
-      parameters(params),
-      video_capture_controller_(std::move(controller)) {}
+      parameters(params) {}
 
 VideoCaptureManager::DeviceEntry::~DeviceEntry() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // DCHECK that this DeviceEntry does not still own a
   // media::VideoCaptureDevice. media::VideoCaptureDevice must be deleted on
   // the device thread.
-  DCHECK(video_capture_device_ == nullptr);
+  DCHECK(video_capture_device == nullptr);
 }
 
-void VideoCaptureManager::DeviceEntry::SetVideoCaptureDevice(
-    std::unique_ptr<VideoCaptureDevice> device) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  video_capture_device_.swap(device);
+std::unique_ptr<media::VideoCaptureDevice::Client>
+VideoCaptureManager::DeviceEntry::CreateDeviceClient() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  const int max_buffers = stream_type == MEDIA_TAB_VIDEO_CAPTURE
+                              ? kMaxNumberOfBuffersForTabCapture
+                              : kMaxNumberOfBuffers;
+  if (!buffer_pool) {
+    buffer_pool = new media::VideoCaptureBufferPoolImpl(
+        base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(),
+        max_buffers);
+  }
+
+  return base::MakeUnique<media::VideoCaptureDeviceClient>(
+      base::MakeUnique<VideoFrameReceiverOnIOThread>(
+          video_capture_controller.GetWeakPtrForIOThread()),
+      buffer_pool,
+      base::Bind(
+          &CreateGpuJpegDecoder,
+          base::Bind(&media::VideoFrameReceiver::OnIncomingCapturedVideoFrame,
+                     video_capture_controller.GetWeakPtrForIOThread())));
 }
 
-std::unique_ptr<media::VideoCaptureDevice>
-VideoCaptureManager::DeviceEntry::ReleaseVideoCaptureDevice() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return std::move(video_capture_device_);
-}
-
-VideoCaptureController*
-VideoCaptureManager::DeviceEntry::video_capture_controller() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return video_capture_controller_.get();
-}
-
-media::VideoCaptureDevice*
-VideoCaptureManager::DeviceEntry::video_capture_device() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return video_capture_device_.get();
+std::unique_ptr<media::FrameBufferPool>
+VideoCaptureManager::DeviceEntry::CreateFrameBufferPool() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(buffer_pool);
+  return base::MakeUnique<BufferPoolFrameBufferPool>(buffer_pool);
 }
 
 VideoCaptureManager::DeviceInfo::DeviceInfo() = default;
@@ -385,8 +424,7 @@
   if (existing_device) {
     // Remove any client that is still using the session. This is safe to call
     // even if there are no clients using the session.
-    existing_device->video_capture_controller()
-        ->StopSession(capture_session_id);
+    existing_device->video_capture_controller.StopSession(capture_session_id);
 
     // StopSession() may have removed the last client, so we might need to
     // close the device.
@@ -434,17 +472,18 @@
 
   DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry->id
            << " serial_id = " << entry->serial_id << ".";
-  entry->video_capture_controller()->OnLog(
+  entry->video_capture_controller.OnLog(
       base::StringPrintf("Stopping device: id: %s", entry->id.c_str()));
-  entry->video_capture_controller()->SetConsumerFeedbackObserver(nullptr);
+  entry->video_capture_controller.SetConsumerFeedbackObserver(nullptr);
+  entry->video_capture_controller.SetFrameBufferPool(nullptr);
 
   // |entry->video_capture_device| can be null if creating the device has
   // failed.
-  if (entry->video_capture_device()) {
+  if (entry->video_capture_device) {
     device_task_runner_->PostTask(
         FROM_HERE,
         base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
-                   base::Passed(entry->ReleaseVideoCaptureDevice())));
+                   base::Passed(&entry->video_capture_device)));
   }
 }
 
@@ -466,6 +505,11 @@
   DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = "
            << entry->id << " start id = " << entry->serial_id;
 
+  std::unique_ptr<media::VideoCaptureDevice::Client> device_client =
+      entry->CreateDeviceClient();
+  std::unique_ptr<media::FrameBufferPool> frame_buffer_pool =
+      entry->CreateFrameBufferPool();
+
   base::Callback<std::unique_ptr<VideoCaptureDevice>(void)>
       start_capture_function;
 
@@ -476,16 +520,16 @@
       // held in the browser-side VideoCaptureDevice::Name structure.
       const DeviceInfo* found = GetDeviceInfoById(entry->id);
       if (found) {
-        entry->video_capture_controller()->OnLog(
+        entry->video_capture_controller.OnLog(
             base::StringPrintf("Starting device: id: %s, name: %s, api: %s",
                                found->descriptor.device_id.c_str(),
                                found->descriptor.GetNameAndModel().c_str(),
                                found->descriptor.GetCaptureApiTypeString()));
 
-        start_capture_function = base::Bind(
-            &VideoCaptureManager::DoStartDeviceCaptureOnDeviceThread, this,
-            found->descriptor, request->params(),
-            base::Passed(entry->video_capture_controller()->NewDeviceClient()));
+        start_capture_function =
+            base::Bind(&VideoCaptureManager::DoStartDeviceCaptureOnDeviceThread,
+                       this, found->descriptor, request->params(),
+                       base::Passed(std::move(device_client)));
       } else {
         // Errors from DoStartDeviceCaptureOnDeviceThread go via
         // VideoCaptureDeviceClient::OnError, which needs some thread
@@ -496,8 +540,8 @@
             "Error on %s:%d: device %s unknown. Maybe recently disconnected?",
             __FILE__, __LINE__, entry->id.c_str());
         DLOG(ERROR) << log_message;
-        entry->video_capture_controller()->OnLog(log_message);
-        entry->video_capture_controller()->OnError();
+        entry->video_capture_controller.OnLog(log_message);
+        entry->video_capture_controller.OnError();
         // Drop the failed start request.
         device_start_queue_.pop_front();
 
@@ -508,15 +552,13 @@
     case MEDIA_TAB_VIDEO_CAPTURE:
       start_capture_function = base::Bind(
           &VideoCaptureManager::DoStartTabCaptureOnDeviceThread, this,
-          entry->id, request->params(),
-          base::Passed(entry->video_capture_controller()->NewDeviceClient()));
+          entry->id, request->params(), base::Passed(std::move(device_client)));
       break;
 
     case MEDIA_DESKTOP_VIDEO_CAPTURE:
       start_capture_function = base::Bind(
           &VideoCaptureManager::DoStartDesktopCaptureOnDeviceThread, this,
-          entry->id, request->params(),
-          base::Passed(entry->video_capture_controller()->NewDeviceClient()));
+          entry->id, request->params(), base::Passed(std::move(device_client)));
       break;
 
     default: {
@@ -527,11 +569,12 @@
   base::PostTaskAndReplyWithResult(
       device_task_runner_.get(), FROM_HERE, start_capture_function,
       base::Bind(&VideoCaptureManager::OnDeviceStarted, this,
-                 request->serial_id()));
+                 request->serial_id(), base::Passed(&frame_buffer_pool)));
 }
 
 void VideoCaptureManager::OnDeviceStarted(
     int serial_id,
+    std::unique_ptr<media::FrameBufferPool> frame_buffer_pool,
     std::unique_ptr<VideoCaptureDevice> device) {
   DVLOG(3) << __func__;
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -552,15 +595,19 @@
   } else {
     DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id);
     DCHECK(entry);
-    DCHECK(!entry->video_capture_device());
-    // Passing raw pointer |device.get()| to the controller is safe,
-    // because we transfer ownership of it to |entry|. We are calling
-    // SetConsumerFeedbackObserver(nullptr) before releasing
-    // |entry->video_capture_device_| on the |device_task_runner_|.
-    entry->video_capture_controller()->SetConsumerFeedbackObserver(
-        base::MakeUnique<VideoFrameConsumerFeedbackObserverOnTaskRunner>(
-            device.get(), device_task_runner_));
-    entry->SetVideoCaptureDevice(std::move(device));
+    DCHECK(!entry->video_capture_device);
+    if (device) {
+      entry->video_capture_controller.SetFrameBufferPool(
+          std::move(frame_buffer_pool));
+      // Passing raw pointer |device.get()| to the controller is safe,
+      // because we transfer ownership of it to |entry|. We are calling
+      // SetConsumerFeedbackObserver(nullptr) before releasing
+      // |entry->video_capture_device_| on the |device_task_runner_|.
+      entry->video_capture_controller.SetConsumerFeedbackObserver(
+          base::MakeUnique<VideoFrameConsumerFeedbackObserverOnTaskRunner>(
+              device.get(), device_task_runner_));
+    }
+    entry->video_capture_device = std::move(device);
 
     if (entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
       const media::VideoCaptureSessionId session_id =
@@ -573,8 +620,8 @@
     while (it != photo_request_queue_.end()) {
       auto request = it++;
       DeviceEntry* maybe_entry = GetDeviceEntryBySessionId(request->first);
-      if (maybe_entry && maybe_entry->video_capture_device()) {
-        request->second.Run(maybe_entry->video_capture_device());
+      if (maybe_entry && maybe_entry->video_capture_device) {
+        request->second.Run(maybe_entry->video_capture_device.get());
         photo_request_queue_.erase(request);
       }
     }
@@ -687,21 +734,19 @@
     return;
   }
 
-  DCHECK(entry->video_capture_controller());
-
   LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE);
 
   // First client starts the device.
-  if (!entry->video_capture_controller()->HasActiveClient() &&
-      !entry->video_capture_controller()->HasPausedClient()) {
+  if (!entry->video_capture_controller.HasActiveClient() &&
+      !entry->video_capture_controller.HasPausedClient()) {
     DVLOG(1) << "VideoCaptureManager starting device (type = "
              << entry->stream_type << ", id = " << entry->id << ")";
     QueueStartDevice(session_id, entry, params);
   }
   // Run the callback first, as AddClient() may trigger OnFrameInfo().
-  done_cb.Run(entry->video_capture_controller()->GetWeakPtrForIOThread());
-  entry->video_capture_controller()->AddClient(
-      client_id, client_handler, session_id, params);
+  done_cb.Run(entry->video_capture_controller.GetWeakPtrForIOThread());
+  entry->video_capture_controller.AddClient(client_id, client_handler,
+                                             session_id, params);
 }
 
 void VideoCaptureManager::StopCaptureForClient(
@@ -766,7 +811,7 @@
   controller->PauseClient(client_id, client_handler);
   if (!had_active_client || controller->HasActiveClient())
     return;
-  if (media::VideoCaptureDevice* device = entry->video_capture_device()) {
+  if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) {
     device_task_runner_->PostTask(
         FROM_HERE,
         base::Bind(&VideoCaptureDevice::MaybeSuspend,
@@ -796,7 +841,7 @@
   controller->ResumeClient(client_id, client_handler);
   if (had_active_client || !controller->HasActiveClient())
     return;
-  if (media::VideoCaptureDevice* device = entry->video_capture_device()) {
+  if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) {
     device_task_runner_->PostTask(
         FROM_HERE,
         base::Bind(&VideoCaptureDevice::Resume,
@@ -813,7 +858,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (DeviceEntry* entry = GetDeviceEntryByController(controller)) {
-    if (media::VideoCaptureDevice* device = entry->video_capture_device()) {
+    if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) {
       device_task_runner_->PostTask(
           FROM_HERE,
           base::Bind(&VideoCaptureDevice::RequestRefreshFrame,
@@ -861,7 +906,7 @@
   if (device_in_use) {
     // Currently only one format-in-use is supported at the VCC level.
     formats_in_use->push_back(
-        device_in_use->video_capture_controller()->GetVideoCaptureFormat());
+        device_in_use->video_capture_controller.GetVideoCaptureFormat());
   }
   return true;
 }
@@ -889,7 +934,7 @@
     return;
   }
 
-  if (!existing_device->video_capture_device()) {
+  if (!existing_device->video_capture_device) {
     DVLOG(2) << "Screen capture device not yet started.";
     return;
   }
@@ -911,8 +956,7 @@
   device_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
-                 this,
-                 existing_device->video_capture_device(),
+                 this, existing_device->video_capture_device.get(),
                  window_id_it->second));
 
   notification_window_ids_.erase(window_id_it);
@@ -926,7 +970,7 @@
   const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id);
   if (!entry)
     return;
-  VideoCaptureDevice* device = entry->video_capture_device();
+  VideoCaptureDevice* device = entry->video_capture_device.get();
   if (device) {
     VideoCaptureManager::DoGetPhotoCapabilities(std::move(callback), device);
     return;
@@ -946,7 +990,7 @@
   const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id);
   if (!entry)
     return;
-  VideoCaptureDevice* device = entry->video_capture_device();
+  VideoCaptureDevice* device = entry->video_capture_device.get();
   if (device) {
     VideoCaptureManager::DoSetPhotoOptions(std::move(callback),
                                            std::move(settings), device);
@@ -966,7 +1010,7 @@
   const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id);
   if (!entry)
     return;
-  VideoCaptureDevice* device = entry->video_capture_device();
+  VideoCaptureDevice* device = entry->video_capture_device.get();
   if (device) {
     VideoCaptureManager::DoTakePhoto(std::move(callback), device);
     return;
@@ -1074,8 +1118,8 @@
 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Removal of the last client stops the device.
-  if (!entry->video_capture_controller()->HasActiveClient() &&
-      !entry->video_capture_controller()->HasPausedClient()) {
+  if (!entry->video_capture_controller.HasActiveClient() &&
+      !entry->video_capture_controller.HasPausedClient()) {
     DVLOG(1) << "VideoCaptureManager stopping device (type = "
              << entry->stream_type << ", id = " << entry->id << ")";
 
@@ -1125,7 +1169,7 @@
 
   // Look up |controller| in |devices_|.
   for (const std::unique_ptr<DeviceEntry>& device : devices_) {
-    if (device->video_capture_controller() == controller)
+    if (&device->video_capture_controller == controller)
       return device.get();
   }
   return nullptr;
@@ -1170,15 +1214,9 @@
     return existing_device;
   }
 
-  const int max_buffers = device_info.type == MEDIA_TAB_VIDEO_CAPTURE ?
-      kMaxNumberOfBuffersForTabCapture : kMaxNumberOfBuffers;
-  std::unique_ptr<VideoCaptureController> video_capture_controller(
-      new VideoCaptureController(max_buffers));
-  DeviceEntry* new_device =
-      new DeviceEntry(device_info.type, device_info.id,
-                      std::move(video_capture_controller), params);
-  devices_.emplace_back(new_device);
-  return new_device;
+  devices_.emplace_back(
+      new DeviceEntry(device_info.type, device_info.id, params));
+  return devices_.back().get();
 }
 
 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
@@ -1264,7 +1302,7 @@
     // Do not resume Content Video Capture devices, e.g. Tab or Screen capture.
     // Do not try to restart already running devices.
     if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE ||
-        entry->video_capture_device())
+        entry->video_capture_device)
       continue;
 
     // Check if the device is already in the start queue.
diff --git a/content/browser/renderer_host/media/video_capture_manager.h b/content/browser/renderer_host/media/video_capture_manager.h
index e6857c6..3bad7b1 100644
--- a/content/browser/renderer_host/media/video_capture_manager.h
+++ b/content/browser/renderer_host/media/video_capture_manager.h
@@ -174,7 +174,7 @@
 
  private:
   class CaptureDeviceStartRequest;
-  class DeviceEntry;
+  struct DeviceEntry;
   struct DeviceInfo;
 
   using SessionMap = std::map<media::VideoCaptureSessionId, MediaStreamDevice>;
@@ -239,8 +239,10 @@
   void QueueStartDevice(media::VideoCaptureSessionId session_id,
                         DeviceEntry* entry,
                         const media::VideoCaptureParams& params);
-  void OnDeviceStarted(int serial_id,
-                       std::unique_ptr<VideoCaptureDevice> device);
+  void OnDeviceStarted(
+      int serial_id,
+      std::unique_ptr<media::FrameBufferPool> frame_buffer_pool,
+      std::unique_ptr<VideoCaptureDevice> device);
   void DoStopDevice(DeviceEntry* entry);
   void HandleQueuedStartRequest();
 
diff --git a/content/browser/renderer_host/media/video_frame_receiver_on_io_thread.cc b/content/browser/renderer_host/media/video_frame_receiver_on_io_thread.cc
new file mode 100644
index 0000000..a4a46c2e
--- /dev/null
+++ b/content/browser/renderer_host/media/video_frame_receiver_on_io_thread.cc
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h"
+
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+VideoFrameReceiverOnIOThread::VideoFrameReceiverOnIOThread(
+    const base::WeakPtr<VideoFrameReceiver>& receiver)
+    : receiver_(receiver) {}
+
+VideoFrameReceiverOnIOThread::~VideoFrameReceiverOnIOThread() = default;
+
+void VideoFrameReceiverOnIOThread::OnIncomingCapturedVideoFrame(
+    std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
+    scoped_refptr<media::VideoFrame> frame) {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(&VideoFrameReceiver::OnIncomingCapturedVideoFrame, receiver_,
+                 base::Passed(&buffer), frame));
+}
+
+void VideoFrameReceiverOnIOThread::OnError() {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(&VideoFrameReceiver::OnError, receiver_));
+}
+
+void VideoFrameReceiverOnIOThread::OnLog(const std::string& message) {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(&VideoFrameReceiver::OnLog, receiver_, message));
+}
+
+void VideoFrameReceiverOnIOThread::OnBufferDestroyed(int buffer_id_to_drop) {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(&VideoFrameReceiver::OnBufferDestroyed, receiver_,
+                 buffer_id_to_drop));
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h b/content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h
new file mode 100644
index 0000000..9925dd1
--- /dev/null
+++ b/content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_FRAME_RECEIVER_ON_IO_THREAD_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_FRAME_RECEIVER_ON_IO_THREAD_H_
+
+#include "content/common/content_export.h"
+#include "media/capture/video/video_frame_receiver.h"
+
+namespace content {
+
+// Decorator for media::VideoFrameReceiver that forwards all incoming calls
+// to the Browser IO thread.
+// TODO(chfremer): Change this to VideoFrameReceiverOnTaskRunner and have the
+// target task runner be passed into the constructor. See crbug.com/674190.
+class CONTENT_EXPORT VideoFrameReceiverOnIOThread
+    : public media::VideoFrameReceiver {
+ public:
+  explicit VideoFrameReceiverOnIOThread(
+      const base::WeakPtr<VideoFrameReceiver>& receiver);
+  ~VideoFrameReceiverOnIOThread() override;
+
+  void OnIncomingCapturedVideoFrame(
+      std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
+      scoped_refptr<media::VideoFrame> frame) override;
+  void OnError() override;
+  void OnLog(const std::string& message) override;
+  void OnBufferDestroyed(int buffer_id_to_drop) override;
+
+ private:
+  base::WeakPtr<VideoFrameReceiver> receiver_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_FRAME_RECEIVER_ON_IO_THREAD_H_
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 606370d..d2740e3b 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -99,4 +99,8 @@
   return false;
 }
 
+WebContents* RenderWidgetHostDelegate::GetAsWebContents() {
+  return nullptr;
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 68346580..8030e15 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -40,6 +40,7 @@
 class RenderWidgetHostInputEventRouter;
 class RenderViewHostDelegateView;
 class TextInputManager;
+class WebContents;
 struct ScreenInfo;
 struct NativeWebKeyboardEvent;
 
@@ -249,6 +250,10 @@
   // element accepts text input.
   virtual void FocusedNodeTouched(bool editable) {}
 
+  // Return this object cast to a WebContents, if it is one. If the object is
+  // not a WebContents, returns nullptr.
+  virtual WebContents* GetAsWebContents();
+
  protected:
   virtual ~RenderWidgetHostDelegate() {}
 };
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 37e88877..a1542f0 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1923,12 +1923,16 @@
     bool user_gesture,
     bool last_unlocked_by_target,
     bool privileged) {
-  if (mouse_lock_widget_) {
-    render_widget_host->GotResponseToLockMouseRequest(false);
-    return;
+  for (WebContentsImpl* current = this; current;
+       current = current->GetOuterWebContents()) {
+    if (current->mouse_lock_widget_) {
+      render_widget_host->GotResponseToLockMouseRequest(false);
+      return;
+    }
   }
 
   if (privileged) {
+    DCHECK(!GetOuterWebContents());
     mouse_lock_widget_ = render_widget_host;
     render_widget_host->GotResponseToLockMouseRequest(true);
     return;
@@ -1944,7 +1948,11 @@
   }
 
   if (widget_in_frame_tree && delegate_) {
-    mouse_lock_widget_ = render_widget_host;
+    for (WebContentsImpl* current = this; current;
+         current = current->GetOuterWebContents()) {
+      current->mouse_lock_widget_ = render_widget_host;
+    }
+
     delegate_->RequestToLockMouse(this, user_gesture, last_unlocked_by_target);
   } else {
     render_widget_host->GotResponseToLockMouseRequest(false);
@@ -1953,8 +1961,15 @@
 
 void WebContentsImpl::LostMouseLock(RenderWidgetHostImpl* render_widget_host) {
   CHECK(mouse_lock_widget_);
+
+  if (mouse_lock_widget_->delegate()->GetAsWebContents() != this)
+    return mouse_lock_widget_->delegate()->LostMouseLock(render_widget_host);
+
   mouse_lock_widget_->SendMouseLockLost();
-  mouse_lock_widget_ = nullptr;
+  for (WebContentsImpl* current = this; current;
+       current = current->GetOuterWebContents()) {
+    current->mouse_lock_widget_ = nullptr;
+  }
 
   if (delegate_)
     delegate_->LostMouseLock();
@@ -3070,14 +3085,26 @@
 }
 
 bool WebContentsImpl::GotResponseToLockMouseRequest(bool allowed) {
-  if (GetBrowserPluginGuest())
+  if (!GuestMode::IsCrossProcessFrameGuest(GetWebContents()) &&
+      GetBrowserPluginGuest())
     return GetBrowserPluginGuest()->LockMouse(allowed);
 
-  if (mouse_lock_widget_ &&
-      mouse_lock_widget_->GotResponseToLockMouseRequest(allowed))
-    return true;
+  if (mouse_lock_widget_) {
+    if (mouse_lock_widget_->delegate()->GetAsWebContents() != this) {
+      return mouse_lock_widget_->delegate()
+          ->GetAsWebContents()
+          ->GotResponseToLockMouseRequest(allowed);
+    }
 
-  mouse_lock_widget_ = nullptr;
+    if (mouse_lock_widget_->GotResponseToLockMouseRequest(allowed))
+      return true;
+  }
+
+  for (WebContentsImpl* current = this; current;
+       current = current->GetOuterWebContents()) {
+    current->mouse_lock_widget_ = nullptr;
+  }
+
   return false;
 }
 
diff --git a/content/browser/webui/i18n_source_stream.cc b/content/browser/webui/i18n_source_stream.cc
index 1ff237e2..c2120a8 100644
--- a/content/browser/webui/i18n_source_stream.cc
+++ b/content/browser/webui/i18n_source_stream.cc
@@ -29,7 +29,6 @@
                                    SourceStream::SourceType type,
                                    const ui::TemplateReplacements* replacements)
     : FilterSourceStream(type, std::move(upstream)),
-      drain_offset_(0),
       replacements_(replacements) {}
 
 std::string I18nSourceStream::GetTypeAsString() const {
@@ -42,24 +41,30 @@
                                  int input_buffer_size,
                                  int* consumed_bytes,
                                  bool upstream_end_reached) {
-  DCHECK(output_.empty() || (upstream_end_reached && input_buffer_size == 0));
-  *consumed_bytes = input_buffer_size;
-  // TODO(dschuyler): Perform replacements without accumulation.
-  // Accumulate.
+  // |input_| is often empty (or it may have something from the prior call).
   input_.append(input_buffer->data(), input_buffer_size);
-  if (upstream_end_reached && !drain_offset_ && output_.empty()) {
-    // Process.
-    output_ = ui::ReplaceTemplateExpressions(input_, *replacements_);
+  *consumed_bytes = input_buffer_size;
+
+  // The replacement tag starts with '$' and ends with '}'. The white-space
+  // characters are an optimization that looks for characters that are invalid
+  // within $i18n{} tags.
+  size_t pos = input_.find_last_of("$} \t\r\n");
+  std::string to_process;
+  if (!upstream_end_reached && pos != std::string::npos && input_[pos] == '$') {
+    // If there is a trailing '$' then split the |input_| at that point. Process
+    // the first part; save the second part for the next call to FilterData().
+    to_process.assign(input_, 0, pos);
+    input_.erase(0, pos);
+  } else {
+    // There is no risk of a split key, process the whole input.
+    to_process.swap(input_);
   }
 
-  if (drain_offset_ == output_.size())
-    return 0;
-
-  // Drain.
-  int bytes_out = std::min(output_.size() - drain_offset_,
-                           static_cast<size_t>(output_buffer_size));
-  output_.copy(output_buffer->data(), bytes_out, drain_offset_);
-  drain_offset_ += bytes_out;
+  output_.append(ui::ReplaceTemplateExpressions(to_process, *replacements_));
+  int bytes_out =
+      std::min(output_.size(), static_cast<size_t>(output_buffer_size));
+  output_.copy(output_buffer->data(), bytes_out);
+  output_.erase(0, bytes_out);
   return bytes_out;
 }
 
diff --git a/content/browser/webui/i18n_source_stream.h b/content/browser/webui/i18n_source_stream.h
index 0f37d87..64378c8 100644
--- a/content/browser/webui/i18n_source_stream.h
+++ b/content/browser/webui/i18n_source_stream.h
@@ -40,15 +40,16 @@
                  int* consumed_bytes,
                  bool upstream_end_reached) override;
 
-  // Accumulated from upstream.
+  // Keep split $i18n tags (wait for the whole tag). This is expected to vary
+  // in size from 0 to a few KB and should never be larger than the input file
+  // (in the worst case).
   std::string input_;
 
-  // To send downstream.
+  // Keep excess that didn't fit in the output buffer. This is expected to vary
+  // in size from 0 to a few KB and should never get much larger than the input
+  // file (in the worst case).
   std::string output_;
 
-  // How much of the |output_| has been sent.
-  size_t drain_offset_;
-
   // A map of i18n replacement keys and translations.
   const ui::TemplateReplacements* replacements_;  // weak
 
diff --git a/content/browser/webui/i18n_source_stream_unittest.cc b/content/browser/webui/i18n_source_stream_unittest.cc
index 76ebf69..d031240 100644
--- a/content/browser/webui/i18n_source_stream_unittest.cc
+++ b/content/browser/webui/i18n_source_stream_unittest.cc
@@ -178,19 +178,18 @@
   const char kText[] = "This text has no i18n replacements.";
   size_t kTextLength = strlen(kText);
   source()->AddReadResult(kText, kTextLength, net::OK, GetParam().mode);
-  source()->AddReadResult(kText + kTextLength, 0, net::OK, GetParam().mode);
+  source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode);
   std::string actual_output;
   int rv = ReadStream(&actual_output);
   EXPECT_EQ(static_cast<int>(kTextLength), rv);
-  EXPECT_EQ(std::string(kText, kTextLength), actual_output);
+  EXPECT_EQ(kText, actual_output);
   EXPECT_EQ("i18n", stream()->Description());
 }
 
 TEST_P(I18nSourceStreamTest, I18nOneRead) {
   Init();
   source()->AddReadResult(source_data(), kSourceSize, net::OK, GetParam().mode);
-  source()->AddReadResult(source_data() + kSourceSize, 0, net::OK,
-                          GetParam().mode);
+  source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode);
   std::string actual_output;
   int rv = ReadStream(&actual_output);
   EXPECT_EQ(static_cast<int>(kResultSize), rv);
@@ -209,8 +208,7 @@
   }
   source()->AddReadResult(source_data() + written, kSourceSize - written,
                           net::OK, GetParam().mode);
-  source()->AddReadResult(source_data() + kSourceSize, 0, net::OK,
-                          GetParam().mode);
+  source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode);
   std::string actual_output;
   int rv = ReadStream(&actual_output);
   EXPECT_EQ(static_cast<int>(kResultSize), rv);
@@ -218,4 +216,17 @@
   EXPECT_EQ("i18n", stream()->Description());
 }
 
+TEST_P(I18nSourceStreamTest, I18nTagAtEndOfLine) {
+  Init();
+  const char kSourceData[] = "test with tag at end of line $";
+  const size_t source_size = strlen(kSourceData);
+  source()->AddReadResult(kSourceData, source_size, net::OK, GetParam().mode);
+  source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode);
+  std::string actual_output;
+  int rv = ReadStream(&actual_output);
+  EXPECT_EQ(static_cast<int>(source_size), rv);
+  EXPECT_EQ(kSourceData, actual_output);
+  EXPECT_EQ("i18n", stream()->Description());
+}
+
 }  // namespace content
diff --git a/content/common/video_capture.mojom b/content/common/video_capture.mojom
index 358fb4ad98..bfa1ee2 100644
--- a/content/common/video_capture.mojom
+++ b/content/common/video_capture.mojom
@@ -4,7 +4,6 @@
 
 module content.mojom;
 
-import "gpu/ipc/common/sync_token.mojom";
 import "media/mojo/interfaces/media_types.mojom";
 import "media/capture/mojo/video_capture_types.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
@@ -98,7 +97,6 @@
 
   // Indicates that a renderer has finished using a previously shared buffer.
   ReleaseBuffer(int32 device_id, int32 buffer_id,
-                gpu.mojom.SyncToken sync_token,
                 double consumer_resource_utilization);
 
   // Get the formats supported by a device referenced by |session_id|.
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
index 994b458a..6d63ded 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
@@ -37,9 +37,6 @@
 public class BrowserAccessibilityManager {
     private static final String TAG = "BrowserAccessibilityManager";
 
-    private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500;
-    private static final int ACCESSIBILITY_FOCUS_LOCATION_CHANGED_DELAY_MS = 100;
-
     // Constants from AccessibilityNodeInfo defined in the K SDK.
     private static final int ACTION_COLLAPSE = 0x00080000;
     private static final int ACTION_EXPAND = 0x00040000;
@@ -48,6 +45,7 @@
     private static final int ACTION_SET_TEXT = 0x200000;
     private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500;
 
     // Constants from AccessibilityNodeInfo defined in the M SDK.
     // Source: https://developer.android.com/reference/android/R.id.html
@@ -77,7 +75,6 @@
     private int mSelectionEndIndex;
     protected int mAccessibilityFocusId;
     private Runnable mSendWindowContentChangedRunnable;
-    private Runnable mAccessibilityFocusLocationChangedRunnable;
 
     /**
      * Create a BrowserAccessibilityManager object, which is owned by the C++
@@ -240,12 +237,6 @@
                 if (mAccessibilityFocusId == virtualViewId) {
                     mAccessibilityFocusId = View.NO_ID;
                     mAccessibilityFocusRect = null;
-                    // If we had a pending callback to update the location of the previous object
-                    // with accessibility focus, remove it.
-                    if (mAccessibilityFocusLocationChangedRunnable != null) {
-                        mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable);
-                        mAccessibilityFocusLocationChangedRunnable = null;
-                    }
                 }
                 return true;
             case AccessibilityNodeInfo.ACTION_CLICK:
@@ -546,20 +537,6 @@
         mSelectionStartIndex = 0;
         mSelectionEndIndex = 0;
 
-        // If we had a pending callback to update the location of the previous object with
-        // accessibility focus, remove it.
-        if (mAccessibilityFocusLocationChangedRunnable != null) {
-            mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable);
-            mAccessibilityFocusLocationChangedRunnable = null;
-        }
-
-        // Call nativeSetAccessibilityFocus. For the most part Chrome doesn't have a
-        // concept of accessibility focus, but we do two things: (1) auto-focus certain
-        // roles like links when they get accessibility focus and (2) load inline text boxes
-        // for nodes when they get accessibility focus since inline text boxes are expensive
-        // to load and on Android they're only needed for nodes that have input focus or
-        // accessibility focus.
-        //
         // Calling nativeSetAccessibilityFocus will asynchronously load inline text boxes for
         // this node and its subtree. If accessibility focus is on anything other than
         // the root, do it - otherwise set it to -1 so we don't load inline text boxes
@@ -589,47 +566,6 @@
     }
 
     /**
-     * Work around a bug in the Android framework where if the object with accessibility
-     *  focus moves, the accessibility focus rect is not updated - both the visual highlight,
-     * and the location on the screen that's clicked if you double-tap. To work around this,
-     * when we know the object with accessibility focus moved, move focus away and then
-     * move focus right back to it, which tricks Android into updating its bounds.
-     *
-     * Do this after a short delay because sometimes the change to the object with accessibility
-     * focus happens just before navigating somewhere else.
-     */
-    private void updateAccessibilityFocusLocationAfterDelay() {
-        if (mNativeObj == 0) return;
-
-        if (mAccessibilityFocusLocationChangedRunnable != null) return;
-
-        mAccessibilityFocusLocationChangedRunnable = new Runnable() {
-            @Override
-            public void run() {
-                updateAccessibilityFocusLocation();
-            }
-        };
-
-        mView.postDelayed(mAccessibilityFocusLocationChangedRunnable,
-                ACCESSIBILITY_FOCUS_LOCATION_CHANGED_DELAY_MS);
-    }
-
-    /**
-     * See updateAccessibilityFocusLocationAfterDelay for details.
-     */
-    private void updateAccessibilityFocusLocation() {
-        // This can be called from a timeout, so we need to make sure we're still valid.
-        if (mNativeObj == 0 || mContentViewCore == null || mView == null) return;
-
-        if (mAccessibilityFocusLocationChangedRunnable != null) {
-            mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable);
-            mAccessibilityFocusLocationChangedRunnable = null;
-        }
-
-        moveAccessibilityFocusToIdAndRefocusIfNeeded(mAccessibilityFocusId);
-    }
-
-    /**
      * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throttle such
      * events from firing too quickly during animations, for example.
      */
@@ -1056,14 +992,17 @@
 
         node.setBoundsInScreen(rect);
 
-        // If this is the node with accessibility focus, ensure that its location on-screen
-        // is up-to-date.
+        // Work around a bug in the Android framework where if the object with accessibility
+        // focus moves, the accessibility focus rect is not updated - both the visual highlight,
+        // and the location on the screen that's clicked if you double-tap. To work around this,
+        // when we know the object with accessibility focus moved, move focus away and then
+        // move focus right back to it, which tricks Android into updating its bounds.
         if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentRootId) {
             if (mAccessibilityFocusRect == null) {
                 mAccessibilityFocusRect = rect;
             } else if (!mAccessibilityFocusRect.equals(rect)) {
                 mAccessibilityFocusRect = rect;
-                updateAccessibilityFocusLocationAfterDelay();
+                moveAccessibilityFocusToIdAndRefocusIfNeeded(virtualViewId);
             }
         }
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/FloatingPastePopupMenu.java b/content/public/android/java/src/org/chromium/content/browser/input/FloatingPastePopupMenu.java
index c5310db4..2864cf76 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/FloatingPastePopupMenu.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/FloatingPastePopupMenu.java
@@ -43,10 +43,6 @@
     private int mRawPositionX;
     private int mRawPositionY;
 
-    // Embedders may not support floating ActionModes, in which case we should
-    // use the legacy menu as a fallback.
-    private LegacyPastePopupMenu mFallbackPastePopupMenu;
-
     public FloatingPastePopupMenu(Context context, View parent, PastePopupMenuDelegate delegate) {
         assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
 
@@ -63,11 +59,6 @@
 
     @Override
     public void show(int x, int y) {
-        if (mFallbackPastePopupMenu != null) {
-            mFallbackPastePopupMenu.show(x, y);
-            return;
-        }
-
         if (isShowing()) {
             int dx = mRawPositionX - x;
             int dy = mRawPositionY - y;
@@ -81,16 +72,11 @@
             return;
         }
 
-        ensureActionModeOrFallback();
+        ensureActionMode();
     }
 
     @Override
     public void hide() {
-        if (mFallbackPastePopupMenu != null) {
-            mFallbackPastePopupMenu.hide();
-            return;
-        }
-
         if (mActionMode != null) {
             mActionMode.finish();
             mActionMode = null;
@@ -99,13 +85,11 @@
 
     @Override
     public boolean isShowing() {
-        if (mFallbackPastePopupMenu != null) return mFallbackPastePopupMenu.isShowing();
         return mActionMode != null;
     }
 
-    private void ensureActionModeOrFallback() {
+    private void ensureActionMode() {
         if (mActionMode != null) return;
-        if (mFallbackPastePopupMenu != null) return;
 
         ActionMode actionMode = mParent.startActionMode(
                 new ActionModeCallback(), ActionMode.TYPE_FLOATING);
@@ -115,9 +99,6 @@
 
             assert actionMode.getType() == ActionMode.TYPE_FLOATING;
             mActionMode = actionMode;
-        } else {
-            mFallbackPastePopupMenu = new LegacyPastePopupMenu(mContext, mParent, mDelegate);
-            mFallbackPastePopupMenu.show(mRawPositionX, mRawPositionY);
         }
     }
 
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index b46d35a..089fcbc 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -269,8 +269,7 @@
                 << ", storage:" << info->storage_type;
   }
   if (!consume_buffer) {
-    GetVideoCaptureHost()->ReleaseBuffer(device_id_, buffer_id,
-                                         gpu::SyncToken(), -1.0);
+    GetVideoCaptureHost()->ReleaseBuffer(device_id_, buffer_id, -1.0);
     return;
   }
 
@@ -310,18 +309,16 @@
           buffer->buffer_size(), buffer->buffer()->handle(),
           0 /* shared_memory_offset */, info->timestamp);
   if (!frame) {
-    GetVideoCaptureHost()->ReleaseBuffer(device_id_, buffer_id,
-                                         gpu::SyncToken(), -1.0);
+    GetVideoCaptureHost()->ReleaseBuffer(device_id_, buffer_id, -1.0);
     return;
   }
 
   BufferFinishedCallback buffer_finished_callback = media::BindToCurrentLoop(
       base::Bind(&VideoCaptureImpl::OnClientBufferFinished,
                  weak_factory_.GetWeakPtr(), buffer_id, buffer));
-  std::unique_ptr<gpu::SyncToken> release_sync_token(new gpu::SyncToken);
   frame->AddDestructionObserver(
       base::Bind(&VideoCaptureImpl::DidFinishConsumingFrame, frame->metadata(),
-                 base::Passed(&release_sync_token), buffer_finished_callback));
+                 buffer_finished_callback));
 
   frame->metadata()->MergeInternalValuesFrom(*info->metadata);
 
@@ -345,11 +342,10 @@
 void VideoCaptureImpl::OnClientBufferFinished(
     int buffer_id,
     const scoped_refptr<ClientBuffer>& /* ignored_buffer */,
-    const gpu::SyncToken& release_sync_token,
     double consumer_resource_utilization) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   GetVideoCaptureHost()->ReleaseBuffer(
-      device_id_, buffer_id, release_sync_token, consumer_resource_utilization);
+      device_id_, buffer_id, consumer_resource_utilization);
 }
 
 void VideoCaptureImpl::StopDevice() {
@@ -428,7 +424,6 @@
 // static
 void VideoCaptureImpl::DidFinishConsumingFrame(
     const media::VideoFrameMetadata* metadata,
-    std::unique_ptr<gpu::SyncToken> release_sync_token,
     const BufferFinishedCallback& callback_to_io_thread) {
   // Note: This function may be called on any thread by the VideoFrame
   // destructor.  |metadata| is still valid for read-access at this point.
@@ -437,8 +432,7 @@
                            &consumer_resource_utilization)) {
     consumer_resource_utilization = -1.0;
   }
-
-  callback_to_io_thread.Run(*release_sync_token, consumer_resource_utilization);
+  callback_to_io_thread.Run(consumer_resource_utilization);
 }
 
 }  // namespace content
diff --git a/content/renderer/media/video_capture_impl.h b/content/renderer/media/video_capture_impl.h
index f875e71..b39b999 100644
--- a/content/renderer/media/video_capture_impl.h
+++ b/content/renderer/media/video_capture_impl.h
@@ -80,8 +80,7 @@
   using ClientInfoMap = std::map<int, ClientInfo>;
 
   using BufferFinishedCallback =
-      base::Callback<void(const gpu::SyncToken& sync_token,
-                          double consumer_resource_utilization)>;
+      base::Callback<void(double consumer_resource_utilization)>;
 
   // mojom::VideoCaptureObserver implementation.
   void OnStateChanged(mojom::VideoCaptureState state) override;
@@ -95,7 +94,6 @@
   // buffer.
   void OnClientBufferFinished(int buffer_id,
                               const scoped_refptr<ClientBuffer>& buffer,
-                              const gpu::SyncToken& release_sync_token,
                               double consumer_resource_utilization);
 
   void StopDevice();
@@ -120,7 +118,6 @@
   // callback, to trampoline back to the IO thread with the values.
   static void DidFinishConsumingFrame(
       const media::VideoFrameMetadata* metadata,
-      std::unique_ptr<gpu::SyncToken> release_sync_token,
       const BufferFinishedCallback& callback_to_io_thread);
 
   // |device_id_| and |session_id_| are different concepts, but we reuse the
diff --git a/content/renderer/media/video_capture_impl_manager_unittest.cc b/content/renderer/media/video_capture_impl_manager_unittest.cc
index d1af016..a68a26b 100644
--- a/content/renderer/media/video_capture_impl_manager_unittest.cc
+++ b/content/renderer/media/video_capture_impl_manager_unittest.cc
@@ -76,8 +76,8 @@
   }
 
   MOCK_METHOD1(RequestRefreshFrame, void(int32_t));
-  MOCK_METHOD4(ReleaseBuffer,
-               void(int32_t, int32_t, const gpu::SyncToken&, double));
+  MOCK_METHOD3(ReleaseBuffer,
+               void(int32_t, int32_t, double));
   MOCK_METHOD3(GetDeviceSupportedFormats,
                void(int32_t,
                     int32_t,
diff --git a/content/renderer/media/video_capture_impl_unittest.cc b/content/renderer/media/video_capture_impl_unittest.cc
index 9fee656c..f130682 100644
--- a/content/renderer/media/video_capture_impl_unittest.cc
+++ b/content/renderer/media/video_capture_impl_unittest.cc
@@ -37,7 +37,7 @@
         .WillByDefault(WithArgs<2>(Invoke(RunEmptyFormatsCallback)));
     ON_CALL(*this, GetDeviceFormatsInUse(_, _, _))
         .WillByDefault(WithArgs<2>(Invoke(RunEmptyFormatsCallback)));
-    ON_CALL(*this, ReleaseBuffer(_, _, _, _))
+    ON_CALL(*this, ReleaseBuffer(_, _, _))
         .WillByDefault(InvokeWithoutArgs(
             this, &MockMojoVideoCaptureHost::increase_released_buffer_count));
   }
@@ -56,8 +56,7 @@
   MOCK_METHOD3(Resume,
                void(int32_t, int32_t, const media::VideoCaptureParams&));
   MOCK_METHOD1(RequestRefreshFrame, void(int32_t));
-  MOCK_METHOD4(ReleaseBuffer,
-               void(int32_t, int32_t, const gpu::SyncToken&, double));
+  MOCK_METHOD3(ReleaseBuffer, void(int32_t, int32_t, double));
   MOCK_METHOD3(GetDeviceSupportedFormats,
                void(int32_t,
                     int32_t,
@@ -265,7 +264,7 @@
   EXPECT_CALL(*this, OnFrameReady(_, _));
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
-  EXPECT_CALL(mock_video_capture_host_, ReleaseBuffer(_, kBufferId, _, _))
+  EXPECT_CALL(mock_video_capture_host_, ReleaseBuffer(_, kBufferId, _))
       .Times(0);
 
   StartCapture(0, params_small_);
@@ -290,7 +289,7 @@
   EXPECT_CALL(*this, OnFrameReady(_, _)).Times(0);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_large_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
-  EXPECT_CALL(mock_video_capture_host_, ReleaseBuffer(_, kBufferId, _, _));
+  EXPECT_CALL(mock_video_capture_host_, ReleaseBuffer(_, kBufferId, _));
 
   StartCapture(0, params_large_);
   SimulateOnBufferCreated(kBufferId, shm);
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 4df0363..c90e9c3 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -55,6 +55,7 @@
     # Windows only.
     self.Fail('conformance2/rendering/blitframebuffer-outside-readbuffer.html',
         ['win'], bug=644740)
+    self.Flaky('deqp/functional/gles3/sync.html', ['win'], bug=676848)
 
     # Win / NVidia
     self.Flaky('deqp/functional/gles3/fbomultisample*',
diff --git a/crypto/capi_util.cc b/crypto/capi_util.cc
index 7adfeb8..1e199db 100644
--- a/crypto/capi_util.cc
+++ b/crypto/capi_util.cc
@@ -5,49 +5,10 @@
 #include "crypto/capi_util.h"
 
 #include <stddef.h>
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "base/synchronization/lock.h"
-
-namespace {
-
-class CAPIUtilSingleton {
- public:
-  static CAPIUtilSingleton* GetInstance() {
-    return base::Singleton<CAPIUtilSingleton>::get();
-  }
-
-  // Returns a lock to guard calls to CryptAcquireContext with
-  // CRYPT_DELETEKEYSET or CRYPT_NEWKEYSET.
-  base::Lock& acquire_context_lock() {
-    return acquire_context_lock_;
-  }
-
- private:
-  friend class base::Singleton<CAPIUtilSingleton>;
-  friend struct base::DefaultSingletonTraits<CAPIUtilSingleton>;
-
-  CAPIUtilSingleton() {}
-
-  base::Lock acquire_context_lock_;
-
-  DISALLOW_COPY_AND_ASSIGN(CAPIUtilSingleton);
-};
-
-}  // namespace
+#include <stdlib.h>
 
 namespace crypto {
 
-BOOL CryptAcquireContextLocked(HCRYPTPROV* prov,
-                               LPCWSTR container,
-                               LPCWSTR provider,
-                               DWORD prov_type,
-                               DWORD flags) {
-  base::AutoLock lock(CAPIUtilSingleton::GetInstance()->acquire_context_lock());
-  return CryptAcquireContext(prov, container, provider, prov_type, flags);
-}
-
 void* WINAPI CryptAlloc(size_t size) {
   return malloc(size);
 }
diff --git a/crypto/capi_util.h b/crypto/capi_util.h
index 6941033..11e2b7b 100644
--- a/crypto/capi_util.h
+++ b/crypto/capi_util.h
@@ -9,26 +9,9 @@
 #include <stddef.h>
 
 #include "crypto/crypto_export.h"
-#include "crypto/wincrypt_shim.h"
 
 namespace crypto {
 
-// CryptAcquireContext when passed CRYPT_NEWKEYSET or CRYPT_DELETEKEYSET in
-// flags is not thread-safe. For such calls, we create a global lock to
-// synchronize it.
-//
-// From "Threading Issues with Cryptographic Service Providers",
-// <http://msdn.microsoft.com/en-us/library/aa388149(v=VS.85).aspx>:
-//
-// "The CryptAcquireContext function is generally thread safe unless
-// CRYPT_NEWKEYSET or CRYPT_DELETEKEYSET is specified in the dwFlags
-// parameter."
-CRYPTO_EXPORT BOOL CryptAcquireContextLocked(HCRYPTPROV* prov,
-                                             LPCWSTR container,
-                                             LPCWSTR provider,
-                                             DWORD prov_type,
-                                             DWORD flags);
-
 // Wrappers of malloc and free for CryptoAPI routines that need memory
 // allocators, such as in CRYPT_DECODE_PARA. Such routines require WINAPI
 // calling conventions.
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 4276b3c9..47f4a6ee 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -1375,6 +1375,16 @@
   return is_guest_fullscreen_;
 }
 
+void WebViewGuest::RequestToLockMouse(WebContents* web_contents,
+                                      bool user_gesture,
+                                      bool last_unlocked_by_target) {
+  RequestPointerLockPermission(
+      user_gesture, last_unlocked_by_target,
+      base::Bind(
+          base::IgnoreResult(&WebContents::GotResponseToLockMouseRequest),
+          base::Unretained(web_contents)));
+}
+
 void WebViewGuest::LoadURLWithParams(
     const GURL& url,
     const content::Referrer& referrer,
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index 79db5bf2..2a3befd5 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -253,6 +253,9 @@
   void ExitFullscreenModeForTab(content::WebContents* web_contents) final;
   bool IsFullscreenForTabOrPending(
       const content::WebContents* web_contents) const final;
+  void RequestToLockMouse(content::WebContents* web_contents,
+                          bool user_gesture,
+                          bool last_unlocked_by_target) override;
 
   // WebContentsObserver implementation.
   void DidStartNavigation(content::NavigationHandle* navigation_handle) final;
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index 56d598fc..6b93df46 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -200,9 +200,9 @@
     {
       "id": 31,
       "cr_bugs": [154715, 10068, 269829, 294779, 285292],
-      "description": "The Mali-xxx driver does not guarantee flush ordering",
+      "description": "The Mali-Txxx driver does not guarantee flush ordering",
       "gl_vendor": "ARM.*",
-      "gl_renderer": "Mali.*",
+      "gl_renderer": "Mali-T.*",
       "features": [
         "use_virtualized_gl_contexts"
       ]
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index e565127..352e988 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -400,6 +400,8 @@
 - (void)showSyncSettings;
 // Shows the Save Passwords settings.
 - (void)showSavePasswordsSettings;
+// Shows the Physical Web settings UI.
+- (void)showPhysicalWebSettings;
 // Invokes the sign in flow with the specified authentication operation and
 // invokes |callback| when finished.
 - (void)showSignInWithOperation:(AuthenticationOperation)operation
@@ -1525,6 +1527,9 @@
     case IDC_SHOW_SAVE_PASSWORDS_SETTINGS:
       [self showSavePasswordsSettings];
       break;
+    case IDC_SHOW_PHYSICAL_WEB_SETTINGS:
+      [self showPhysicalWebSettings];
+      break;
     case IDC_SHOW_HISTORY:
       [self showHistory];
       break;
@@ -2111,6 +2116,18 @@
                  completion:nil];
 }
 
+- (void)showPhysicalWebSettings {
+  if (_settingsNavigationController)
+    return;
+  _settingsNavigationController.reset([SettingsNavigationController
+      newPhysicalWebController:_mainBrowserState
+                      delegate:self]);
+  [[self topPresentedViewController]
+      presentViewController:_settingsNavigationController
+                   animated:YES
+                 completion:nil];
+}
+
 - (void)showReportAnIssue {
   if (_settingsNavigationController)
     return;
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h
index 334b758..4a2da9834 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h
@@ -21,7 +21,7 @@
 @property(nonatomic, readonly, retain) UIImageView* imageView;
 @property(nonatomic, readonly, retain) UIImageView* answerImageView;
 @property(nonatomic, readonly, retain) UIButton* appendButton;
-@property(nonatomic, readonly, retain) UIImageView* physicalWebImageView;
+@property(nonatomic, readonly, retain) UIButton* physicalWebButton;
 @property(nonatomic, assign) CGFloat rowHeight;
 
 // Initialize the row with the given incognito state. The colors and styling are
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
index be53f8a6..e105675 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
@@ -34,9 +34,9 @@
 @synthesize textTruncatingLabel = _textTruncatingLabel;
 @synthesize detailTruncatingLabel = _detailTruncatingLabel;
 @synthesize appendButton = _appendButton;
+@synthesize physicalWebButton = _physicalWebButton;
 @synthesize answerImageView = _answerImageView;
 @synthesize imageView = _imageView;
-@synthesize physicalWebImageView = _physicalWebImageView;
 @synthesize rowHeight = _rowHeight;
 
 - (instancetype)initWithStyle:(UITableViewCellStyle)style
@@ -73,11 +73,10 @@
     // The current implementation is from before using a UITableViewCell.
     [self addSubview:_appendButton];
 
-    _physicalWebImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
-    _physicalWebImageView.userInteractionEnabled = NO;
-    _physicalWebImageView.contentMode = UIViewContentModeCenter;
-    _physicalWebImageView.image = NativeImage(IDR_IOS_OMNIBOX_PHYSICAL_WEB);
-    [self addSubview:_physicalWebImageView];
+    _physicalWebButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
+    [_physicalWebButton setContentMode:UIViewContentModeRight];
+    [self updatePhysicalWebImage];
+    [self addSubview:_physicalWebButton];
 
     // Left icon is only displayed on iPad.
     if (IsIPadIdiom()) {
@@ -120,7 +119,7 @@
                      floor((_rowHeight - appendButtonDimensionLength) / 2),
                      appendButtonDimensionLength, appendButtonDimensionLength);
   _appendButton.frame = LayoutRectGetRect(rightAccessoryLayout);
-  _physicalWebImageView.frame = LayoutRectGetRect(rightAccessoryLayout);
+  _physicalWebButton.frame = LayoutRectGetRect(rightAccessoryLayout);
 }
 
 - (void)updateLeftImage:(int)imageID {
@@ -168,6 +167,16 @@
                  forState:UIControlStateHighlighted];
 }
 
+- (void)updatePhysicalWebImage {
+  UIImage* physicalWebImage = NativeImage(IDR_IOS_OMNIBOX_PHYSICAL_WEB);
+  [_physicalWebButton setImage:physicalWebImage forState:UIControlStateNormal];
+
+  UIImage* physicalWebImageSelected =
+      NativeImage(IDR_IOS_OMNIBOX_PHYSICAL_WEB_HIGHLIGHTED);
+  [_physicalWebButton setImage:physicalWebImageSelected
+                      forState:UIControlStateHighlighted];
+}
+
 - (NSString*)accessibilityLabel {
   return _textTruncatingLabel.attributedText.string;
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
index 29c71449f..f408e9c2 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
@@ -9,6 +9,7 @@
 #include "base/ios/ios_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
+#include "base/metrics/user_metrics.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/omnibox/browser/autocomplete_input.h"
@@ -16,6 +17,8 @@
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/suggestion_answer.h"
 #include "ios/chrome/browser/ui/animation_util.h"
+#import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
+#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
@@ -183,6 +186,9 @@
                          action:@selector(appendButtonTapped:)
                forControlEvents:UIControlEventTouchUpInside];
     [row.appendButton setTag:i];
+    [row.physicalWebButton addTarget:self
+                              action:@selector(physicalWebButtonTapped:)
+                    forControlEvents:UIControlEventTouchUpInside];
     row.rowHeight = kRowHeight;
   }
   _rows.reset([rowsBuilder copy]);
@@ -384,7 +390,8 @@
   BOOL physicalWebMatch =
       match.type == AutocompleteMatchType::PHYSICAL_WEB ||
       match.type == AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW;
-  row.physicalWebImageView.hidden = !physicalWebMatch;
+  row.physicalWebButton.hidden = !physicalWebMatch;
+  [row.physicalWebButton cancelTrackingWithEvent:nil];
 
   // If a right accessory element is present or the text alignment is right
   // aligned, adjust the width to align with the accessory element.
@@ -645,6 +652,16 @@
   _popupView->CopyToOmnibox(contents);
 }
 
+- (void)physicalWebButtonTapped:(id)sender {
+  base::scoped_nsobject<GenericChromeCommand> command([
+      [GenericChromeCommand alloc] initWithTag:IDC_SHOW_PHYSICAL_WEB_SETTINGS]);
+  [command executeOnMainWindow];
+
+  // Record when the user opens the Physical Web preference page from the
+  // omnibox suggestion.
+  base::RecordAction(base::UserMetricsAction("PhysicalWeb.Prefs.FromOmnibox"));
+}
+
 #pragma mark -
 #pragma mark UIScrollViewDelegate
 
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.h b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
index 3532e5e..3b2c1bd 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
@@ -109,6 +109,13 @@
 newSavePasswordsController:(ios::ChromeBrowserState*)browserState
                   delegate:(id<SettingsNavigationControllerDelegate>)delegate;
 
+// Creates and displays a new PhysicalWebCollectionViewController and the chrome
+// around it. |browserState| is used to personalize some settings aspects and
+// should not be null. |delegate| may be nil.
++ (SettingsNavigationController*)
+newPhysicalWebController:(ios::ChromeBrowserState*)browserState
+                delegate:(id<SettingsNavigationControllerDelegate>)delegate;
+
 // Creates and displays a new UserFeedbackViewController. |browserState| is used
 // to personalize some settings aspects and should not be nil. |dataSource| is
 // used to populate the UserFeedbackViewController. |delegate| may be nil.
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 7940e97..bdf380b 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -9,6 +9,7 @@
 #include "base/mac/foundation_util.h"
 #import "base/mac/scoped_nsobject.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
@@ -25,6 +26,7 @@
 #import "ios/chrome/browser/ui/settings/contextual_search_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/import_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/native_apps_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/physical_web_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_utils.h"
@@ -258,6 +260,20 @@
 }
 
 + (SettingsNavigationController*)
+newPhysicalWebController:(ios::ChromeBrowserState*)browserState
+                delegate:(id<SettingsNavigationControllerDelegate>)delegate {
+  base::scoped_nsobject<UIViewController> controller(
+      [[PhysicalWebCollectionViewController alloc]
+          initWithPrefs:GetApplicationContext()->GetLocalState()]);
+  SettingsNavigationController* nc = [[SettingsNavigationController alloc]
+      initWithRootViewController:controller
+                    browserState:browserState
+                        delegate:delegate];
+  [controller navigationItem].rightBarButtonItem = [nc doneButton];
+  return nc;
+}
+
++ (SettingsNavigationController*)
 newImportDataController:(ios::ChromeBrowserState*)browserState
                delegate:(id<SettingsNavigationControllerDelegate>)delegate
      importDataDelegate:(id<ImportDataControllerDelegate>)importDataDelegate
diff --git a/mash/task_viewer/task_viewer.cc b/mash/task_viewer/task_viewer.cc
index cd9d8a4..30025d44 100644
--- a/mash/task_viewer/task_viewer.cc
+++ b/mash/task_viewer/task_viewer.cc
@@ -146,7 +146,7 @@
   // Overridden from views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override {
     DCHECK_EQ(sender, kill_button_);
-    DCHECK_EQ(table_view_->SelectedRowCount(), 1);
+    DCHECK_EQ(table_view_->selection_model().size(), 1UL);
     int row = table_view_->FirstSelectedRow();
     DCHECK(row < static_cast<int>(instances_.size()));
     base::Process process = base::Process::Open(instances_[row]->pid);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 654ba89..c6b8b571ce 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -346,14 +346,21 @@
 }
 
 void WebMediaPlayerImpl::enteredFullscreen() {
-  if (!force_video_overlays_ && enable_fullscreen_video_overlays_)
+  // |force_video_overlays_| implies that we're already in overlay mode, so take
+  // no action here.  Otherwise, switch to an overlay if it's allowed and if
+  // it will display properly.
+  if (!force_video_overlays_ && enable_fullscreen_video_overlays_ &&
+      DoesOverlaySupportMetadata()) {
     EnableOverlay();
+  }
   if (observer_)
     observer_->OnEnteredFullscreen();
 }
 
 void WebMediaPlayerImpl::exitedFullscreen() {
-  if (!force_video_overlays_ && enable_fullscreen_video_overlays_)
+  // If we're in overlay mode, then exit it unless we're supposed to be in
+  // overlay mode all the time.
+  if (!force_video_overlays_ && overlay_enabled_)
     DisableOverlay();
   if (observer_)
     observer_->OnExitedFullscreen();
@@ -1155,8 +1162,15 @@
     pipeline_metadata_.natural_size = GetRotatedVideoSize(
         pipeline_metadata_.video_rotation, pipeline_metadata_.natural_size);
 
-    if (overlay_enabled_ && surface_manager_)
-      surface_manager_->NaturalSizeChanged(pipeline_metadata_.natural_size);
+    if (overlay_enabled_) {
+      // SurfaceView doesn't support rotated video, so transition back if
+      // the video is now rotated.  If |force_video_overlays_|, we keep the
+      // overlay anyway so that the state machine keeps working.
+      if (!force_video_overlays_ && !DoesOverlaySupportMetadata())
+        DisableOverlay();
+      else if (surface_manager_)
+        surface_manager_->NaturalSizeChanged(pipeline_metadata_.natural_size);
+    }
 
     DCHECK(!video_weblayer_);
     video_weblayer_.reset(new cc_blink::WebLayerImpl(cc::VideoLayer::Create(
@@ -2011,6 +2025,10 @@
   return delegate_ && delegate_->IsHidden();
 }
 
+bool WebMediaPlayerImpl::DoesOverlaySupportMetadata() const {
+  return pipeline_metadata_.video_rotation == VIDEO_ROTATION_0;
+}
+
 void WebMediaPlayerImpl::ActivateViewportIntersectionMonitoring(bool activate) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 711799d..5aa465c 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -363,6 +363,10 @@
   // Returns true if the player is hidden.
   bool IsHidden() const;
 
+  // Return whether |pipeline_metadata_| is compatible with an overlay. This
+  // is intended for android.
+  bool DoesOverlaySupportMetadata() const;
+
   blink::WebLocalFrame* frame_;
 
   // The playback state last reported to |delegate_|, to avoid setting duplicate
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h
index dc0b77f..e561267 100644
--- a/media/capture/video/video_capture_device.h
+++ b/media/capture/video/video_capture_device.h
@@ -41,6 +41,15 @@
 
 namespace media {
 
+class CAPTURE_EXPORT FrameBufferPool {
+ public:
+  virtual ~FrameBufferPool() {}
+
+  virtual void SetBufferHold(int buffer_id) = 0;
+  virtual void ReleaseBufferHold(int buffer_id) = 0;
+  virtual mojo::ScopedSharedBufferHandle GetHandleForTransit(int buffer_id) = 0;
+};
+
 class CAPTURE_EXPORT VideoFrameConsumerFeedbackObserver {
  public:
   virtual ~VideoFrameConsumerFeedbackObserver() {}
@@ -148,11 +157,10 @@
     // |timestamp|.
     // TODO(chfremer): Consider removing one of the two in order to simplify the
     // interface.
-    virtual void OnIncomingCapturedBuffer(
-        std::unique_ptr<Buffer> buffer,
-        const VideoCaptureFormat& frame_format,
-        base::TimeTicks reference_time,
-        base::TimeDelta timestamp) = 0;
+    virtual void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
+                                          const VideoCaptureFormat& format,
+                                          base::TimeTicks reference_time,
+                                          base::TimeDelta timestamp) = 0;
     virtual void OnIncomingCapturedVideoFrame(
         std::unique_ptr<Buffer> buffer,
         scoped_refptr<VideoFrame> frame) = 0;
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index ab0b916..3321633 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -95,20 +95,20 @@
 void VideoCaptureDeviceClient::OnIncomingCapturedData(
     const uint8_t* data,
     int length,
-    const VideoCaptureFormat& frame_format,
+    const VideoCaptureFormat& format,
     int rotation,
     base::TimeTicks reference_time,
     base::TimeDelta timestamp,
     int frame_feedback_id) {
   TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedData");
-  DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage);
+  DCHECK_EQ(media::PIXEL_STORAGE_CPU, format.pixel_storage);
 
-  if (last_captured_pixel_format_ != frame_format.pixel_format) {
+  if (last_captured_pixel_format_ != format.pixel_format) {
     OnLog("Pixel format: " +
-          media::VideoPixelFormatToString(frame_format.pixel_format));
-    last_captured_pixel_format_ = frame_format.pixel_format;
+          media::VideoPixelFormatToString(format.pixel_format));
+    last_captured_pixel_format_ = format.pixel_format;
 
-    if (frame_format.pixel_format == media::PIXEL_FORMAT_MJPEG &&
+    if (format.pixel_format == media::PIXEL_FORMAT_MJPEG &&
         !external_jpeg_decoder_initialized_) {
       external_jpeg_decoder_initialized_ = true;
       external_jpeg_decoder_ = jpeg_decoder_factory_callback_.Run();
@@ -116,20 +116,20 @@
     }
   }
 
-  if (!frame_format.IsValid())
+  if (!format.IsValid())
     return;
 
-  if (frame_format.pixel_format == media::PIXEL_FORMAT_Y16) {
-    return OnIncomingCapturedY16Data(data, length, frame_format, reference_time,
+  if (format.pixel_format == media::PIXEL_FORMAT_Y16) {
+    return OnIncomingCapturedY16Data(data, length, format, reference_time,
                                      timestamp, frame_feedback_id);
   }
 
   // |chopped_{width,height} and |new_unrotated_{width,height}| are the lowest
   // bit decomposition of {width, height}, grabbing the odd and even parts.
-  const int chopped_width = frame_format.frame_size.width() & 1;
-  const int chopped_height = frame_format.frame_size.height() & 1;
-  const int new_unrotated_width = frame_format.frame_size.width() & ~1;
-  const int new_unrotated_height = frame_format.frame_size.height() & ~1;
+  const int chopped_width = format.frame_size.width() & 1;
+  const int chopped_height = format.frame_size.height() & 1;
+  const int new_unrotated_width = format.frame_size.width() & ~1;
+  const int new_unrotated_height = format.frame_size.height() & ~1;
 
   int destination_width = new_unrotated_width;
   int destination_height = new_unrotated_height;
@@ -167,7 +167,7 @@
   libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY;
 
   bool flip = false;
-  switch (frame_format.pixel_format) {
+  switch (format.pixel_format) {
     case media::PIXEL_FORMAT_UNKNOWN:  // Color format not set.
       break;
     case media::PIXEL_FORMAT_I420:
@@ -232,7 +232,7 @@
 
   // The input |length| can be greater than the required buffer size because of
   // paddings and/or alignments, but it cannot be smaller.
-  DCHECK_GE(static_cast<size_t>(length), frame_format.ImageAllocationSize());
+  DCHECK_GE(static_cast<size_t>(length), format.ImageAllocationSize());
 
   if (external_jpeg_decoder_) {
     const VideoCaptureJpegDecoder::STATUS status =
@@ -240,29 +240,27 @@
     if (status == VideoCaptureJpegDecoder::FAILED) {
       external_jpeg_decoder_.reset();
     } else if (status == VideoCaptureJpegDecoder::INIT_PASSED &&
-               frame_format.pixel_format == media::PIXEL_FORMAT_MJPEG &&
+               format.pixel_format == media::PIXEL_FORMAT_MJPEG &&
                rotation == 0 && !flip) {
-      external_jpeg_decoder_->DecodeCapturedData(data, length, frame_format,
-                                                 reference_time, timestamp,
-                                                 std::move(buffer));
+      external_jpeg_decoder_->DecodeCapturedData(
+          data, length, format, reference_time, timestamp, std::move(buffer));
       return;
     }
   }
 
-  if (libyuv::ConvertToI420(data, length, y_plane_data, yplane_stride,
-                            u_plane_data, uv_plane_stride, v_plane_data,
-                            uv_plane_stride, crop_x, crop_y,
-                            frame_format.frame_size.width(),
-                            (flip ? -1 : 1) * frame_format.frame_size.height(),
-                            new_unrotated_width, new_unrotated_height,
-                            rotation_mode, origin_colorspace) != 0) {
+  if (libyuv::ConvertToI420(
+          data, length, y_plane_data, yplane_stride, u_plane_data,
+          uv_plane_stride, v_plane_data, uv_plane_stride, crop_x, crop_y,
+          format.frame_size.width(),
+          (flip ? -1 : 1) * format.frame_size.height(), new_unrotated_width,
+          new_unrotated_height, rotation_mode, origin_colorspace) != 0) {
     DLOG(WARNING) << "Failed to convert buffer's pixel format to I420 from "
-                  << media::VideoPixelFormatToString(frame_format.pixel_format);
+                  << media::VideoPixelFormatToString(format.pixel_format);
     return;
   }
 
   const VideoCaptureFormat output_format =
-      VideoCaptureFormat(dimensions, frame_format.frame_rate,
+      VideoCaptureFormat(dimensions, format.frame_rate,
                          media::PIXEL_FORMAT_I420, media::PIXEL_STORAGE_CPU);
   OnIncomingCapturedBuffer(std::move(buffer), output_format, reference_time,
                            timestamp);
@@ -294,11 +292,11 @@
 
 void VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
     std::unique_ptr<Buffer> buffer,
-    const VideoCaptureFormat& frame_format,
+    const VideoCaptureFormat& format,
     base::TimeTicks reference_time,
     base::TimeDelta timestamp) {
-  DCHECK(IsFormatSupported(frame_format.pixel_format));
-  DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage);
+  DCHECK(IsFormatSupported(format.pixel_format));
+  DCHECK_EQ(media::PIXEL_STORAGE_CPU, format.pixel_storage);
 
   scoped_refptr<VideoFrame> frame;
   if (buffer->IsBackedByVideoFrame()) {
@@ -306,17 +304,15 @@
     frame->set_timestamp(timestamp);
   } else {
     frame = VideoFrame::WrapExternalSharedMemory(
-        frame_format.pixel_format, frame_format.frame_size,
-        gfx::Rect(frame_format.frame_size), frame_format.frame_size,
-        reinterpret_cast<uint8_t*>(buffer->data()),
-        VideoFrame::AllocationSize(frame_format.pixel_format,
-                                   frame_format.frame_size),
+        format.pixel_format, format.frame_size, gfx::Rect(format.frame_size),
+        format.frame_size, reinterpret_cast<uint8_t*>(buffer->data()),
+        VideoFrame::AllocationSize(format.pixel_format, format.frame_size),
         base::SharedMemory::NULLHandle(), 0u, timestamp);
   }
   if (!frame)
     return;
   frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
-                               frame_format.frame_rate);
+                               format.frame_rate);
   frame->metadata()->SetTimeTicks(media::VideoFrameMetadata::REFERENCE_TIME,
                                   reference_time);
   OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
@@ -395,16 +391,16 @@
 void VideoCaptureDeviceClient::OnIncomingCapturedY16Data(
     const uint8_t* data,
     int length,
-    const VideoCaptureFormat& frame_format,
+    const VideoCaptureFormat& format,
     base::TimeTicks reference_time,
     base::TimeDelta timestamp,
     int frame_feedback_id) {
   std::unique_ptr<Buffer> buffer(
-      ReserveOutputBuffer(frame_format.frame_size, media::PIXEL_FORMAT_Y16,
+      ReserveOutputBuffer(format.frame_size, media::PIXEL_FORMAT_Y16,
                           media::PIXEL_STORAGE_CPU, frame_feedback_id));
   // The input |length| can be greater than the required buffer size because of
   // paddings and/or alignments, but it cannot be smaller.
-  DCHECK_GE(static_cast<size_t>(length), frame_format.ImageAllocationSize());
+  DCHECK_GE(static_cast<size_t>(length), format.ImageAllocationSize());
 #if DCHECK_IS_ON()
   dropped_frame_counter_ = buffer.get() ? 0 : dropped_frame_counter_ + 1;
   if (dropped_frame_counter_ >= kMaxDroppedFrames)
@@ -415,7 +411,7 @@
     return;
   memcpy(buffer->data(), data, length);
   const VideoCaptureFormat output_format =
-      VideoCaptureFormat(frame_format.frame_size, frame_format.frame_rate,
+      VideoCaptureFormat(format.frame_size, format.frame_rate,
                          media::PIXEL_FORMAT_Y16, media::PIXEL_STORAGE_CPU);
   OnIncomingCapturedBuffer(std::move(buffer), output_format, reference_time,
                            timestamp);
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index 60d523f..7258650 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -32,11 +32,8 @@
     "common_type_converters.h",
     "data_pipe_drainer.cc",
     "data_pipe_drainer.h",
-    "data_pipe_file_utils.cc",
     "data_pipe_utils.cc",
     "data_pipe_utils.h",
-    "user_agent.cc",
-    "user_agent.h",
   ]
 
   defines = [ "MOJO_COMMON_IMPLEMENTATION" ]
diff --git a/mojo/common/DEPS b/mojo/common/DEPS
index f3e298f..e8ac4288 100644
--- a/mojo/common/DEPS
+++ b/mojo/common/DEPS
@@ -1,16 +1,6 @@
 include_rules = [
   # common must not depend on embedder.
   "-mojo",
-  "+services/service_manager/public/cpp",
   "+mojo/common",
   "+mojo/public",
 ]
-
-specific_include_rules = {
-  "trace_controller_impl\.h": [
-    "+services/tracing/public/interfaces/tracing.mojom.h"
-  ],
-  "tracing_impl\.h": [
-    "+services/tracing/public/interfaces/tracing.mojom.h"
-  ],
-}
diff --git a/mojo/common/data_pipe_file_utils.cc b/mojo/common/data_pipe_file_utils.cc
deleted file mode 100644
index 841dfde9..0000000
--- a/mojo/common/data_pipe_file_utils.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/common/data_pipe_utils.h"
-
-#include <stdint.h>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/location.h"
-#include "base/task_runner_util.h"
-
-namespace mojo {
-namespace common {
-namespace {
-
-bool BlockingCopyFromFile(const base::FilePath& source,
-                          ScopedDataPipeProducerHandle destination,
-                          uint32_t skip) {
-  base::File file(source, base::File::FLAG_OPEN | base::File::FLAG_READ);
-  if (!file.IsValid())
-    return false;
-  if (file.Seek(base::File::FROM_BEGIN, skip) != skip) {
-    LOG(ERROR) << "Seek of " << skip << " in " << source.value() << " failed";
-    return false;
-  }
-  for (;;) {
-    void* buffer = nullptr;
-    uint32_t buffer_num_bytes = 0;
-    MojoResult result =
-        BeginWriteDataRaw(destination.get(), &buffer, &buffer_num_bytes,
-                          MOJO_WRITE_DATA_FLAG_NONE);
-    if (result == MOJO_RESULT_OK) {
-      int bytes_read =
-          file.ReadAtCurrentPos(static_cast<char*>(buffer), buffer_num_bytes);
-      if (bytes_read >= 0) {
-        EndWriteDataRaw(destination.get(), bytes_read);
-        if (bytes_read == 0) {
-          // eof
-          return true;
-        }
-      } else {
-        // error
-        EndWriteDataRaw(destination.get(), 0);
-        return false;
-      }
-    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
-      result = Wait(destination.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
-                    MOJO_DEADLINE_INDEFINITE, nullptr);
-      if (result != MOJO_RESULT_OK) {
-        // If the consumer handle was closed, then treat as EOF.
-        return result == MOJO_RESULT_FAILED_PRECONDITION;
-      }
-    } else {
-      // If the consumer handle was closed, then treat as EOF.
-      return result == MOJO_RESULT_FAILED_PRECONDITION;
-    }
-  }
-#if !defined(OS_WIN)
-  NOTREACHED();
-  return false;
-#endif
-}
-
-}  // namespace
-
-void CopyFromFile(const base::FilePath& source,
-                  ScopedDataPipeProducerHandle destination,
-                  uint32_t skip,
-                  base::TaskRunner* task_runner,
-                  const base::Callback<void(bool)>& callback) {
-  base::PostTaskAndReplyWithResult(task_runner, FROM_HERE,
-                                   base::Bind(&BlockingCopyFromFile, source,
-                                              base::Passed(&destination), skip),
-                                   callback);
-}
-
-}  // namespace common
-}  // namespace mojo
diff --git a/mojo/common/data_pipe_utils.cc b/mojo/common/data_pipe_utils.cc
index 8540ac63..bed5e85 100644
--- a/mojo/common/data_pipe_utils.cc
+++ b/mojo/common/data_pipe_utils.cc
@@ -4,16 +4,9 @@
 
 #include "mojo/common/data_pipe_utils.h"
 
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
 #include <utility>
 
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/message_loop/message_loop.h"
-#include "base/task_runner_util.h"
+#include "base/bind.h"
 
 namespace mojo {
 namespace common {
@@ -58,12 +51,7 @@
   return num_bytes;
 }
 
-size_t CopyToFileHelper(FILE* fp, const void* buffer, uint32_t num_bytes) {
-  return fwrite(buffer, 1, num_bytes, fp);
-}
-
-} // namespace
-
+}  // namespace
 
 // TODO(hansmuller): Add a max_size parameter.
 bool BlockingCopyToString(ScopedDataPipeConsumerHandle source,
@@ -107,25 +95,5 @@
   }
 }
 
-bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
-                        const base::FilePath& destination) {
-  base::ScopedFILE fp(base::OpenFile(destination, "wb"));
-  if (!fp)
-    return false;
-  return BlockingCopyHelper(std::move(source),
-                            base::Bind(&CopyToFileHelper, fp.get()));
-}
-
-void CopyToFile(ScopedDataPipeConsumerHandle source,
-                const base::FilePath& destination,
-                base::TaskRunner* task_runner,
-                const base::Callback<void(bool)>& callback) {
-  base::PostTaskAndReplyWithResult(
-      task_runner,
-      FROM_HERE,
-      base::Bind(&BlockingCopyToFile, base::Passed(&source), destination),
-      callback);
-}
-
 }  // namespace common
 }  // namespace mojo
diff --git a/mojo/common/data_pipe_utils.h b/mojo/common/data_pipe_utils.h
index 426912c..a3f7c093 100644
--- a/mojo/common/data_pipe_utils.h
+++ b/mojo/common/data_pipe_utils.h
@@ -2,41 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MOJO_SHELL_DATA_PIPE_UTILS_H_
-#define MOJO_SHELL_DATA_PIPE_UTILS_H_
+#ifndef MOJO_COMMON_DATA_PIPE_UTILS_H_
+#define MOJO_COMMON_DATA_PIPE_UTILS_H_
 
 #include <stdint.h>
 
 #include <string>
 
-#include "base/callback_forward.h"
 #include "mojo/common/mojo_common_export.h"
-#include "mojo/public/cpp/system/core.h"
-
-namespace base {
-class FilePath;
-class TaskRunner;
-}
+#include "mojo/public/cpp/system/data_pipe.h"
 
 namespace mojo {
 namespace common {
 
-// Asynchronously copies data from source to the destination file. The given
-// |callback| is run upon completion. File writes will be scheduled to the
-// given |task_runner|.
-void MOJO_COMMON_EXPORT CopyToFile(
-    ScopedDataPipeConsumerHandle source,
-    const base::FilePath& destination,
-    base::TaskRunner* task_runner,
-    const base::Callback<void(bool /*success*/)>& callback);
-
-void MOJO_COMMON_EXPORT
-CopyFromFile(const base::FilePath& source,
-             ScopedDataPipeProducerHandle destination,
-             uint32_t skip,
-             base::TaskRunner* task_runner,
-             const base::Callback<void(bool /*success*/)>& callback);
-
 // Copies the data from |source| into |contents| and returns true on success and
 // false on error.  In case of I/O error, |contents| holds the data that could
 // be read from source before the error occurred.
@@ -48,13 +26,7 @@
     const std::string& source,
     const ScopedDataPipeProducerHandle& destination);
 
-// Synchronously copies data from source to the destination file returning true
-// on success and false on error.  In case of an error, |destination| holds the
-// data that could be read from the source before the error occured.
-bool MOJO_COMMON_EXPORT BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
-                                           const base::FilePath& destination);
-
 }  // namespace common
 }  // namespace mojo
 
-#endif  // MOJO_SHELL_DATA_PIPE_UTILS_H_
+#endif  // MOJO_COMMON_DATA_PIPE_UTILS_H_
diff --git a/mojo/common/user_agent.cc b/mojo/common/user_agent.cc
deleted file mode 100644
index 6055cbe5a..0000000
--- a/mojo/common/user_agent.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/common/user_agent.h"
-
-#include "build/build_config.h"
-
-namespace mojo {
-namespace common {
-
-std::string GetUserAgent() {
-  // TODO(jam): change depending on OS
-#if defined(OS_ANDROID)
-  return "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY48G) "
-         "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.68 "
-         "Safari/537.36";
-#else
-  return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like "
-         "Gecko) Chrome/42.0.2311.68 Safari/537.36";
-#endif
-}
-
-}  // namespace common
-}  // namespace mojo
diff --git a/mojo/common/user_agent.h b/mojo/common/user_agent.h
deleted file mode 100644
index 031b1029..0000000
--- a/mojo/common/user_agent.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_COMMON_USER_AGENT_H_
-#define MOJO_COMMON_USER_AGENT_H_
-
-#include <string>
-
-#include "mojo/common/mojo_common_export.h"
-
-namespace mojo {
-namespace common {
-
-std::string MOJO_COMMON_EXPORT GetUserAgent();
-
-}  // namespace common
-}  // namespace mojo
-
-#endif  // MOJO_COMMON_USER_AGENT_H_
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 37cb814..e7b4227f 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -850,7 +850,7 @@
 // Logged when a delegate informs the URL_REQUEST of what's currently blocking
 // the request. The parameters attached to the begin event are:
 //   {
-//     "delegate_info": <Information about what's blocking the request>,
+//     "delegate_blocked_by": <Information about what's blocking the request>,
 //   }
 EVENT_TYPE(DELEGATE_INFO)
 
@@ -3030,3 +3030,13 @@
 //              the end of file. Result < 0 means an error.>
 // }
 EVENT_TYPE(UPLOAD_DATA_STREAM_READ)
+
+// -----------------------------------------------------------------------------
+// ResourceScheduler related events
+// -----------------------------------------------------------------------------
+
+// The ResourceScheduler has started a previously blocked request.  Parameters:
+// {
+//   "trigger": <Trigger for evaluation that caused request start>
+// }
+EVENT_TYPE(RESOURCE_SCHEDULER_REQUEST_STARTED)
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 75eb422..f687bec 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -294,7 +294,7 @@
   if (!load_state.param.empty())
     dict->SetString("load_state_param", load_state.param);
   if (!blocked_by_.empty())
-    dict->SetString("delegate_info", blocked_by_);
+    dict->SetString("delegate_blocked_by", blocked_by_);
 
   dict->SetString("method", method_);
   dict->SetBoolean("has_upload", has_upload());
@@ -335,8 +335,9 @@
   blocked_by_ = blocked_by;
   use_blocked_by_as_load_param_ = false;
 
-  net_log_.BeginEvent(NetLogEventType::DELEGATE_INFO,
-                      NetLog::StringCallback("delegate_info", &blocked_by_));
+  net_log_.BeginEvent(
+      NetLogEventType::DELEGATE_INFO,
+      NetLog::StringCallback("delegate_blocked_by", &blocked_by_));
 }
 
 void URLRequest::LogAndReportBlockedBy(const char* source) {
@@ -584,7 +585,9 @@
   DCHECK(base::ThreadTaskRunnerHandle::IsSet());
 
   context->url_requests()->insert(this);
-  net_log_.BeginEvent(NetLogEventType::REQUEST_ALIVE);
+  net_log_.BeginEvent(
+      NetLogEventType::REQUEST_ALIVE,
+      base::Bind(&NetLogURLRequestConstructorCallback, &url, priority_));
 }
 
 void URLRequest::BeforeRequestComplete(int error) {
@@ -627,7 +630,6 @@
   net_log_.BeginEvent(
       NetLogEventType::URL_REQUEST_START_JOB,
       base::Bind(&NetLogURLRequestStartCallback, &url(), &method_, load_flags_,
-                 priority_,
                  upload_data_stream_ ? upload_data_stream_->identifier() : -1));
 
   job_.reset(job);
@@ -1024,12 +1026,11 @@
     return;
 
   priority_ = priority;
-  if (job_.get()) {
-    net_log_.AddEvent(
-        NetLogEventType::URL_REQUEST_SET_PRIORITY,
-        NetLog::StringCallback("priority", RequestPriorityToString(priority_)));
+  net_log_.AddEvent(
+      NetLogEventType::URL_REQUEST_SET_PRIORITY,
+      NetLog::StringCallback("priority", RequestPriorityToString(priority_)));
+  if (job_.get())
     job_->SetPriority(priority_);
-  }
 }
 
 void URLRequest::NotifyAuthRequired(AuthChallengeInfo* auth_info) {
diff --git a/net/url_request/url_request_netlog_params.cc b/net/url_request/url_request_netlog_params.cc
index 97b67bad..9fc0f22 100644
--- a/net/url_request/url_request_netlog_params.cc
+++ b/net/url_request/url_request_netlog_params.cc
@@ -13,18 +13,26 @@
 
 namespace net {
 
+std::unique_ptr<base::Value> NetLogURLRequestConstructorCallback(
+    const GURL* url,
+    RequestPriority priority,
+    NetLogCaptureMode /* capture_mode */) {
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  dict->SetString("url", url->possibly_invalid_spec());
+  dict->SetString("priority", RequestPriorityToString(priority));
+  return std::move(dict);
+}
+
 std::unique_ptr<base::Value> NetLogURLRequestStartCallback(
     const GURL* url,
     const std::string* method,
     int load_flags,
-    RequestPriority priority,
     int64_t upload_id,
     NetLogCaptureMode /* capture_mode */) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
   dict->SetString("url", url->possibly_invalid_spec());
   dict->SetString("method", *method);
   dict->SetInteger("load_flags", load_flags);
-  dict->SetString("priority", RequestPriorityToString(priority));
   if (upload_id > -1)
     dict->SetString("upload_id", base::Int64ToString(upload_id));
   return std::move(dict);
diff --git a/net/url_request/url_request_netlog_params.h b/net/url_request/url_request_netlog_params.h
index a42ca3f3..2d672f5 100644
--- a/net/url_request/url_request_netlog_params.h
+++ b/net/url_request/url_request_netlog_params.h
@@ -23,12 +23,17 @@
 
 class NetLogCaptureMode;
 
+// Returns a Value containing NetLog parameters for constructing a URLRequest.
+NET_EXPORT std::unique_ptr<base::Value> NetLogURLRequestConstructorCallback(
+    const GURL* url,
+    RequestPriority priority,
+    NetLogCaptureMode /* capture_mode */);
+
 // Returns a Value containing NetLog parameters for starting a URLRequest.
 NET_EXPORT std::unique_ptr<base::Value> NetLogURLRequestStartCallback(
     const GURL* url,
     const std::string* method,
     int load_flags,
-    RequestPriority priority,
     int64_t upload_id,
     NetLogCaptureMode /* capture_mode */);
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 616eb319..e5c941f 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -4861,7 +4861,7 @@
     std::string delegate_info;
     EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type);
     EXPECT_EQ(NetLogEventPhase::BEGIN, entries[log_position].phase);
-    EXPECT_TRUE(entries[log_position].GetStringValue("delegate_info",
+    EXPECT_TRUE(entries[log_position].GetStringValue("delegate_blocked_by",
                                                      &delegate_info));
     EXPECT_EQ(kFirstDelegateInfo, delegate_info);
 
@@ -4872,7 +4872,7 @@
     ++log_position;
     EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type);
     EXPECT_EQ(NetLogEventPhase::BEGIN, entries[log_position].phase);
-    EXPECT_TRUE(entries[log_position].GetStringValue("delegate_info",
+    EXPECT_TRUE(entries[log_position].GetStringValue("delegate_blocked_by",
                                                      &delegate_info));
     EXPECT_EQ(kSecondDelegateInfo, delegate_info);
 
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index d4d5672..904502a 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -41,7 +41,10 @@
     "//third_party/skia/include/utils",
   ]
 
-  defines = [ "SK_IGNORE_DW_GRAY_FIX" ]
+  defines = [
+    "SK_IGNORE_DW_GRAY_FIX",
+    "SK_LEGACY_FONTMGR_FACTORY",
+  ]
   defines += skia_for_chromium_defines
 
   defines += []
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 6b0390a..42adfe5 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2340,8 +2340,6 @@
 crbug.com/664855 virtual/scroll_customization/fast/scroll-behavior/main-frame-interrupted-scroll.html [ Pass Failure ]
 crbug.com/664856 virtual/sharedarraybuffer/fast/workers/worker-gc.html [ Pass Failure ]
 
-crbug.com/664857 virtual/threaded/animations/composited-animations-simple.html [ Pass Failure ]
-
 # Possible duplicate of crbug.com/665577
 # crbug.com/664858 virtual/threaded/fast/scroll-behavior/overflow-scroll-animates.html [ Pass Failure ]
 # crbug.com/664858 virtual/threaded/fast/scroll-behavior/smooth-scroll/horizontal-smooth-scroll-in-rtl.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/animations/resources/composited-animation-test.js b/third_party/WebKit/LayoutTests/animations/resources/composited-animation-test.js
index dfdbd3a129..3b5fc00 100644
--- a/third_party/WebKit/LayoutTests/animations/resources/composited-animation-test.js
+++ b/third_party/WebKit/LayoutTests/animations/resources/composited-animation-test.js
@@ -139,15 +139,21 @@
     this.error.textContent += `${test.name}: ${message} `;
   }
 
+  waitForCompositor() {
+    return this.error.animate({opacity: ['1', '1']}, 1).ready;
+  }
+
   layoutAndPaint() {
     if (window.testRunner)
       testRunner.waitUntilDone();
 
-    requestAnimationFrame(() => {
-      if (window.internals)
-        this.assertAnimationCompositedState();
-      if (window.testRunner)
-        testRunner.notifyDone();
+    this.waitForCompositor().then(() => {
+      requestAnimationFrame(() => {
+        if (window.internals)
+          this.assertAnimationCompositedState();
+        if (window.testRunner)
+          testRunner.notifyDone();
+      });
     });
   }
 
diff --git a/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js b/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js
index 7946790..a4864ee 100644
--- a/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js
+++ b/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js
@@ -3,23 +3,17 @@
 let mockVRService = loadMojoModules(
     'mockVRService',
     ['mojo/public/js/bindings',
-     'mojo/public/js/connection',
-     'mojo/public/js/router',
      'device/vr/vr_service.mojom',
     ]).then(mojo => {
-  let [bindings, connection, router, vr_service] = mojo.modules;
+  let [bindings, vr_service] = mojo.modules;
 
-  class MockVRDisplay extends vr_service.VRDisplay.stubClass {
+  class MockVRDisplay {
     constructor(interfaceProvider) {
-      super();
+      this.bindingSet_ = new bindings.BindingSet(vr_service.VRDisplay);
+
       interfaceProvider.addInterfaceOverrideForTesting(
           vr_service.VRDisplay.name,
-          handle => this.connect_(handle));
-    }
-
-    connect_(handle) {
-      this.router_ = new router.Router(handle);
-      this.router_.setIncomingReceiver(this);
+          handle => this.bindingSet_.addBinding(this, handle));
     }
 
     requestPresent(secureOrigin) {
@@ -27,42 +21,36 @@
     }
   }
 
-  class MockVRService extends vr_service.VRService.stubClass {
+  class MockVRService {
     constructor(interfaceProvider) {
-      super();
+      this.bindingSet_ = new bindings.BindingSet(vr_service.VRService);
+      this.displayClient_ = new vr_service.VRDisplayClientPtr();
+      this.vrDisplays_ = null;
+
       interfaceProvider.addInterfaceOverrideForTesting(
           vr_service.VRService.name,
-          handle => this.connect_(handle));
-      this.vr_displays_ = null;
-    }
-
-    connect_(handle) {
-      this.router_ = new router.Router(handle);
-      this.router_.setIncomingReceiver(this);
+          handle => this.bindingSet_.addBinding(this, handle));
     }
 
     setVRDisplays(displays) {
       for (let i = 0; i < displays.length; i++) {
         displays[i].index = i;
       }
-      this.vr_displays_ = displays;
+      this.vrDisplays_ = displays;
     }
 
     notifyClientOfDisplays() {
-      if (this.vr_displays_ == null) {
+      if (this.vrDisplays_ == null) {
         return;
       }
-      for (let i = 0; i < this.vr_displays_.length; i++) {
+      for (let i = 0; i < this.vrDisplays_.length; i++) {
         let displayPtr = new vr_service.VRDisplayPtr();
         let request = bindings.makeRequest(displayPtr);
         let binding = new bindings.Binding(
             vr_service.VRDisplay,
             new MockVRDisplay(mojo.frameInterfaces), request);
-        let client_handle = new bindings.InterfaceRequest(
-          connection.bindProxy(proxy => {
-            this.displayClient_ = proxy;
-        }, vr_service.VRDisplayClient));
-        this.client_.onDisplayConnected(displayPtr, client_handle, this.vr_displays_[i]);
+        let clientRequest = bindings.makeRequest(this.displayClient_);
+        this.client_.onDisplayConnected(displayPtr, clientRequest, this.vrDisplays_[i]);
       }
     }
 
@@ -70,7 +58,7 @@
       this.client_ = client;
       this.notifyClientOfDisplays();
 
-      var device_number = (this.vr_displays_== null ? 0 : this.vr_displays_.length);
+      var device_number = (this.vrDisplays_== null ? 0 : this.vrDisplays_.length);
       return Promise.resolve({numberOfConnectedDevices: device_number});
     }
   }
diff --git a/third_party/WebKit/Source/core/input/MouseEventManager.cpp b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
index d377ff2b..da2fdc0 100644
--- a/third_party/WebKit/Source/core/input/MouseEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
@@ -901,8 +901,7 @@
                                           DragOperation operation) {
   if (dragState().m_dragSrc) {
     dragState().m_dragDataTransfer->setDestinationOperation(operation);
-    // For now we don't care if event handler cancels default behavior, since
-    // there is none.
+    // The return value is ignored because dragend is not cancelable.
     dispatchDragSrcEvent(EventTypeNames::dragend, event);
   }
   clearDragDataTransfer();
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_units.h b/third_party/WebKit/Source/core/layout/ng/ng_units.h
index 235bf80..6c768d7 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_units.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_units.h
@@ -348,8 +348,6 @@
                              LayoutUnit margin_end) const {
     DCHECK_GE(container_size, LayoutUnit());
     DCHECK_GE(length, LayoutUnit());
-    DCHECK_GE(margin_start, LayoutUnit());
-    DCHECK_GE(margin_end, LayoutUnit());
     if (position_matches)
       return position;
     else
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 6e8ab3d..93fc1fc5 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -422,8 +422,16 @@
           localPaintingInfo.paintDirtyRect, cacheSlot,
           IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot,
           localPaintingInfo.subPixelAccumulation);
-    } else if (!collectPaintFragmentsForPaginatedFixedPosition(
-                   paintingInfo, layerFragments)) {
+    } else if (isFixedPositionObjectInPagedMedia()) {
+      PaintLayerFragments singleFragment;
+      m_paintLayer.appendSingleFragmentIgnoringPagination(
+          singleFragment, localPaintingInfo.rootLayer,
+          localPaintingInfo.paintDirtyRect, cacheSlot,
+          IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot,
+          localPaintingInfo.subPixelAccumulation);
+      repeatFixedPositionObjectInPages(singleFragment[0], paintingInfo,
+                                       layerFragments);
+    } else {
       m_paintLayer.collectFragments(layerFragments, localPaintingInfo.rootLayer,
                                     localPaintingInfo.paintDirtyRect, cacheSlot,
                                     IgnoreOverlayScrollbarSize,
@@ -586,40 +594,42 @@
   return false;
 }
 
-bool PaintLayerPainter::collectPaintFragmentsForPaginatedFixedPosition(
-    const PaintLayerPaintingInfo& paintingInfo,
-    PaintLayerFragments& layerFragments) {
+inline bool PaintLayerPainter::isFixedPositionObjectInPagedMedia() {
   LayoutObject* object = m_paintLayer.layoutObject();
   LayoutView* view = object->view();
-  bool isFixedPosObjectInPagedMedia =
-      object->style()->position() == FixedPosition &&
-      object->container() == view && view->pageLogicalHeight();
+  return object->styleRef().position() == FixedPosition &&
+         object->container() == view && view->pageLogicalHeight() &&
+         // TODO(crbug.com/619094): Figure out the correct behaviour for fixed
+         // position objects in paged media with vertical writing modes.
+         view->isHorizontalWritingMode();
+}
 
-  // TODO(crbug.com/619094): Figure out the correct behaviour for fixed position
-  // objects in paged media with vertical writing modes.
-  if (!isFixedPosObjectInPagedMedia || !view->isHorizontalWritingMode())
-    return false;
+void PaintLayerPainter::repeatFixedPositionObjectInPages(
+    const PaintLayerFragment& singleFragmentIgnoredPagination,
+    const PaintLayerPaintingInfo& paintingInfo,
+    PaintLayerFragments& layerFragments) {
+  DCHECK(isFixedPositionObjectInPagedMedia());
 
-  // "For paged media, boxes with fixed positions are repeated on every page."
-  // https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#fixed-positioning
+  LayoutView* view = m_paintLayer.layoutObject()->view();
   unsigned pages =
       ceilf(view->documentRect().height() / view->pageLogicalHeight());
-  LayoutPoint paginationOffset;
 
   // The fixed position object is offset from the top of the page, so remove
   // any scroll offset.
   LayoutPoint offsetFromRoot;
   m_paintLayer.convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot);
-  paginationOffset -= offsetFromRoot - m_paintLayer.location();
+  LayoutSize offsetAdjustment = m_paintLayer.location() - offsetFromRoot;
+  layerFragments.append(singleFragmentIgnoredPagination);
+  layerFragments[0].paginationOffset += offsetAdjustment;
+  layerFragments[0].layerBounds.move(offsetAdjustment);
 
-  for (unsigned i = 0; i < pages; i++) {
-    PaintLayerFragment fragment;
-    fragment.backgroundRect = paintingInfo.paintDirtyRect;
-    fragment.paginationOffset = paginationOffset;
+  LayoutPoint pageOffset(LayoutUnit(), view->pageLogicalHeight());
+  for (unsigned i = 1; i < pages; i++) {
+    PaintLayerFragment fragment = layerFragments[i - 1];
+    fragment.paginationOffset += pageOffset;
+    fragment.layerBounds.moveBy(pageOffset);
     layerFragments.append(fragment);
-    paginationOffset += LayoutPoint(LayoutUnit(), view->pageLogicalHeight());
   }
-  return true;
 }
 
 PaintResult PaintLayerPainter::paintLayerWithTransform(
@@ -637,48 +647,43 @@
   // its parent.
   PaintLayer* parentLayer = m_paintLayer.parent();
 
-  LayoutObject* object = m_paintLayer.layoutObject();
-  LayoutView* view = object->view();
-  bool isFixedPosObjectInPagedMedia =
-      object->style()->position() == FixedPosition &&
-      object->container() == view && view->pageLogicalHeight();
   PaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer();
   PaintLayerFragments layerFragments;
-  if (!collectPaintFragmentsForPaginatedFixedPosition(paintingInfo,
-                                                      layerFragments)) {
-    if (paginationLayer) {
-      // FIXME: This is a mess. Look closely at this code and the code in Layer
-      // and fix any issues in it & refactor to make it obvious from code
-      // structure what it does and that it's correct.
-      ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects)
-                                         ? UncachedClipRects
-                                         : PaintingClipRects;
-      ShouldRespectOverflowClipType respectOverflowClip =
-          shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject());
-      // Calculate the transformed bounding box in the current coordinate space,
-      // to figure out which fragmentainers (e.g. columns) we need to visit.
-      LayoutRect transformedExtent = PaintLayer::transparencyClipBox(
-          &m_paintLayer, paginationLayer,
-          PaintLayer::PaintingTransparencyClipBox,
-          PaintLayer::RootOfTransparencyClipBox,
-          paintingInfo.subPixelAccumulation,
-          paintingInfo.getGlobalPaintFlags());
-      // FIXME: we don't check if paginationLayer is within
-      // paintingInfo.rootLayer
-      // here.
-      paginationLayer->collectFragments(
-          layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect,
-          cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0,
-          paintingInfo.subPixelAccumulation, &transformedExtent);
-    } else {
-      // We don't need to collect any fragments in the regular way here. We have
-      // already calculated a clip rectangle for the ancestry if it was needed,
-      // and clipping this layer is something that can be done further down the
-      // path, when the transform has been applied.
-      PaintLayerFragment fragment;
-      fragment.backgroundRect = paintingInfo.paintDirtyRect;
+  bool isFixedPositionObjectInPagedMedia =
+      this->isFixedPositionObjectInPagedMedia();
+  if (!paginationLayer || isFixedPositionObjectInPagedMedia) {
+    // We don't need to collect any fragments in the regular way here. We have
+    // already calculated a clip rectangle for the ancestry if it was needed,
+    // and clipping this layer is something that can be done further down the
+    // path, when the transform has been applied.
+    PaintLayerFragment fragment;
+    fragment.backgroundRect = paintingInfo.paintDirtyRect;
+    if (isFixedPositionObjectInPagedMedia)
+      repeatFixedPositionObjectInPages(fragment, paintingInfo, layerFragments);
+    else
       layerFragments.append(fragment);
-    }
+  } else {
+    // FIXME: This is a mess. Look closely at this code and the code in Layer
+    // and fix any issues in it & refactor to make it obvious from code
+    // structure what it does and that it's correct.
+    ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects)
+                                       ? UncachedClipRects
+                                       : PaintingClipRects;
+    ShouldRespectOverflowClipType respectOverflowClip =
+        shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject());
+    // Calculate the transformed bounding box in the current coordinate space,
+    // to figure out which fragmentainers (e.g. columns) we need to visit.
+    LayoutRect transformedExtent = PaintLayer::transparencyClipBox(
+        &m_paintLayer, paginationLayer, PaintLayer::PaintingTransparencyClipBox,
+        PaintLayer::RootOfTransparencyClipBox,
+        paintingInfo.subPixelAccumulation, paintingInfo.getGlobalPaintFlags());
+    // FIXME: we don't check if paginationLayer is within
+    // paintingInfo.rootLayer
+    // here.
+    paginationLayer->collectFragments(
+        layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect,
+        cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, nullptr,
+        paintingInfo.subPixelAccumulation, &transformedExtent);
   }
 
   Optional<DisplayItemCacheSkipper> cacheSkipper;
@@ -707,9 +712,9 @@
     Optional<LayerClipRecorder> clipRecorder;
     if (parentLayer && !RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
       ClipRect clipRectForFragment(ancestorBackgroundClipRect);
-      // A fixed-position object is repeated on every page, but if it is clipped
-      // by an ancestor layer then the repetitions are clipped out.
-      if (!isFixedPosObjectInPagedMedia)
+      // A fixed-position object is repeated on every page instead of paginated,
+      // so we should apply the original ancestor clip rect.
+      if (!isFixedPositionObjectInPagedMedia)
         clipRectForFragment.moveBy(fragment.paginationOffset);
       clipRectForFragment.intersect(fragment.backgroundRect);
       if (clipRectForFragment.isEmpty())
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.h b/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
index 3e9f349..2214bc5 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
@@ -56,7 +56,15 @@
  private:
   enum ClipState { HasNotClipped, HasClipped };
 
-  bool collectPaintFragmentsForPaginatedFixedPosition(
+  inline bool isFixedPositionObjectInPagedMedia();
+
+  // "For paged media, boxes with fixed positions are repeated on every page."
+  // https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#fixed-positioning
+  // Repeats singleFragmentIgnoredPagination of the fixed-position object in
+  // each page, with paginationOffset and layerBounds adjusted for each page.
+  // TODO(wangxianzhu): Fold this into PaintLayer::collectFragments().
+  void repeatFixedPositionObjectInPages(
+      const PaintLayerFragment& singleFragmentIgnoredPagination,
       const PaintLayerPaintingInfo&,
       PaintLayerFragments&);
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index b01a6cd..d49e038 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -4,6 +4,7 @@
 
 #include "core/paint/PaintPropertyTreeBuilder.h"
 
+#include "core/dom/DOMNodeIds.h"
 #include "core/frame/FrameView.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Settings.h"
@@ -336,6 +337,16 @@
       style.transformOriginZ());
 }
 
+namespace {
+
+CompositorElementId createDomNodeBasedCompositorElementId(
+    const LayoutObject& object) {
+  return createCompositorElementId(DOMNodeIds::idForNode(object.node()),
+                                   CompositorSubElementId::Primary);
+}
+
+}  // namespace
+
 void PaintPropertyTreeBuilder::updateTransform(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
@@ -375,12 +386,17 @@
         if (style.preserves3D() && !renderingContextId)
           renderingContextId = PtrHash<const LayoutObject>::hash(&object);
 
+        CompositorElementId compositorElementId =
+            style.hasCurrentTransformAnimation()
+                ? createDomNodeBasedCompositorElementId(object)
+                : CompositorElementId();
+
         auto& properties =
             object.getMutableForPainting().ensurePaintProperties();
         context.forceSubtreeUpdate |= properties.updateTransform(
             context.current.transform, matrix, transformOrigin(box),
             context.current.shouldFlattenInheritedTransform, renderingContextId,
-            compositingReasons);
+            compositingReasons, compositorElementId);
         hasTransform = true;
       }
     }
@@ -484,16 +500,23 @@
     }
 
     CompositingReasons compositingReasons =
-        CompositingReasonFinder::requiresCompositingForEffectAnimation(
-            object.styleRef());
+        CompositingReasonFinder::requiresCompositingForEffectAnimation(style);
     if (compositingReasons != CompositingReasonNone)
       effectNodeNeeded = true;
 
+    CompositorElementId compositorElementId =
+        (style.hasCurrentOpacityAnimation() ||
+         style.hasCurrentFilterAnimation() ||
+         style.hasCurrentBackdropFilterAnimation())
+            ? createDomNodeBasedCompositorElementId(object)
+            : CompositorElementId();
+
     if (effectNodeNeeded) {
       auto& properties = object.getMutableForPainting().ensurePaintProperties();
       context.forceSubtreeUpdate |= properties.updateEffect(
           context.currentEffect, context.current.transform, outputClip,
-          std::move(filter), opacity, blendMode, compositingReasons);
+          std::move(filter), opacity, blendMode, compositingReasons,
+          compositorElementId);
     } else {
       if (auto* properties = object.getMutableForPainting().paintProperties())
         context.forceSubtreeUpdate |= properties->clearEffect();
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index 46aa009..e7480ff 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -57,6 +57,11 @@
   return object->paintProperties()->localBorderBoxProperties()->paintOffset;
 }
 
+const ObjectPaintProperties*
+PaintPropertyTreeBuilderTest::paintPropertiesForElement(const char* name) {
+  return document().getElementById(name)->layoutObject()->paintProperties();
+}
+
 void PaintPropertyTreeBuilderTest::SetUp() {
   Settings::setMockScrollbarsEnabled(true);
 
@@ -421,60 +426,24 @@
 
 TEST_P(PaintPropertyTreeBuilderTest,
        TransformNodeWithActiveAnimationHasDirectCompositingReason) {
-  setBodyInnerHTML(
-      "<style>"
-      "@keyframes test {"
-      "  0% { transform: translate(1em, 1em) } "
-      "  100% { transform: translate(2em, 2em) } "
-      "} "
-      ".animate { "
-      "  animation-name: test; "
-      "  animation-duration: 1s "
-      "}"
-      "</style>"
-      "<div id='target' class='animate'></div>");
-  Element* target = document().getElementById("target");
-  const ObjectPaintProperties* properties =
-      target->layoutObject()->paintProperties();
-  EXPECT_TRUE(properties->transform()->hasDirectCompositingReasons());
+  loadTestData("transform-animation.html");
+  EXPECT_TRUE(paintPropertiesForElement("target")
+                  ->transform()
+                  ->hasDirectCompositingReasons());
 }
 
-namespace {
-
-const char* kSimpleOpacityExampleHTML =
-    "<style>"
-    "div {"
-    "  width: 100px;"
-    "  height: 100px;"
-    "  background-color: red;"
-    "  animation-name: example;"
-    "  animation-duration: 4s;"
-    "}"
-    "@keyframes example {"
-    "  from { opacity: 0.0;}"
-    "  to { opacity: 1.0;}"
-    "}"
-    "</style>"
-    "<div id='target'></div>";
-
-}  // namespace
-
 TEST_P(PaintPropertyTreeBuilderTest,
        OpacityAnimationDoesNotCreateTransformNode) {
-  setBodyInnerHTML(kSimpleOpacityExampleHTML);
-  Element* target = document().getElementById("target");
-  const ObjectPaintProperties* properties =
-      target->layoutObject()->paintProperties();
-  EXPECT_EQ(nullptr, properties->transform());
+  loadTestData("opacity-animation.html");
+  EXPECT_EQ(nullptr, paintPropertiesForElement("target")->transform());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest,
        EffectNodeWithActiveAnimationHasDirectCompositingReason) {
-  setBodyInnerHTML(kSimpleOpacityExampleHTML);
-  Element* target = document().getElementById("target");
-  const ObjectPaintProperties* properties =
-      target->layoutObject()->paintProperties();
-  EXPECT_TRUE(properties->effect()->hasDirectCompositingReasons());
+  loadTestData("opacity-animation.html");
+  EXPECT_TRUE(paintPropertiesForElement("target")
+                  ->effect()
+                  ->hasDirectCompositingReasons());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, WillChangeTransform) {
@@ -3194,4 +3163,37 @@
                 ->propertyTreeState.clip());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest,
+       TransformNodeNotAnimatedHasNoCompositorElementId) {
+  setBodyInnerHTML("<div id='target' style='transform: translateX(2em)'></div");
+  const ObjectPaintProperties* properties = paintPropertiesForElement("target");
+  EXPECT_TRUE(properties->transform());
+  EXPECT_EQ(CompositorElementId(),
+            properties->transform()->compositorElementId());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest,
+       EffectNodeNotAnimatedHasNoCompositorElementId) {
+  setBodyInnerHTML("<div id='target' style='opacity: 0.5'></div");
+  const ObjectPaintProperties* properties = paintPropertiesForElement("target");
+  EXPECT_TRUE(properties->effect());
+  EXPECT_EQ(CompositorElementId(), properties->effect()->compositorElementId());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest,
+       TransformNodeAnimatedHasCompositorElementId) {
+  loadTestData("transform-animation.html");
+  const ObjectPaintProperties* properties = paintPropertiesForElement("target");
+  EXPECT_TRUE(properties->transform());
+  EXPECT_NE(CompositorElementId(),
+            properties->transform()->compositorElementId());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, EffectNodeAnimatedHasCompositorElementId) {
+  loadTestData("opacity-animation.html");
+  const ObjectPaintProperties* properties = paintPropertiesForElement("target");
+  EXPECT_TRUE(properties->effect());
+  EXPECT_NE(CompositorElementId(), properties->effect()->compositorElementId());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.h b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.h
index 1f936e7..6c023f6 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.h
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.h
@@ -40,6 +40,8 @@
   // ObjectPaintProperties::localBorderBoxProperties().
   LayoutPoint paintOffset(const LayoutObject*);
 
+  const ObjectPaintProperties* paintPropertiesForElement(const char* name);
+
  private:
   void SetUp() override;
   void TearDown() override;
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
index 4401e5f..a2a0a83 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
@@ -445,18 +445,7 @@
 
 TEST_P(PaintPropertyTreeUpdateTest,
        TransformNodeWithAnimationLosesNodeWhenAnimationRemoved) {
-  setBodyInnerHTML(
-      "<style>"
-      "@keyframes test {"
-      "  0% { transform: translate(1em, 1em) } "
-      "  100% { transform: translate(2em, 2em) } "
-      "} "
-      ".animate { "
-      "  animation-name: test; "
-      "  animation-duration: 1s "
-      "}"
-      "</style>"
-      "<div id='target' class='animate'></div>");
+  loadTestData("transform-animation.html");
   Element* target = document().getElementById("target");
   const ObjectPaintProperties* properties =
       target->layoutObject()->paintProperties();
@@ -470,23 +459,7 @@
 
 TEST_P(PaintPropertyTreeUpdateTest,
        EffectNodeWithAnimationLosesNodeWhenAnimationRemoved) {
-  setBodyInnerHTML(
-      "<style>"
-      "div {"
-      "  width: 100px;"
-      "  height: 100px;"
-      "  background-color: red;"
-      "} "
-      ".animate {"
-      "  animation-name: test;"
-      "  animation-duration: 4s;"
-      "}"
-      "@keyframes test {"
-      "  from { opacity: 0.0;}"
-      "  to { opacity: 1.0;}"
-      "}"
-      "</style>"
-      "<div id='target' class='animate'></div>");
+  loadTestData("opacity-animation.html");
   Element* target = document().getElementById("target");
   const ObjectPaintProperties* properties =
       target->layoutObject()->paintProperties();
@@ -498,6 +471,43 @@
   EXPECT_EQ(nullptr, properties->effect());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest,
+       TransformNodeLosesCompositorElementIdWhenAnimationRemoved) {
+  loadTestData("transform-animation.html");
+
+  Element* target = document().getElementById("target");
+  target->setAttribute(HTMLNames::styleAttr, "transform: translateX(2em)");
+  document().view()->updateAllLifecyclePhases();
+
+  const ObjectPaintProperties* properties =
+      target->layoutObject()->paintProperties();
+  EXPECT_NE(CompositorElementId(),
+            properties->transform()->compositorElementId());
+
+  // Remove the animation but keep the transform on the element.
+  target->removeAttribute(HTMLNames::classAttr);
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(CompositorElementId(),
+            properties->transform()->compositorElementId());
+}
+
+TEST_P(PaintPropertyTreeUpdateTest,
+       EffectNodeLosesCompositorElementIdWhenAnimationRemoved) {
+  loadTestData("opacity-animation.html");
+
+  Element* target = document().getElementById("target");
+  target->setAttribute(HTMLNames::styleAttr, "opacity: 0.2");
+  document().view()->updateAllLifecyclePhases();
+
+  const ObjectPaintProperties* properties =
+      target->layoutObject()->paintProperties();
+  EXPECT_NE(CompositorElementId(), properties->effect()->compositorElementId());
+
+  target->removeAttribute(HTMLNames::classAttr);
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(CompositorElementId(), properties->effect()->compositorElementId());
+}
+
 TEST_P(PaintPropertyTreeUpdateTest, PerspectiveOriginUpdatesOnSizeChanges) {
   setBodyInnerHTML(
       "<style>"
diff --git a/third_party/WebKit/Source/core/paint/test_data/opacity-animation.html b/third_party/WebKit/Source/core/paint/test_data/opacity-animation.html
new file mode 100644
index 0000000..0cfdd94
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/test_data/opacity-animation.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+.animate {
+  animation-name: test;
+  animation-duration: 4s;
+}
+@keyframes test {
+  from { opacity: 0.0;}
+  to { opacity: 1.0;}
+}
+</style>
+<div id="target" class="animate"></div>;
diff --git a/third_party/WebKit/Source/core/paint/test_data/transform-animation.html b/third_party/WebKit/Source/core/paint/test_data/transform-animation.html
new file mode 100644
index 0000000..600e840
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/test_data/transform-animation.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+@keyframes test {
+  0% { transform: translate(1em, 1em) }
+  100% { transform: translate(2em, 2em) }
+}
+
+.animate {
+  animation-name: test;
+  animation-duration: 1s;
+}
+</style>
+<div id="target" class="animate"></div>;
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index ded653baa..7336342 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -1241,7 +1241,7 @@
       return;
 
     if (this.propertiesTreeOutline.element.shadowRoot.firstChild &&
-        this.propertiesTreeOutline.element.shadowRoot.firstChild.isComponentSelectionCollapsed())
+        !this.propertiesTreeOutline.element.shadowRoot.firstChild.isComponentSelectionCollapsed())
       return;
 
     if (this._checkWillCancelEditing())
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js b/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js
index 6635829..b80e963e 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js
@@ -389,7 +389,7 @@
       oldElement = this._itemToElement.get(oldItem) || null;
     this._selectedIndex = index;
     var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null;
-    var newElement = this._itemToElement.get(newItem) || null;
+    var newElement = this._selectedIndex !== -1 ? this._elementAtIndex(index) : null;
     this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement);
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
index ee250df..38c08d6 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
@@ -67,11 +67,6 @@
     this._element.classList.add('suggest-box');
     this._container.appendChild(this._element);
     this._element.addEventListener('mousedown', this._onBoxMouseDown.bind(this), true);
-    this._detailsPopup = this._container.createChild('div', 'suggest-box details-popup monospace');
-    this._detailsPopup.classList.add('hidden');
-    this._asyncDetailsCallback = null;
-    /** @type {!Map<!UI.SuggestBox.Suggestion, !Promise<{detail: string, description: string}>>} */
-    this._asyncDetailsPromises = new Map();
     this._userInteracted = false;
     this._captureEnter = captureEnter;
     this._viewportWidth = '100vw';
@@ -295,11 +290,6 @@
       toElement.classList.add('selected', 'force-white-icons');
     if (!to)
       return;
-    this._detailsPopup.classList.add('hidden');
-    this._asyncDetails(to).then(details => {
-      if (this._list.selectedItem() === to)
-        this._showDetailsPopup(details);
-    });
     this._applySuggestion(true);
   }
 
@@ -315,30 +305,6 @@
   }
 
   /**
-   * @param {!UI.SuggestBox.Suggestion} item
-   * @return {!Promise<?{detail: string, description: string}>}
-   */
-  _asyncDetails(item) {
-    if (!this._asyncDetailsCallback)
-      return Promise.resolve(/** @type {?{description: string, detail: string}} */ (null));
-    if (!this._asyncDetailsPromises.has(item))
-      this._asyncDetailsPromises.set(item, this._asyncDetailsCallback(item));
-    return /** @type {!Promise<?{detail: string, description: string}>} */ (this._asyncDetailsPromises.get(item));
-  }
-
-  /**
-   * @param {?{detail: string, description: string}} details
-   */
-  _showDetailsPopup(details) {
-    this._detailsPopup.removeChildren();
-    if (!details)
-      return;
-    this._detailsPopup.createChild('section', 'detail').createTextChild(details.detail);
-    this._detailsPopup.createChild('section', 'description').createTextChild(details.description);
-    this._detailsPopup.classList.remove('hidden');
-  }
-
-  /**
    * @param {!UI.SuggestBox.Suggestions} completions
    * @param {boolean} canShowForSingleItem
    * @param {string} userEnteredText
@@ -364,22 +330,10 @@
    * @param {boolean} selectHighestPriority
    * @param {boolean} canShowForSingleItem
    * @param {string} userEnteredText
-   * @param {function(number): !Promise<{detail:string, description:string}>=} asyncDetails
    */
-  updateSuggestions(
-      anchorBox,
-      completions,
-      selectHighestPriority,
-      canShowForSingleItem,
-      userEnteredText,
-      asyncDetails) {
+  updateSuggestions(anchorBox, completions, selectHighestPriority, canShowForSingleItem, userEnteredText) {
     delete this._onlyCompletion;
     if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) {
-      this._asyncDetailsPromises.clear();
-      if (asyncDetails)
-        this._asyncDetailsCallback = item => asyncDetails(completions.indexOf(item));
-      else
-        this._asyncDetailsCallback = null;
       this._userEnteredText = userEnteredText;
 
       this._show();
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css b/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css
index 7d46270..fc0359d 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css
@@ -123,16 +123,3 @@
 .suggest-box .suggest-box-content-item:hover:not(.selected) {
     background-color: rgba(56, 121, 217, 0.1);
 }
-
-.suggest-box .details-popup {
-    padding: 17px;
-    pointer-events: auto;
-    margin-left: 3px;
-    max-width: 750px;
-    word-wrap: normal;
-}
-
-.suggest-box .details-popup .description {
-    margin-top: 22px;
-    color: #808080;
-}
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.cpp
index 63d3283..14a9734 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.cpp
@@ -16,7 +16,7 @@
     : m_device(device) {}
 
 BluetoothRemoteGATTService*
-BluetoothAttributeInstanceMap::getOrCreateBluetoothRemoteGATTService(
+BluetoothAttributeInstanceMap::getOrCreateRemoteGATTService(
     const String& serviceInstanceId,
     const String& uuid,
     bool isPrimary,
@@ -39,7 +39,7 @@
 }
 
 BluetoothRemoteGATTCharacteristic*
-BluetoothAttributeInstanceMap::getOrCreateBluetoothRemoteGATTCharacteristic(
+BluetoothAttributeInstanceMap::getOrCreateRemoteGATTCharacteristic(
     ExecutionContext* context,
     const String& characteristicInstanceId,
     const String& serviceInstanceId,
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h
index 50ba595..0fb529c 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h
@@ -29,7 +29,7 @@
   // no service with the same instance id and adds it to the map.
   // Otherwise returns the BluetoothRemoteGATTService object already
   // in the map.
-  BluetoothRemoteGATTService* getOrCreateBluetoothRemoteGATTService(
+  BluetoothRemoteGATTService* getOrCreateRemoteGATTService(
       const String& serviceInstanceId,
       const String& uuid,
       bool isPrimary,
@@ -43,8 +43,7 @@
   // characteristic with the same instance id and adds it to the map.
   // Otherwise returns the BluetoothRemoteGATTCharacteristic object already in
   // the map.
-  BluetoothRemoteGATTCharacteristic*
-  getOrCreateBluetoothRemoteGATTCharacteristic(
+  BluetoothRemoteGATTCharacteristic* getOrCreateRemoteGATTCharacteristic(
       ExecutionContext*,
       const String& characteristicInstanceId,
       const String& serviceInstanceId,
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
index 8ea588a4..1b0fb018 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
@@ -46,13 +46,12 @@
   return result;
 }
 
-BluetoothRemoteGATTService*
-BluetoothDevice::getOrCreateBluetoothRemoteGATTService(
+BluetoothRemoteGATTService* BluetoothDevice::getOrCreateRemoteGATTService(
     const String& serviceInstanceId,
     const String& uuid,
     bool isPrimary,
     const String& deviceInstanceId) {
-  return m_attributeInstanceMap->getOrCreateBluetoothRemoteGATTService(
+  return m_attributeInstanceMap->getOrCreateRemoteGATTService(
       serviceInstanceId, uuid, isPrimary, deviceInstanceId);
 }
 
@@ -61,14 +60,14 @@
 }
 
 BluetoothRemoteGATTCharacteristic*
-BluetoothDevice::getOrCreateBluetoothRemoteGATTCharacteristic(
+BluetoothDevice::getOrCreateRemoteGATTCharacteristic(
     ExecutionContext* context,
     const String& characteristicInstanceId,
     const String& serviceInstanceId,
     const String& uuid,
     uint32_t characteristicProperties,
     BluetoothRemoteGATTService* service) {
-  return m_attributeInstanceMap->getOrCreateBluetoothRemoteGATTCharacteristic(
+  return m_attributeInstanceMap->getOrCreateRemoteGATTCharacteristic(
       context, characteristicInstanceId, serviceInstanceId, uuid,
       characteristicProperties, service);
 }
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
index 7cae3ba..61f97528 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
@@ -50,15 +50,14 @@
   static mojom::blink::WebBluetoothDeviceIdPtr createMojoDeviceId(
       const String& deviceId);
 
-  BluetoothRemoteGATTService* getOrCreateBluetoothRemoteGATTService(
+  BluetoothRemoteGATTService* getOrCreateRemoteGATTService(
       const String& serviceInstanceId,
       const String& uuid,
       bool isPrimary,
       const String& deviceInstanceId);
   bool isValidService(const String& serviceInstanceId);
 
-  BluetoothRemoteGATTCharacteristic*
-  getOrCreateBluetoothRemoteGATTCharacteristic(
+  BluetoothRemoteGATTCharacteristic* getOrCreateRemoteGATTCharacteristic(
       ExecutionContext*,
       const String& characteristicInstanceId,
       const String& serviceInstanceId,
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
index 0b5bf816..5ac6a05 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
@@ -123,7 +123,7 @@
 
     if (quantity == mojom::blink::WebBluetoothGATTQueryQuantity::SINGLE) {
       DCHECK_EQ(1u, services->size());
-      resolver->resolve(m_device->getOrCreateBluetoothRemoteGATTService(
+      resolver->resolve(m_device->getOrCreateRemoteGATTService(
           services.value()[0]->instance_id, services.value()[0]->uuid,
           true /* isPrimary */, device()->id()));
       return;
@@ -133,7 +133,7 @@
     gattServices.reserveInitialCapacity(services->size());
 
     for (const auto& service : services.value()) {
-      gattServices.append(m_device->getOrCreateBluetoothRemoteGATTService(
+      gattServices.append(m_device->getOrCreateRemoteGATTService(
           service->instance_id, service->uuid, true /* isPrimary */,
           device()->id()));
     }
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
index 729c1a6..d73dd6b 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
@@ -73,7 +73,7 @@
 
     if (quantity == mojom::blink::WebBluetoothGATTQueryQuantity::SINGLE) {
       DCHECK_EQ(1u, characteristics->size());
-      resolver->resolve(device()->getOrCreateBluetoothRemoteGATTCharacteristic(
+      resolver->resolve(device()->getOrCreateRemoteGATTCharacteristic(
           resolver->getExecutionContext(),
           characteristics.value()[0]->instance_id, serviceInstanceId,
           characteristics.value()[0]->uuid,
@@ -84,11 +84,10 @@
     HeapVector<Member<BluetoothRemoteGATTCharacteristic>> gattCharacteristics;
     gattCharacteristics.reserveInitialCapacity(characteristics->size());
     for (const auto& characteristic : characteristics.value()) {
-      gattCharacteristics.append(
-          device()->getOrCreateBluetoothRemoteGATTCharacteristic(
-              resolver->getExecutionContext(), characteristic->instance_id,
-              serviceInstanceId, characteristic->uuid,
-              characteristic->properties, this));
+      gattCharacteristics.append(device()->getOrCreateRemoteGATTCharacteristic(
+          resolver->getExecutionContext(), characteristic->instance_id,
+          serviceInstanceId, characteristic->uuid, characteristic->properties,
+          this));
     }
     resolver->resolve(gattCharacteristics);
   } else {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp
index 85a7293..38d423b 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp
@@ -26,11 +26,12 @@
 String EffectPaintPropertyNode::toString() const {
   return String::format(
       "parent=%p localTransformSpace=%p outputClip=%p opacity=%f filter=%s "
-      "blendMode=%s directCompositingReasons=%s",
+      "blendMode=%s directCompositingReasons=%s compositorElementId=(%d, %d)",
       m_parent.get(), m_localTransformSpace.get(), m_outputClip.get(),
       m_opacity, m_filter.toString().ascii().data(),
       SkBlendMode_Name(m_blendMode),
-      compositingReasonsAsString(m_directCompositingReasons).ascii().data());
+      compositingReasonsAsString(m_directCompositingReasons).ascii().data(),
+      m_compositorElementId.primaryId, m_compositorElementId.secondaryId);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
index ec1a562..2dd0c71 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
@@ -7,6 +7,7 @@
 
 #include "cc/layers/layer.h"
 #include "platform/PlatformExport.h"
+#include "platform/graphics/CompositorElementId.h"
 #include "platform/graphics/CompositorFilterOperations.h"
 #include "platform/graphics/paint/ClipPaintPropertyNode.h"
 #include "platform/graphics/paint/TransformPaintPropertyNode.h"
@@ -36,11 +37,12 @@
       CompositorFilterOperations filter,
       float opacity,
       SkBlendMode blendMode,
-      CompositingReasons directCompositingReasons = CompositingReasonNone) {
+      CompositingReasons directCompositingReasons = CompositingReasonNone,
+      const CompositorElementId& compositorElementId = CompositorElementId()) {
     return adoptRef(new EffectPaintPropertyNode(
         std::move(parent), std::move(localTransformSpace),
         std::move(outputClip), std::move(filter), opacity, blendMode,
-        directCompositingReasons));
+        directCompositingReasons, compositorElementId));
   }
 
   void update(
@@ -50,7 +52,8 @@
       CompositorFilterOperations filter,
       float opacity,
       SkBlendMode blendMode,
-      CompositingReasons directCompositingReasons = CompositingReasonNone) {
+      CompositingReasons directCompositingReasons = CompositingReasonNone,
+      CompositorElementId compositorElementId = CompositorElementId()) {
     DCHECK(!isRoot());
     DCHECK(parent != this);
     m_parent = parent;
@@ -60,6 +63,7 @@
     m_opacity = opacity;
     m_blendMode = blendMode;
     m_directCompositingReasons = directCompositingReasons;
+    m_compositorElementId = compositorElementId;
   }
 
   const TransformPaintPropertyNode* localTransformSpace() const {
@@ -83,7 +87,7 @@
   PassRefPtr<EffectPaintPropertyNode> clone() const {
     return adoptRef(new EffectPaintPropertyNode(
         m_parent, m_localTransformSpace, m_outputClip, m_filter, m_opacity,
-        m_blendMode, m_directCompositingReasons));
+        m_blendMode, m_directCompositingReasons, m_compositorElementId));
   }
 
   // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
@@ -95,7 +99,8 @@
            m_outputClip == o.m_outputClip &&
            m_filter.equalsIgnoringReferenceFilters(o.m_filter) &&
            m_opacity == o.m_opacity && m_blendMode == o.m_blendMode &&
-           m_directCompositingReasons == o.m_directCompositingReasons;
+           m_directCompositingReasons == o.m_directCompositingReasons &&
+           m_compositorElementId == o.m_compositorElementId;
   }
 #endif
 
@@ -105,6 +110,10 @@
     return m_directCompositingReasons != CompositingReasonNone;
   }
 
+  const CompositorElementId& compositorElementId() const {
+    return m_compositorElementId;
+  }
+
  private:
   EffectPaintPropertyNode(
       PassRefPtr<const EffectPaintPropertyNode> parent,
@@ -113,14 +122,16 @@
       CompositorFilterOperations filter,
       float opacity,
       SkBlendMode blendMode,
-      CompositingReasons directCompositingReasons)
+      CompositingReasons directCompositingReasons,
+      CompositorElementId compositorElementId)
       : m_parent(parent),
         m_localTransformSpace(localTransformSpace),
         m_outputClip(outputClip),
         m_filter(std::move(filter)),
         m_opacity(opacity),
         m_blendMode(blendMode),
-        m_directCompositingReasons(directCompositingReasons) {}
+        m_directCompositingReasons(directCompositingReasons),
+        m_compositorElementId(compositorElementId) {}
 
   RefPtr<const EffectPaintPropertyNode> m_parent;
   // The local transform space serves two purposes:
@@ -149,6 +160,7 @@
   mutable scoped_refptr<cc::Layer> m_dummyLayer;
 
   CompositingReasons m_directCompositingReasons;
+  CompositorElementId m_compositorElementId;
 };
 
 // Redeclared here to avoid ODR issues.
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp
index e7162129..70cbe4c0 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp
@@ -16,11 +16,14 @@
 String TransformPaintPropertyNode::toString() const {
   return String::format(
       "parent=%p transform=%s origin=%s flattensInheritedTransform=%s "
-      "renderContextID=%x directCompositingReasons=%s",
+      "renderingContextId=%x directCompositingReasons=%s "
+      "compositorElementId=(%d, "
+      "%d)",
       m_parent.get(), m_matrix.toString().ascii().data(),
       m_origin.toString().ascii().data(),
       m_flattensInheritedTransform ? "yes" : "no", m_renderingContextId,
-      compositingReasonsAsString(m_directCompositingReasons).ascii().data());
+      compositingReasonsAsString(m_directCompositingReasons).ascii().data(),
+      m_compositorElementId.primaryId, m_compositorElementId.secondaryId);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
index be4c3667..d159197 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
@@ -8,6 +8,7 @@
 #include "platform/PlatformExport.h"
 #include "platform/geometry/FloatPoint3D.h"
 #include "platform/graphics/CompositingReasons.h"
+#include "platform/graphics/CompositorElementId.h"
 #include "platform/transforms/TransformationMatrix.h"
 #include "wtf/PassRefPtr.h"
 #include "wtf/RefCounted.h"
@@ -34,10 +35,11 @@
       const FloatPoint3D& origin,
       bool flattensInheritedTransform = false,
       unsigned renderingContextId = 0,
-      CompositingReasons directCompositingReasons = CompositingReasonNone) {
+      CompositingReasons directCompositingReasons = CompositingReasonNone,
+      const CompositorElementId& compositorElementId = CompositorElementId()) {
     return adoptRef(new TransformPaintPropertyNode(
         std::move(parent), matrix, origin, flattensInheritedTransform,
-        renderingContextId, directCompositingReasons));
+        renderingContextId, directCompositingReasons, compositorElementId));
   }
 
   void update(
@@ -46,7 +48,8 @@
       const FloatPoint3D& origin,
       bool flattensInheritedTransform = false,
       unsigned renderingContextId = 0,
-      CompositingReasons directCompositingReasons = CompositingReasonNone) {
+      CompositingReasons directCompositingReasons = CompositingReasonNone,
+      CompositorElementId compositorElementId = CompositorElementId()) {
     DCHECK(!isRoot());
     DCHECK(parent != this);
     m_parent = parent;
@@ -55,6 +58,7 @@
     m_flattensInheritedTransform = flattensInheritedTransform;
     m_renderingContextId = renderingContextId;
     m_directCompositingReasons = directCompositingReasons;
+    m_compositorElementId = compositorElementId;
   }
 
   const TransformationMatrix& matrix() const { return m_matrix; }
@@ -76,6 +80,10 @@
     return m_directCompositingReasons != CompositingReasonNone;
   }
 
+  const CompositorElementId& compositorElementId() const {
+    return m_compositorElementId;
+  }
+
   // Content whose transform nodes have a common rendering context ID are 3D
   // sorted. If this is 0, content will not be 3D sorted.
   unsigned renderingContextId() const { return m_renderingContextId; }
@@ -87,7 +95,8 @@
   PassRefPtr<TransformPaintPropertyNode> clone() const {
     return adoptRef(new TransformPaintPropertyNode(
         m_parent, m_matrix, m_origin, m_flattensInheritedTransform,
-        m_renderingContextId, m_directCompositingReasons));
+        m_renderingContextId, m_directCompositingReasons,
+        m_compositorElementId));
   }
 
   // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
@@ -97,7 +106,8 @@
            m_origin == o.m_origin &&
            m_flattensInheritedTransform == o.m_flattensInheritedTransform &&
            m_renderingContextId == o.m_renderingContextId &&
-           m_directCompositingReasons == o.m_directCompositingReasons;
+           m_directCompositingReasons == o.m_directCompositingReasons &&
+           m_compositorElementId == o.m_compositorElementId;
   }
 #endif
 
@@ -110,13 +120,15 @@
       const FloatPoint3D& origin,
       bool flattensInheritedTransform,
       unsigned renderingContextId,
-      CompositingReasons directCompositingReasons)
+      CompositingReasons directCompositingReasons,
+      CompositorElementId compositorElementId)
       : m_parent(parent),
         m_matrix(matrix),
         m_origin(origin),
         m_flattensInheritedTransform(flattensInheritedTransform),
         m_renderingContextId(renderingContextId),
-        m_directCompositingReasons(directCompositingReasons) {}
+        m_directCompositingReasons(directCompositingReasons),
+        m_compositorElementId(compositorElementId) {}
 
   RefPtr<const TransformPaintPropertyNode> m_parent;
   TransformationMatrix m_matrix;
@@ -124,6 +136,7 @@
   bool m_flattensInheritedTransform;
   unsigned m_renderingContextId;
   CompositingReasons m_directCompositingReasons;
+  CompositorElementId m_compositorElementId;
 };
 
 // Redeclared here to avoid ODR issues.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py
index ee18001..d67aef8d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py
@@ -35,17 +35,6 @@
 _log = logging.getLogger(__name__)
 
 
-# FIXME: Should this function be somewhere more general?
-def _invert_dictionary(dictionary):
-    inverted_dictionary = {}
-    for key, value in dictionary.items():
-        if inverted_dictionary.get(value):
-            inverted_dictionary[value].append(key)
-        else:
-            inverted_dictionary[value] = [key]
-    return inverted_dictionary
-
-
 class BaselineOptimizer(object):
     ROOT_LAYOUT_TESTS_DIRECTORY = 'LayoutTests'
 
diff --git a/third_party/closure_compiler/externs/quick_unlock_private.js b/third_party/closure_compiler/externs/quick_unlock_private.js
index aa6f6d2b..2b97520 100644
--- a/third_party/closure_compiler/externs/quick_unlock_private.js
+++ b/third_party/closure_compiler/externs/quick_unlock_private.js
@@ -25,6 +25,35 @@
 };
 
 /**
+ * @enum {string}
+ * @see https://developer.chrome.com/extensions/quickUnlockPrivate#type-CredentialProblem
+ */
+chrome.quickUnlockPrivate.CredentialProblem = {
+  TOO_SHORT: 'TOO_SHORT',
+  TOO_LONG: 'TOO_LONG',
+  TOO_WEAK: 'TOO_WEAK',
+  CONTAINS_NONDIGIT: 'CONTAINS_NONDIGIT',
+};
+
+/**
+ * @typedef {{
+ *   errors: !Array<!chrome.quickUnlockPrivate.CredentialProblem>,
+ *   warnings: !Array<!chrome.quickUnlockPrivate.CredentialProblem>
+ * }}
+ * @see https://developer.chrome.com/extensions/quickUnlockPrivate#type-CredentialCheck
+ */
+chrome.quickUnlockPrivate.CredentialCheck;
+
+/**
+ * @typedef {{
+ *   minLength: number,
+ *   maxLength: number
+ * }}
+ * @see https://developer.chrome.com/extensions/quickUnlockPrivate#type-CredentialRequirements
+ */
+chrome.quickUnlockPrivate.CredentialRequirements;
+
+/**
  * Returns the set of quick unlock modes that are available for the user to use.
  * Some quick unlock modes may be disabled by policy.
  * @param {function(!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>):void}
@@ -43,6 +72,30 @@
 chrome.quickUnlockPrivate.getActiveModes = function(onComplete) {};
 
 /**
+ * Checks if the given credential can be used for the given unlock mode.
+ * Enterprise policy can change credential requirements.
+ * @param {!chrome.quickUnlockPrivate.QuickUnlockMode} mode The quick unlock
+ *     mode that is used.
+ * @param {string} credential The given credential.
+ * @param {function(!chrome.quickUnlockPrivate.CredentialCheck):void} onComplete
+ *     Called with a list of warnings and errors the given     |credential| has
+ *     (or an empty list if there are none).
+ * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-checkCredential
+ */
+chrome.quickUnlockPrivate.checkCredential = function(mode, credential, onComplete) {};
+
+/**
+ * Gets the credential requirements for the given unlock mode.
+ * @param {!chrome.quickUnlockPrivate.QuickUnlockMode} mode The quick unlock
+ *     mode that is used.
+ * @param {function(!chrome.quickUnlockPrivate.CredentialRequirements):void}
+ *     onComplete Called with the credential requirements of the given
+ *     |mode|.
+ * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-getCredentialRequirements
+ */
+chrome.quickUnlockPrivate.getCredentialRequirements = function(mode, onComplete) {};
+
+/**
  * Update the set of quick unlock modes that are currently active/enabled.
  * @param {string} accountPassword The password associated with the account
  *     (e.g. the GAIA password). This is required to change the quick unlock
diff --git a/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js b/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js
index 5fe9756e..93319196 100644
--- a/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js
+++ b/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js
@@ -32,6 +32,30 @@
   getActiveModes: assertNotReached,
 
   /**
+   * Checks if the given credential can be used for the given unlock mode.
+   * Enterprise policy can change credential requirements.
+   * @param {!chrome.quickUnlockPrivate.QuickUnlockMode} mode The quick unlock
+   *     mode that is used.
+   * @param {string} credential The given credential.
+   * @param {function(!chrome.quickUnlockPrivate.CredentialCheck):void}
+   *     onComplete Called with a list of warnings and errors the given
+   *     |credential| has (or an empty list if there are none).
+   * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-checkCredential
+   */
+  checkCredential: assertNotReached,
+
+  /**
+   * Gets the credential requirements for the given unlock mode.
+   * @param {!chrome.quickUnlockPrivate.QuickUnlockMode} mode The quick unlock
+   *     mode that is used.
+   * @param {function(!chrome.quickUnlockPrivate.CredentialRequirements):void}
+   *     onComplete Called with the credential requirements of the given
+   *     |mode|.
+   * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-getCredentialRequirements
+   */
+  getCredentialRequirements: assertNotReached,
+
+  /**
    * Update the set of quick unlock modes that are currently active/enabled.
    * @param {string} accountPassword The password associated with the account
    *     (e.g. the GAIA password). This is required to change the quick unlock
diff --git a/third_party/retrolambda/LICENSE.txt b/third_party/retrolambda/LICENSE
similarity index 100%
rename from third_party/retrolambda/LICENSE.txt
rename to third_party/retrolambda/LICENSE
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index b62abbd..034a112 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -3967,7 +3967,7 @@
   This addition happens in a second phase once a target and all of its
   dependencies have been resolved. Therefore, a target will not see these
   force-added configs in their "configs" variable while the script is running,
-  and then can not be removed. As a result, this capability should generally
+  and they can not be removed. As a result, this capability should generally
   only be used to add defines and include directories necessary to compile a
   target's headers.
 
@@ -5434,7 +5434,7 @@
   This addition happens in a second phase once a target and all of its
   dependencies have been resolved. Therefore, a target will not see these
   force-added configs in their "configs" variable while the script is running,
-  and then can not be removed. As a result, this capability should generally
+  and they can not be removed. As a result, this capability should generally
   only be used to add defines and include directories necessary to compile a
   target's headers.
 
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index d387baeb..3b60c90 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -416,7 +416,7 @@
   This addition happens in a second phase once a target and all of its
   dependencies have been resolved. Therefore, a target will not see these
   force-added configs in their "configs" variable while the script is running,
-  and then can not be removed. As a result, this capability should generally
+  and they can not be removed. As a result, this capability should generally
   only be used to add defines and include directories necessary to compile a
   target's headers.
 
@@ -1575,7 +1575,7 @@
   This addition happens in a second phase once a target and all of its
   dependencies have been resolved. Therefore, a target will not see these
   force-added configs in their "configs" variable while the script is running,
-  and then can not be removed. As a result, this capability should generally
+  and they can not be removed. As a result, this capability should generally
   only be used to add defines and include directories necessary to compile a
   target's headers.
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index e2aa4a44..72d028f 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -13162,6 +13162,16 @@
   </description>
 </action>
 
+<action name="PhysicalWeb.Prefs.FromOmnibox">
+  <owner>cco3@chromium.org</owner>
+  <owner>mattreynolds@chromium.org</owner>
+  <owner>mmocny@chromium.org</owner>
+  <description>
+    The user tapped the Physical Web logo in an omnibox suggestion and navigated
+    to the Physical Web preferences page. iOS only.
+  </description>
+</action>
+
 <action name="PhysicalWeb.Prefs.LocationDenied">
   <owner>cco3@chromium.org</owner>
   <owner>mattreynolds@chromium.org</owner>
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 1de26fd7..8deaa6e 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -14,26 +14,6 @@
 
 #include "ui/display/util/edid_parser.h"
 
-#if !defined(DRM_MODE_CONNECTOR_DSI)
-#define DRM_MODE_CONNECTOR_DSI 16
-#endif
-
-#if !defined(DRM_CAP_CURSOR_WIDTH)
-#define DRM_CAP_CURSOR_WIDTH 0x8
-#endif
-
-#if !defined(DRM_CAP_CURSOR_HEIGHT)
-#define DRM_CAP_CURSOR_HEIGHT 0x9
-#endif
-
-#if !defined(DRM_FORMAT_R8)
-// TODO(dshwang): after most linux and libdrm has this definition, remove it.
-#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ')
-#endif
-#if !defined(DRM_FORMAT_GR88)
-// TODO(dshwang): after most linux and libdrm has this definition, remove it.
-#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8')
-#endif
 #if !defined(DRM_FORMAT_YV12)
 // TODO(dcastagna): after libdrm has this definition, remove it.
 #define DRM_FORMAT_YV12 fourcc_code('Y', 'V', '1', '2')
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index 271d2d9..ef65a29 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -135,7 +135,7 @@
       table_type_(table_type),
       single_selection_(single_selection),
       select_on_remove_(true),
-      table_view_observer_(NULL),
+      observer_(NULL),
       row_height_(font_list_.GetHeight() + kTextVerticalPadding * 2),
       last_parent_width_(0),
       layout_width_(0),
@@ -193,10 +193,6 @@
   return model_ ? model_->RowCount() : 0;
 }
 
-int TableView::SelectedRowCount() {
-  return static_cast<int>(selection_model_.size());
-}
-
 void TableView::Select(int model_row) {
   if (!model_)
     return;
@@ -205,7 +201,7 @@
 }
 
 int TableView::FirstSelectedRow() {
-  return SelectedRowCount() == 0 ? -1 : selection_model_.selected_indices()[0];
+  return selection_model_.empty() ? -1 : selection_model_.selected_indices()[0];
 }
 
 void TableView::SetColumnVisibility(int id, bool is_visible) {
@@ -392,8 +388,8 @@
     default:
       break;
   }
-  if (table_view_observer_)
-    table_view_observer_->OnKeyDown(event.key_code());
+  if (observer_)
+    observer_->OnKeyDown(event.key_code());
   return false;
 }
 
@@ -408,8 +404,8 @@
 
   if (event.GetClickCount() == 2) {
     SelectByViewIndex(row);
-    if (table_view_observer_)
-      table_view_observer_->OnDoubleClick();
+    if (observer_)
+      observer_->OnDoubleClick();
   } else if (event.GetClickCount() == 1) {
     ui::ListSelectionModel new_model;
     ConfigureSelectionModelForEvent(event, &new_model);
@@ -509,8 +505,8 @@
     selection_model_.set_active(FirstSelectedRow());
   if (!selection_model_.empty() && selection_model_.anchor() == -1)
     selection_model_.set_anchor(FirstSelectedRow());
-  if (table_view_observer_)
-    table_view_observer_->OnSelectionChanged();
+  if (observer_)
+    observer_->OnSelectionChanged();
 }
 
 gfx::Point TableView::GetKeyboardContextMenuLocation() {
@@ -846,8 +842,8 @@
     ScrollRectToVisible(vis_rect);
   }
 
-  if (table_view_observer_)
-    table_view_observer_->OnSelectionChanged();
+  if (observer_)
+    observer_->OnSelectionChanged();
 
   NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
 }
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index 079edaad..6d1c184 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -116,10 +116,6 @@
   // Returns the number of rows in the TableView.
   int RowCount() const;
 
-  // Returns the number of selected rows.
-  // TODO(sky): remove this and force callers to use selection_model().
-  int SelectedRowCount();
-
   // Selects the specified item, making sure it's visible.
   void Select(int model_row);
 
@@ -141,11 +137,8 @@
   // or not).
   bool HasColumn(int id) const;
 
-  // TODO(sky): rename to set_observer().
-  void SetObserver(TableViewObserver* observer) {
-    table_view_observer_ = observer;
-  }
-  TableViewObserver* observer() const { return table_view_observer_; }
+  void set_observer(TableViewObserver* observer) { observer_ = observer; }
+  TableViewObserver* observer() const { return observer_; }
 
   const std::vector<VisibleColumn>& visible_columns() const {
     return visible_columns_;
@@ -331,8 +324,7 @@
   // is selected then.
   bool select_on_remove_ = true;
 
-  // TODO(sky): rename to observer_.
-  TableViewObserver* table_view_observer_;
+  TableViewObserver* observer_;
 
   // The selection, in terms of the model.
   ui::ListSelectionModel selection_model_;
diff --git a/ui/views/controls/table/table_view_unittest.cc b/ui/views/controls/table/table_view_unittest.cc
index c94994c..d72345c 100644
--- a/ui/views/controls/table/table_view_unittest.cc
+++ b/ui/views/controls/table/table_view_unittest.cc
@@ -645,7 +645,7 @@
 // Assertions around changing the selection.
 TEST_F(TableViewTest, Selection) {
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
 
   // Initially no selection.
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
@@ -674,7 +674,7 @@
   EXPECT_EQ(0, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
 
-  table_->SetObserver(NULL);
+  table_->set_observer(NULL);
 }
 
 // 0 1 2 3:
@@ -686,7 +686,7 @@
 // remove 0 -> 0 (none selected)
 TEST_F(TableViewTest, SelectionNoSelectOnRemove) {
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
   table_->set_select_on_remove(false);
 
   // Initially no selection.
@@ -724,7 +724,7 @@
   EXPECT_EQ(1, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
 
-  table_->SetObserver(nullptr);
+  table_->set_observer(nullptr);
 }
 
 // Verifies selection works by way of a gesture.
@@ -733,14 +733,14 @@
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
 
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
 
   // Click on the first row, should select it.
   TapOnRow(0);
   EXPECT_EQ(1, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
 
-  table_->SetObserver(NULL);
+  table_->set_observer(NULL);
 }
 
 // Verifies up/down correctly navigates through groups.
@@ -761,7 +761,7 @@
   table_->SetGrouper(&grouper);
 
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
 
   // Initially no selection.
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
@@ -870,7 +870,7 @@
   EXPECT_EQ(0, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
 
-  table_->SetObserver(NULL);
+  table_->set_observer(NULL);
 }
 
 // Verifies home/end do the right thing.
@@ -891,7 +891,7 @@
   table_->SetGrouper(&grouper);
 
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
 
   // Initially no selection.
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
@@ -904,7 +904,7 @@
   EXPECT_EQ(1, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
 
-  table_->SetObserver(NULL);
+  table_->set_observer(NULL);
 }
 
 // Verifies multiple selection gestures work (control-click, shift-click ...).
@@ -928,7 +928,7 @@
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
 
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
 
   // Click on the first row, should select it and the second row.
   ClickOnRow(0, 0);
@@ -960,7 +960,7 @@
   EXPECT_EQ(1, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
 
-  table_->SetObserver(NULL);
+  table_->set_observer(NULL);
 }
 
 // Verifies multiple selection gestures work when sorted.
@@ -993,7 +993,7 @@
   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
 
   TableViewObserverImpl observer;
-  table_->SetObserver(&observer);
+  table_->set_observer(&observer);
 
   // Click on the third row, should select it and the second row.
   ClickOnRow(2, 0);
@@ -1005,7 +1005,7 @@
   EXPECT_EQ(1, observer.GetChangedCountAndClear());
   EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
 
-  table_->SetObserver(NULL);
+  table_->set_observer(NULL);
 }
 
 // Verifies we don't crash after removing the selected row when there is
diff --git a/ui/views/examples/table_example.cc b/ui/views/examples/table_example.cc
index abdfebb..041270a 100644
--- a/ui/views/examples/table_example.cc
+++ b/ui/views/examples/table_example.cc
@@ -70,7 +70,7 @@
   columns.back().alignment = ui::TableColumn::RIGHT;
   table_ = new TableView(this, columns, ICON_AND_TEXT, true);
   table_->SetGrouper(this);
-  table_->SetObserver(this);
+  table_->set_observer(this);
   icon1_.allocN32Pixels(16, 16);
   SkCanvas canvas1(icon1_);
   canvas1.drawColor(SK_ColorRED);
diff --git a/ui/views/view.cc b/ui/views/view.cc
index f28af0c..68c0e0e 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -496,6 +496,10 @@
   SchedulePaint();
 }
 
+View::Views View::GetChildrenInZOrder() {
+  return children_;
+}
+
 // Transformations -------------------------------------------------------------
 
 gfx::Transform View::GetTransform() const {
@@ -954,7 +958,9 @@
 
   // Walk the child Views recursively looking for the View that most
   // tightly encloses the specified point.
-  for (auto* child : base::Reversed(children_)) {
+  View::Views children = GetChildrenInZOrder();
+  DCHECK_EQ(child_count(), static_cast<int>(children.size()));
+  for (auto* child : base::Reversed(children)) {
     if (!child->visible())
       continue;
 
@@ -1473,8 +1479,9 @@
 
 void View::PaintChildren(const ui::PaintContext& context) {
   TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName());
-  internal::ScopedChildrenLock lock(this);
-  for (auto* child : children_) {
+  View::Views children = GetChildrenInZOrder();
+  DCHECK_EQ(child_count(), static_cast<int>(children.size()));
+  for (auto* child : children) {
     if (!child->layer())
       child->Paint(context);
   }
@@ -1628,10 +1635,10 @@
     // Iterate backwards through the children so that a child with a layer
     // which is further to the back is stacked above one which is further to
     // the front.
-    internal::ScopedChildrenLock lock(this);
-    for (auto* child : base::Reversed(children_)) {
+    View::Views children = GetChildrenInZOrder();
+    DCHECK_EQ(child_count(), static_cast<int>(children.size()));
+    for (auto* child : base::Reversed(children))
       child->ReorderChildLayers(parent_layer);
-    }
   }
 }
 
diff --git a/ui/views/view.h b/ui/views/view.h
index ab25806..71b1cd9 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -192,6 +192,8 @@
   void RemoveAllChildViews(bool delete_children);
 
   int child_count() const { return static_cast<int>(children_.size()); }
+  // See also |GetChildrenInZOrder()| below that returns |children_|
+  // in reverse z-order.
   bool has_children() const { return !children_.empty(); }
 
   // Returns the child view at |index|.
@@ -308,6 +310,14 @@
   // Returns whether the view is enabled.
   bool enabled() const { return enabled_; }
 
+  // Returns the child views ordered in reverse z-order. That is, views later in
+  // the returned vector have a higher z-order (are painted later) than those
+  // early in the vector. The returned vector has exactly the same number of
+  // Views as |children_|. The default implementation returns |children_|,
+  // subclass if the paint order should differ from that of |children_|.
+  // This order is taken into account by painting and targeting implementations.
+  virtual View::Views GetChildrenInZOrder();
+
   // Transformations -----------------------------------------------------------
 
   // Methods for setting transformations for a view (e.g. rotation, scaling).
diff --git a/ui/views/view_targeter_delegate.cc b/ui/views/view_targeter_delegate.cc
index b20842f..7347141 100644
--- a/ui/views/view_targeter_delegate.cc
+++ b/ui/views/view_targeter_delegate.cc
@@ -6,6 +6,7 @@
 
 #include <limits.h>
 
+#include "base/containers/adapters.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/views/rect_based_targeting_utils.h"
 #include "ui/views/view.h"
@@ -42,9 +43,9 @@
   // from this function call if point-based targeting were used.
   View* point_view = NULL;
 
-  for (int i = root->child_count() - 1; i >= 0; --i) {
-    View* child = root->child_at(i);
-
+  View::Views children = root->GetChildrenInZOrder();
+  DCHECK_EQ(root->child_count(), static_cast<int>(children.size()));
+  for (auto* child : base::Reversed(children)) {
     if (!child->CanProcessEventsWithinSubtree())
       continue;
 
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 5f42fa7..bb713373 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -4669,6 +4669,67 @@
   EXPECT_FALSE(view.GetWidget());
 }
 
+// A View that keeps the children with a special ID above other children.
+class OrderableView : public View {
+ public:
+  // ID used by the children that are stacked above other children.
+  static constexpr int VIEW_ID_RAISED = 1000;
+
+  OrderableView() : View() {}
+  ~OrderableView() override {}
+
+  View::Views GetChildrenInZOrder() override {
+    View::Views children;
+    // Iterate over regular children and later over the raised children to
+    // create a custom Z-order.
+    for (int i = 0; i < child_count(); ++i) {
+      if (child_at(i)->id() != VIEW_ID_RAISED)
+        children.push_back(child_at(i));
+    }
+    for (int i = 0; i < child_count(); ++i) {
+      if (child_at(i)->id() == VIEW_ID_RAISED)
+        children.push_back(child_at(i));
+    }
+    DCHECK_EQ(child_count(), static_cast<int>(children.size()));
+    return children;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OrderableView);
+};
+
+TEST_F(ViewTest, ChildViewZOrderChanged) {
+  const int kChildrenCount = 4;
+  std::unique_ptr<View> view(new OrderableView());
+  view->SetPaintToLayer(true);
+  for (int i = 0; i < kChildrenCount; ++i)
+    AddViewWithChildLayer(view.get());
+  View::Views children = view->GetChildrenInZOrder();
+  const std::vector<ui::Layer*>& layers = view->layer()->children();
+  EXPECT_EQ(kChildrenCount, static_cast<int>(layers.size()));
+  EXPECT_EQ(kChildrenCount, static_cast<int>(children.size()));
+  for (int i = 0; i < kChildrenCount; ++i) {
+    EXPECT_EQ(view->child_at(i)->layer(), layers[i]);
+    EXPECT_EQ(view->child_at(i), children[i]);
+  }
+
+  // Raise one of the children in z-order and add another child to reorder.
+  view->child_at(2)->set_id(OrderableView::VIEW_ID_RAISED);
+  AddViewWithChildLayer(view.get());
+
+  // 2nd child should be now on top, i.e. the last element in the array returned
+  // by GetChildrenInZOrder(). Its layer should also be above the others.
+  // The rest of the children and layers order should be unchanged.
+  const int expected_order[] = {0, 1, 3, 4, 2};
+  children = view->GetChildrenInZOrder();
+  EXPECT_EQ(kChildrenCount + 1, static_cast<int>(children.size()));
+  EXPECT_EQ(kChildrenCount + 1, static_cast<int>(layers.size()));
+  for (size_t i = 0; i < kChildrenCount + 1; ++i) {
+    EXPECT_EQ(view->child_at(expected_order[i]), children[i]);
+    EXPECT_EQ(view->child_at(expected_order[i])->layer(), layers[i]);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Observer tests.
 ////////////////////////////////////////////////////////////////////////////////